Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ module.exports = {
'/node_modules/',
'/src/cli.ts',
'/src/command-line-arguments.ts',
'/src/ui.ts',
'/src/dirname.ts',
],

// Indicates which provider should be used to instrument code for coverage
Expand Down Expand Up @@ -217,7 +219,7 @@ module.exports = {

// A map from regular expressions to paths to transformers
transform: {
"\\.[jt]sx?$": "babel-jest"
'\\.[jt]sx?$': 'babel-jest',
},

// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
Expand Down
12 changes: 10 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,28 @@
"dist/"
],
"scripts": {
"build": "tsc --project tsconfig.build.json",
"build": "tsc --project tsconfig.build.json && npm run build:ui-static",
"build:clean": "rimraf dist && yarn build",
"lint": "yarn lint:eslint && yarn lint:misc --check",
"lint:eslint": "eslint . --cache --ext js,ts",
"lint:fix": "yarn lint:eslint --fix && yarn lint:misc --write",
"lint:misc": "prettier '**/*.json' '**/*.md' '!CHANGELOG.md' '**/*.yml' '!.yarnrc.yml' --ignore-path .gitignore --no-error-on-unmatched-pattern",
"prepack": "./scripts/prepack.sh",
"test": "jest && jest-it-up --config jest.config.cjs",
"test:watch": "jest --watch"
"test:watch": "jest --watch",
"build:ui-static": "cp src/ui/index.html dist/ui"
},
"dependencies": {
"@metamask/action-utils": "^1.0.0",
"@metamask/auto-changelog": "^4.0.0",
"@metamask/utils": "^9.0.0",
"debug": "^4.3.4",
"execa": "^8.0.1",
"express": "^4.21.2",
"pony-cause": "^2.1.9",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-markdown": "^9.0.3",
"semver": "^7.5.4",
"validate-npm-package-name": "^5.0.0",
"which": "^3.0.0",
Expand All @@ -48,10 +53,13 @@
"@metamask/eslint-config-nodejs": "^10.0.0",
"@metamask/eslint-config-typescript": "^10.0.0",
"@types/debug": "^4.1.7",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.10",
"@types/jest-when": "^3.5.2",
"@types/node": "^17.0.23",
"@types/prettier": "^2.7.3",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@types/rimraf": "^4.0.5",
"@types/validate-npm-package-name": "^4.0.2",
"@types/which": "^3.0.0",
Expand Down
15 changes: 15 additions & 0 deletions src/command-line-arguments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export type CommandLineArguments = {
reset: boolean;
backport: boolean;
defaultBranch: string;
interactive: boolean;
port: number;
};

/**
Expand Down Expand Up @@ -51,6 +53,19 @@ export async function readCommandLineArguments(
default: 'main',
type: 'string',
})
.option('interactive', {
alias: 'i',
describe:
'Start an interactive web UI for selecting package versions to release',
type: 'boolean',
default: false,
})
.option('port', {
describe:
'Port to run the interactive web UI server (only used with --interactive)',
type: 'number',
default: 3000,
})
.help()
.strict()
.parse();
Expand Down
14 changes: 14 additions & 0 deletions src/dirname.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { fileURLToPath } from 'url';
import { dirname } from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

/**
* Get the current directory path.
*
* @returns The current directory path.
*/
export function getCurrentDirectoryPath() {
return __dirname;
}
18 changes: 18 additions & 0 deletions src/initial-parameters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ describe('initial-parameters', () => {
reset: true,
backport: false,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand All @@ -56,6 +58,8 @@ describe('initial-parameters', () => {
reset: true,
releaseType: 'ordinary',
defaultBranch: 'main',
interactive: false,
port: 3000,
});
});

Expand All @@ -72,6 +76,8 @@ describe('initial-parameters', () => {
reset: true,
backport: false,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand Down Expand Up @@ -102,6 +108,8 @@ describe('initial-parameters', () => {
reset: true,
backport: false,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand Down Expand Up @@ -132,6 +140,8 @@ describe('initial-parameters', () => {
reset: true,
backport: false,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand Down Expand Up @@ -162,6 +172,8 @@ describe('initial-parameters', () => {
reset: true,
backport: false,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand Down Expand Up @@ -190,6 +202,8 @@ describe('initial-parameters', () => {
reset: false,
backport: false,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand Down Expand Up @@ -218,6 +232,8 @@ describe('initial-parameters', () => {
reset: false,
backport: true,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand Down Expand Up @@ -246,6 +262,8 @@ describe('initial-parameters', () => {
reset: false,
backport: false,
defaultBranch: 'main',
interactive: false,
port: 3000,
});
jest
.spyOn(envModule, 'getEnvironmentVariables')
Expand Down
4 changes: 4 additions & 0 deletions src/initial-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type InitialParameters = {
reset: boolean;
releaseType: ReleaseType;
defaultBranch: string;
interactive: boolean;
port: number;
};

/**
Expand Down Expand Up @@ -61,5 +63,7 @@ export async function determineInitialParameters({
reset: args.reset,
defaultBranch: args.defaultBranch,
releaseType: args.backport ? 'backport' : 'ordinary',
interactive: args.interactive,
port: args.port,
};
}
45 changes: 44 additions & 1 deletion src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ import { buildMockProject } from '../tests/unit/helpers.js';
import { main } from './main.js';
import * as initialParametersModule from './initial-parameters.js';
import * as monorepoWorkflowOperations from './monorepo-workflow-operations.js';
import * as ui from './ui.js';

jest.mock('./initial-parameters');
jest.mock('./monorepo-workflow-operations');
jest.mock('./ui');
jest.mock('./dirname', () => ({
getCurrentDirectoryPath: jest.fn().mockReturnValue('/path/to/somewhere'),
}));

describe('main', () => {
it('executes the monorepo workflow if the project is a monorepo', async () => {
it('executes the CLI monorepo workflow if the project is a monorepo and interactive is false', async () => {
const project = buildMockProject({ isMonorepo: true });
const stdout = fs.createWriteStream('/dev/null');
const stderr = fs.createWriteStream('/dev/null');
Expand All @@ -20,6 +25,8 @@ describe('main', () => {
reset: true,
defaultBranch: 'main',
releaseType: 'backport',
interactive: false,
port: 3000,
});
const followMonorepoWorkflowSpy = jest
.spyOn(monorepoWorkflowOperations, 'followMonorepoWorkflow')
Expand All @@ -43,6 +50,40 @@ describe('main', () => {
});
});

it('executes the interactive UI monorepo workflow if the project is a monorepo and interactive is true', async () => {
const project = buildMockProject({ isMonorepo: true });
const stdout = fs.createWriteStream('/dev/null');
const stderr = fs.createWriteStream('/dev/null');
jest
.spyOn(initialParametersModule, 'determineInitialParameters')
.mockResolvedValue({
project,
tempDirectoryPath: '/path/to/temp/directory',
reset: true,
defaultBranch: 'main',
releaseType: 'backport',
interactive: true,
port: 3000,
});
const startUISpy = jest.spyOn(ui, 'startUI').mockResolvedValue();

await main({
argv: [],
cwd: '/path/to/somewhere',
stdout,
stderr,
});

expect(startUISpy).toHaveBeenCalledWith({
project,
releaseType: 'backport',
defaultBranch: 'main',
port: 3000,
stdout,
stderr,
});
});

it('executes the polyrepo workflow if the project is within a polyrepo', async () => {
const project = buildMockProject({ isMonorepo: false });
const stdout = fs.createWriteStream('/dev/null');
Expand All @@ -55,6 +96,8 @@ describe('main', () => {
reset: false,
defaultBranch: 'main',
releaseType: 'backport',
interactive: false,
port: 3000,
});
const followMonorepoWorkflowSpy = jest
.spyOn(monorepoWorkflowOperations, 'followMonorepoWorkflow')
Expand Down
43 changes: 32 additions & 11 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { WriteStream } from 'fs';
import { determineInitialParameters } from './initial-parameters.js';
import { followMonorepoWorkflow } from './monorepo-workflow-operations.js';
import { startUI } from './ui.js';

/**
* The main function for this tool. Designed to not access `process.argv`,
Expand All @@ -25,22 +26,42 @@ export async function main({
stdout: Pick<WriteStream, 'write'>;
stderr: Pick<WriteStream, 'write'>;
}) {
const { project, tempDirectoryPath, reset, releaseType, defaultBranch } =
await determineInitialParameters({ argv, cwd, stderr });
const {
project,
tempDirectoryPath,
reset,
releaseType,
defaultBranch,
interactive,
port,
} = await determineInitialParameters({ argv, cwd, stderr });

if (project.isMonorepo) {
stdout.write(
'Project appears to have workspaces. Following monorepo workflow.\n',
);
await followMonorepoWorkflow({
project,
tempDirectoryPath,
firstRemovingExistingReleaseSpecification: reset,
releaseType,
defaultBranch,
stdout,
stderr,
});

if (interactive) {
stdout.write(`Starting UI on port ${port}...\n`);
await startUI({
project,
releaseType,
defaultBranch,
port,
stdout,
stderr,
});
} else {
await followMonorepoWorkflow({
project,
tempDirectoryPath,
firstRemovingExistingReleaseSpecification: reset,
releaseType,
defaultBranch,
stdout,
stderr,
});
}
} else {
stdout.write(
'Project does not appear to have any workspaces. Following polyrepo workflow.\n',
Expand Down
Loading
Loading