diff --git a/src/editor.test.ts b/src/editor.test.ts index db5fda32..728774f6 100644 --- a/src/editor.test.ts +++ b/src/editor.test.ts @@ -11,7 +11,7 @@ describe('editor', () => { it('returns information about the editor from EDITOR if it resolves to an executable', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: 'editor', TODAY: undefined }); + .mockReturnValue({ EDITOR: 'editor' }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('editor') .mockResolvedValue('/path/to/resolved-editor'); @@ -25,7 +25,7 @@ describe('editor', () => { it('falls back to VSCode if it exists and if EDITOR does not point to an executable', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: 'editor', TODAY: undefined }); + .mockReturnValue({ EDITOR: 'editor' }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('editor') .mockResolvedValue(null) @@ -41,7 +41,7 @@ describe('editor', () => { it('returns null if resolving EDITOR returns null and resolving VSCode returns null', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: 'editor', TODAY: undefined }); + .mockReturnValue({ EDITOR: 'editor' }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('editor') .mockResolvedValue(null) @@ -54,7 +54,7 @@ describe('editor', () => { it('returns null if resolving EDITOR returns null and resolving VSCode throws', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: 'editor', TODAY: undefined }); + .mockReturnValue({ EDITOR: 'editor' }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('editor') .mockResolvedValue(null) @@ -67,7 +67,7 @@ describe('editor', () => { it('returns null if resolving EDITOR throws and resolving VSCode returns null', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: 'editor', TODAY: undefined }); + .mockReturnValue({ EDITOR: 'editor' }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('editor') .mockRejectedValue(new Error('some error')) @@ -80,7 +80,7 @@ describe('editor', () => { it('returns null if resolving EDITOR throws and resolving VSCode throws', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: 'editor', TODAY: undefined }); + .mockReturnValue({ EDITOR: 'editor' }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('editor') .mockRejectedValue(new Error('some error')) @@ -93,7 +93,7 @@ describe('editor', () => { it('returns null if EDITOR is unset and resolving VSCode returns null', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: undefined, TODAY: undefined }); + .mockReturnValue({ EDITOR: undefined }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('code') .mockResolvedValue(null); @@ -104,7 +104,7 @@ describe('editor', () => { it('returns null if EDITOR is unset and resolving VSCode throws', async () => { jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ EDITOR: undefined, TODAY: undefined }); + .mockReturnValue({ EDITOR: undefined }); when(jest.spyOn(miscUtils, 'resolveExecutable')) .calledWith('code') .mockRejectedValue(new Error('some error')); diff --git a/src/env.test.ts b/src/env.test.ts index c40dbc65..02f334a5 100644 --- a/src/env.test.ts +++ b/src/env.test.ts @@ -16,12 +16,10 @@ describe('env', () => { it('returns only the environment variables from process.env that we use in this tool', () => { process.env.EDITOR = 'editor'; - process.env.TODAY = 'today'; process.env.EXTRA = 'extra'; expect(getEnvironmentVariables()).toStrictEqual({ EDITOR: 'editor', - TODAY: 'today', }); }); }); diff --git a/src/env.ts b/src/env.ts index 22d80553..c5253488 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,6 +1,5 @@ interface Env { EDITOR: string | undefined; - TODAY: string | undefined; } /** @@ -10,7 +9,7 @@ interface Env { * this tool needs to access, whether their values are defined or not. */ export function getEnvironmentVariables(): Env { - return ['EDITOR', 'TODAY'].reduce((object, key) => { + return ['EDITOR'].reduce((object, key) => { return { ...object, [key]: process.env[key] }; }, {} as Env); } diff --git a/src/functional.test.ts b/src/functional.test.ts index 9c9dc4b9..4be3f46d 100644 --- a/src/functional.test.ts +++ b/src/functional.test.ts @@ -9,7 +9,7 @@ describe('create-release-branch (functional)', () => { packages: { $root$: { name: '@scope/monorepo', - version: '2022.1.1', + version: '1.0.0', directoryPath: '.', }, a: { @@ -41,7 +41,6 @@ describe('create-release-branch (functional)', () => { workspaces: { '.': ['packages/*'], }, - today: new Date('2022-06-24'), }, async (environment) => { await environment.updateJsonFile('package.json', { @@ -88,7 +87,7 @@ describe('create-release-branch (functional)', () => { expect(await environment.readJsonFile('package.json')).toStrictEqual({ name: '@scope/monorepo', - version: '2022.6.24', + version: '2.0.0', private: true, workspaces: ['packages/*'], scripts: { foo: 'bar' }, @@ -138,7 +137,7 @@ describe('create-release-branch (functional)', () => { packages: { $root$: { name: '@scope/monorepo', - version: '2022.1.1', + version: '1.0.0', directoryPath: '.', }, a: { @@ -238,7 +237,7 @@ describe('create-release-branch (functional)', () => { packages: { $root$: { name: '@scope/monorepo', - version: '2022.1.1', + version: '1.0.0', directoryPath: '.', }, a: { @@ -250,7 +249,6 @@ describe('create-release-branch (functional)', () => { workspaces: { '.': ['packages/*'], }, - today: new Date('2022-06-24'), }, async (environment) => { await environment.runTool({ @@ -262,9 +260,9 @@ describe('create-release-branch (functional)', () => { }); // Tests four things: - // * The latest commit should be called "Release YYYY-MM-DD" + // * The latest commit should be called "Release 1.0.0" // * The latest commit should be the current commit (HEAD) - // * The latest branch should be called "release/YYYY-MM-DD" + // * The latest branch should be called "release/1.0.0" // * The latest branch should point to the latest commit const [latestCommitSubject, latestCommitId, latestCommitRevsMarker] = ( @@ -284,9 +282,9 @@ describe('create-release-branch (functional)', () => { '--max-count=1', ]) ).stdout; - expect(latestCommitSubject).toStrictEqual('Release 2022-06-24'); + expect(latestCommitSubject).toStrictEqual('Release 2.0.0'); expect(latestCommitRevs).toContain('HEAD'); - expect(latestCommitRevs).toContain('release/2022-06-24'); + expect(latestCommitRevs).toContain('release/2.0.0'); expect(latestBranchCommitId).toStrictEqual(latestCommitId); }, ); diff --git a/src/initial-parameters.test.ts b/src/initial-parameters.test.ts index 894b7ed1..c4459b0a 100644 --- a/src/initial-parameters.test.ts +++ b/src/initial-parameters.test.ts @@ -32,7 +32,7 @@ describe('initial-parameters', () => { }); jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ TODAY: '2022-06-22', EDITOR: undefined }); + .mockReturnValue({ EDITOR: undefined }); when(jest.spyOn(projectModule, 'readProject')) .calledWith('/path/to/project') .mockResolvedValue(project); @@ -46,7 +46,6 @@ describe('initial-parameters', () => { project, tempDirectoryPath: '/path/to/temp', reset: true, - today: new Date('2022-06-22'), }); }); @@ -63,7 +62,7 @@ describe('initial-parameters', () => { }); jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ TODAY: undefined, EDITOR: undefined }); + .mockReturnValue({ EDITOR: undefined }); const readProjectSpy = jest .spyOn(projectModule, 'readProject') .mockResolvedValue(project); @@ -84,7 +83,7 @@ describe('initial-parameters', () => { }); jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ TODAY: undefined, EDITOR: undefined }); + .mockReturnValue({ EDITOR: undefined }); when(jest.spyOn(projectModule, 'readProject')) .calledWith('/path/to/project') .mockResolvedValue(project); @@ -110,7 +109,7 @@ describe('initial-parameters', () => { }); jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ TODAY: undefined, EDITOR: undefined }); + .mockReturnValue({ EDITOR: undefined }); when(jest.spyOn(projectModule, 'readProject')) .calledWith('/path/to/project') .mockResolvedValue(project); @@ -125,56 +124,52 @@ describe('initial-parameters', () => { ); }); - it('uses the current date if the TODAY environment variable was not provided', async () => { + it('returns initial parameters including reset: true, derived from a command-line argument of "--reset true"', async () => { const project = buildMockProject(); - const today = new Date('2022-01-01'); when(jest.spyOn(commandLineArgumentsModule, 'readCommandLineArguments')) .calledWith(['arg1', 'arg2']) .mockResolvedValue({ projectDirectory: '/path/to/project', - tempDirectory: undefined, + tempDirectory: '/path/to/temp', reset: true, }); jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ TODAY: undefined, EDITOR: undefined }); + .mockReturnValue({ EDITOR: undefined }); when(jest.spyOn(projectModule, 'readProject')) .calledWith('/path/to/project') .mockResolvedValue(project); - jest.setSystemTime(today); const config = await determineInitialParameters( ['arg1', 'arg2'], - '/path/to/cwd', + '/path/to/somewhere', ); - expect(config.today).toStrictEqual(today); + expect(config.reset).toBe(true); }); - it('uses the current date if TODAY is not a parsable date', async () => { + it('returns initial parameters including reset: false, derived from a command-line argument of "--reset false"', async () => { const project = buildMockProject(); - const today = new Date('2022-01-01'); when(jest.spyOn(commandLineArgumentsModule, 'readCommandLineArguments')) .calledWith(['arg1', 'arg2']) .mockResolvedValue({ projectDirectory: '/path/to/project', - tempDirectory: undefined, - reset: true, + tempDirectory: '/path/to/temp', + reset: false, }); jest .spyOn(envModule, 'getEnvironmentVariables') - .mockReturnValue({ TODAY: 'asdfgdasf', EDITOR: undefined }); + .mockReturnValue({ EDITOR: undefined }); when(jest.spyOn(projectModule, 'readProject')) .calledWith('/path/to/project') .mockResolvedValue(project); - jest.setSystemTime(today); const config = await determineInitialParameters( ['arg1', 'arg2'], - '/path/to/cwd', + '/path/to/somewhere', ); - expect(config.today).toStrictEqual(today); + expect(config.reset).toBe(false); }); }); }); diff --git a/src/initial-parameters.ts b/src/initial-parameters.ts index c4b81a77..f945931e 100644 --- a/src/initial-parameters.ts +++ b/src/initial-parameters.ts @@ -1,6 +1,5 @@ import os from 'os'; import path from 'path'; -import { getEnvironmentVariables } from './env'; import { readCommandLineArguments } from './command-line-arguments'; import { readProject, Project } from './project'; @@ -8,7 +7,6 @@ interface InitialParameters { project: Project; tempDirectoryPath: string; reset: boolean; - today: Date; } /** @@ -24,7 +22,6 @@ export async function determineInitialParameters( cwd: string, ): Promise { const inputs = await readCommandLineArguments(argv); - const { TODAY } = getEnvironmentVariables(); const projectDirectoryPath = path.resolve(cwd, inputs.projectDirectory); const project = await readProject(projectDirectoryPath); @@ -36,11 +33,6 @@ export async function determineInitialParameters( project.rootPackage.validatedManifest.name.replace('/', '__'), ) : path.resolve(cwd, inputs.tempDirectory); - const parsedTodayTimestamp = - TODAY === undefined ? NaN : new Date(TODAY).getTime(); - const today = isNaN(parsedTodayTimestamp) - ? new Date() - : new Date(parsedTodayTimestamp); - return { project, tempDirectoryPath, reset: inputs.reset, today }; + return { project, tempDirectoryPath, reset: inputs.reset }; } diff --git a/src/main.test.ts b/src/main.test.ts index 78699002..539275cb 100644 --- a/src/main.test.ts +++ b/src/main.test.ts @@ -10,7 +10,6 @@ jest.mock('./monorepo-workflow-operations'); describe('main', () => { it('executes the monorepo workflow if the project is a monorepo', async () => { const project = buildMockProject({ isMonorepo: true }); - const today = new Date(); const stdout = fs.createWriteStream('/dev/null'); const stderr = fs.createWriteStream('/dev/null'); jest @@ -19,7 +18,6 @@ describe('main', () => { project, tempDirectoryPath: '/path/to/temp/directory', reset: false, - today, }); const followMonorepoWorkflowSpy = jest .spyOn(monorepoWorkflowOperations, 'followMonorepoWorkflow') @@ -36,7 +34,6 @@ describe('main', () => { project, tempDirectoryPath: '/path/to/temp/directory', firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -44,7 +41,6 @@ describe('main', () => { it('executes the polyrepo workflow if the project is within a polyrepo', async () => { const project = buildMockProject({ isMonorepo: false }); - const today = new Date(); const stdout = fs.createWriteStream('/dev/null'); const stderr = fs.createWriteStream('/dev/null'); jest @@ -53,7 +49,6 @@ describe('main', () => { project, tempDirectoryPath: '/path/to/temp/directory', reset: false, - today, }); const followMonorepoWorkflowSpy = jest .spyOn(monorepoWorkflowOperations, 'followMonorepoWorkflow') diff --git a/src/main.ts b/src/main.ts index 0e1dcdcf..66a0fe76 100644 --- a/src/main.ts +++ b/src/main.ts @@ -25,7 +25,7 @@ export async function main({ stdout: Pick; stderr: Pick; }) { - const { project, tempDirectoryPath, reset, today } = + const { project, tempDirectoryPath, reset } = await determineInitialParameters(argv, cwd); if (project.isMonorepo) { @@ -36,7 +36,6 @@ export async function main({ project, tempDirectoryPath, firstRemovingExistingReleaseSpecification: reset, - today, stdout, stderr, }); diff --git a/src/monorepo-workflow-operations.test.ts b/src/monorepo-workflow-operations.test.ts index 5e9caaa8..07b6d421 100644 --- a/src/monorepo-workflow-operations.test.ts +++ b/src/monorepo-workflow-operations.test.ts @@ -89,21 +89,25 @@ function buildMockReleaseSpecification({ * * @param overrides - The properties you want to override in the mock release * plan. - * @param overrides.releaseName - The name of the new release. For a polyrepo or - * a monorepo with fixed versions, this will be a version string with the shape - * `..`; for a monorepo with independent versions, this - * will be a version string with the shape `..-`. - * @param overrides.packages - Information about all of the packages in the - * project. For a polyrepo, this consists of the self-same package; for a - * monorepo it consists of the root package and any workspace packages. + * @param overrides.newVersion - The new version that should be released, + * encompassing one or more updates to packages within the project. This is + * always a SemVer-compatible string, though the meaning of each number depends + * on the type of project. For a polyrepo package or a monorepo with fixed + * versions, the format of the version string is "MAJOR.MINOR.PATCH"; for a + * monorepo with independent versions, it is "ORDINARY.BACKPORT.0", where + * `BACKPORT` is used to name a release that sits between two ordinary releases, + * and `ORDINARY` is used to name any other (non-backport) release. + * @param overrides.packages - Describes how the packages in the project should + * be updated. For a polyrepo package, this list will only contain the package + * itself; for a monorepo package it will consist of the root package and any + * workspace packages that will be included in the release. * @returns The mock release specification. */ function buildMockReleasePlan({ - releaseName = 'release-name', + newVersion = '1.0.0', packages = [], }: Partial = {}): ReleasePlan { - return { releaseName, packages }; + return { newVersion, packages }; } /** @@ -141,6 +145,8 @@ function buildMockEditor({ * throw. * @param args.errorUponExecutingReleasePlan - The error that * `executeReleasePlan` will throw. + * @param args.releaseVersion - The new version that the release plan will + * contain. * @returns Mock functions and other data that can be used in tests to make * assertions. */ @@ -152,6 +158,7 @@ async function setupFollowMonorepoWorkflow({ errorUponValidatingReleaseSpec, errorUponPlanningRelease, errorUponExecutingReleasePlan, + releaseVersion = '1.0.0', }: { sandbox: Sandbox; doesReleaseSpecFileExist: boolean; @@ -160,6 +167,7 @@ async function setupFollowMonorepoWorkflow({ errorUponValidatingReleaseSpec?: Error; errorUponPlanningRelease?: Error; errorUponExecutingReleasePlan?: Error; + releaseVersion?: string; }) { const { determineEditorSpy, @@ -178,11 +186,9 @@ async function setupFollowMonorepoWorkflow({ const releaseSpecification = buildMockReleaseSpecification({ path: releaseSpecificationPath, }); - const releaseName = 'some-release-name'; - const releasePlan = buildMockReleasePlan({ releaseName }); + const releasePlan = buildMockReleasePlan({ newVersion: releaseVersion }); const projectDirectoryPath = '/path/to/project'; const project = buildMockProject({ directoryPath: projectDirectoryPath }); - const today = new Date(); const stdout = new MockWritable(); const stderr = new MockWritable(); determineEditorSpy.mockResolvedValue(isEditorAvailable ? editor : null); @@ -212,21 +218,29 @@ async function setupFollowMonorepoWorkflow({ if (errorUponPlanningRelease) { when(planReleaseSpy) - .calledWith({ project, releaseSpecification, today }) + .calledWith({ project, releaseSpecification }) .mockRejectedValue(errorUponPlanningRelease); } else { when(planReleaseSpy) - .calledWith({ project, releaseSpecification, today }) + .calledWith({ project, releaseSpecification }) .mockResolvedValue(releasePlan); } if (errorUponExecutingReleasePlan) { - executeReleasePlanSpy.mockRejectedValue(errorUponExecutingReleasePlan); + when(executeReleasePlanSpy) + .calledWith(project, releasePlan, stderr) + .mockRejectedValue(errorUponExecutingReleasePlan); } else { - executeReleasePlanSpy.mockResolvedValue(); + when(executeReleasePlanSpy) + .calledWith(project, releasePlan, stderr) + .mockResolvedValue(undefined); } - captureChangesInReleaseBranchSpy.mockResolvedValue(); + when(captureChangesInReleaseBranchSpy) + .calledWith(projectDirectoryPath, { + releaseVersion, + }) + .mockResolvedValue(); if (doesReleaseSpecFileExist) { await fs.promises.writeFile( @@ -238,7 +252,6 @@ async function setupFollowMonorepoWorkflow({ return { project, projectDirectoryPath, - today, stdout, stderr, generateReleaseSpecificationTemplateForMonorepoSpy, @@ -246,7 +259,7 @@ async function setupFollowMonorepoWorkflow({ executeReleasePlanSpy, captureChangesInReleaseBranchSpy, releasePlan, - releaseName, + releaseVersion, releaseSpecificationPath, }; } @@ -258,7 +271,6 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, executeReleasePlanSpy, @@ -273,7 +285,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -290,37 +301,35 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, captureChangesInReleaseBranchSpy, projectDirectoryPath, - releaseName, } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, isEditorAvailable: true, + releaseVersion: '4.38.0', }); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); expect(captureChangesInReleaseBranchSpy).toHaveBeenCalledWith( projectDirectoryPath, - releaseName, + { releaseVersion: '4.38.0' }, ); }); }); it('removes the release spec file after editing, validating, and executing the release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -331,7 +340,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -342,7 +350,7 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to execute the release spec if it was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, executeReleasePlanSpy } = + const { project, stdout, stderr, executeReleasePlanSpy } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -355,7 +363,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -367,25 +374,19 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to create a new branch if the release spec was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { - project, - today, - stdout, - stderr, - captureChangesInReleaseBranchSpy, - } = await setupFollowMonorepoWorkflow({ - sandbox, - doesReleaseSpecFileExist: false, - isEditorAvailable: true, - errorUponEditingReleaseSpec: new Error('oops'), - }); + const { project, stdout, stderr, captureChangesInReleaseBranchSpy } = + await setupFollowMonorepoWorkflow({ + sandbox, + doesReleaseSpecFileExist: false, + isEditorAvailable: true, + errorUponEditingReleaseSpec: new Error('oops'), + }); await expect( followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -397,7 +398,7 @@ describe('monorepo-workflow-operations', () => { it('removes the release spec file even if it was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -410,7 +411,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -422,20 +422,20 @@ describe('monorepo-workflow-operations', () => { it('throws an error produced while editing the release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr } = - await setupFollowMonorepoWorkflow({ + const { project, stdout, stderr } = await setupFollowMonorepoWorkflow( + { sandbox, doesReleaseSpecFileExist: false, isEditorAvailable: true, errorUponEditingReleaseSpec: new Error('oops'), - }); + }, + ); await expect( followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -446,7 +446,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while validating the release spec', async () => { await withSandbox(async (sandbox) => { const errorUponValidatingReleaseSpec = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -459,7 +459,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -472,7 +471,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while planning the release', async () => { await withSandbox(async (sandbox) => { const errorUponPlanningRelease = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -485,7 +484,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -498,7 +496,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while executing the release plan', async () => { await withSandbox(async (sandbox) => { const errorUponExecutingReleasePlan = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -511,7 +509,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -525,7 +522,7 @@ describe('monorepo-workflow-operations', () => { describe('when firstRemovingExistingReleaseSpecification is false, the release spec file does not already exist, and an editor is not available', () => { it('does not attempt to execute the edited release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, executeReleasePlanSpy } = + const { project, stdout, stderr, executeReleasePlanSpy } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -536,7 +533,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -547,23 +543,17 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to create a new branch', async () => { await withSandbox(async (sandbox) => { - const { - project, - today, - stdout, - stderr, - captureChangesInReleaseBranchSpy, - } = await setupFollowMonorepoWorkflow({ - sandbox, - doesReleaseSpecFileExist: false, - isEditorAvailable: false, - }); + const { project, stdout, stderr, captureChangesInReleaseBranchSpy } = + await setupFollowMonorepoWorkflow({ + sandbox, + doesReleaseSpecFileExist: false, + isEditorAvailable: false, + }); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -574,18 +564,18 @@ describe('monorepo-workflow-operations', () => { it('prints a message', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr } = - await setupFollowMonorepoWorkflow({ + const { project, stdout, stderr } = await setupFollowMonorepoWorkflow( + { sandbox, doesReleaseSpecFileExist: false, isEditorAvailable: false, - }); + }, + ); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -598,7 +588,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -609,7 +599,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -624,7 +613,6 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, waitForUserToEditReleaseSpecificationSpy, @@ -637,7 +625,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -652,7 +639,6 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, executeReleasePlanSpy, @@ -666,7 +652,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -683,36 +668,34 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, captureChangesInReleaseBranchSpy, projectDirectoryPath, - releaseName, } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, + releaseVersion: '4.38.0', }); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); expect(captureChangesInReleaseBranchSpy).toHaveBeenCalledWith( projectDirectoryPath, - releaseName, + { releaseVersion: '4.38.0' }, ); }); }); it('removes the release spec file after validating and executing the release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -722,7 +705,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }); @@ -734,7 +716,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if an error is thrown while validating the release spec', async () => { await withSandbox(async (sandbox) => { const errorUponValidatingReleaseSpec = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -746,7 +728,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -759,7 +740,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if an error is thrown while planning the release', async () => { await withSandbox(async (sandbox) => { const errorUponPlanningRelease = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -771,7 +752,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -784,7 +764,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if an error is thrown while executing the release plan', async () => { await withSandbox(async (sandbox) => { const errorUponExecutingReleasePlan = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -796,7 +776,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: false, - today, stdout, stderr, }), @@ -812,7 +791,6 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, executeReleasePlanSpy, @@ -827,7 +805,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -844,37 +821,35 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, captureChangesInReleaseBranchSpy, projectDirectoryPath, - releaseName, } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, isEditorAvailable: true, + releaseVersion: '4.38.0', }); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); expect(captureChangesInReleaseBranchSpy).toHaveBeenCalledWith( projectDirectoryPath, - releaseName, + { releaseVersion: '4.38.0' }, ); }); }); it('removes the release spec file after editing, validating, and executing the release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -885,7 +860,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -896,7 +870,7 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to execute the release spec if it was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, executeReleasePlanSpy } = + const { project, stdout, stderr, executeReleasePlanSpy } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -909,7 +883,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -921,25 +894,19 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to create a new branch if the release spec was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { - project, - today, - stdout, - stderr, - captureChangesInReleaseBranchSpy, - } = await setupFollowMonorepoWorkflow({ - sandbox, - doesReleaseSpecFileExist: false, - isEditorAvailable: true, - errorUponEditingReleaseSpec: new Error('oops'), - }); + const { project, stdout, stderr, captureChangesInReleaseBranchSpy } = + await setupFollowMonorepoWorkflow({ + sandbox, + doesReleaseSpecFileExist: false, + isEditorAvailable: true, + errorUponEditingReleaseSpec: new Error('oops'), + }); await expect( followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -951,7 +918,7 @@ describe('monorepo-workflow-operations', () => { it('removes the release spec file even if it was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -964,7 +931,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -976,20 +942,20 @@ describe('monorepo-workflow-operations', () => { it('throws an error produced while editing the release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr } = - await setupFollowMonorepoWorkflow({ + const { project, stdout, stderr } = await setupFollowMonorepoWorkflow( + { sandbox, doesReleaseSpecFileExist: false, isEditorAvailable: true, errorUponEditingReleaseSpec: new Error('oops'), - }); + }, + ); await expect( followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1000,7 +966,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while validating the release spec', async () => { await withSandbox(async (sandbox) => { const errorUponValidatingReleaseSpec = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -1013,7 +979,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1026,7 +991,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while planning the release', async () => { await withSandbox(async (sandbox) => { const errorUponPlanningRelease = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -1039,7 +1004,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1052,7 +1016,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while executing the release plan', async () => { await withSandbox(async (sandbox) => { const errorUponExecutingReleasePlan = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -1065,7 +1029,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1079,7 +1042,7 @@ describe('monorepo-workflow-operations', () => { describe('when firstRemovingExistingReleaseSpecification is true, the release spec file does not already exist, and an editor is not available', () => { it('does not attempt to execute the edited release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, executeReleasePlanSpy } = + const { project, stdout, stderr, executeReleasePlanSpy } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -1090,7 +1053,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1101,23 +1063,17 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to create a new branch', async () => { await withSandbox(async (sandbox) => { - const { - project, - today, - stdout, - stderr, - captureChangesInReleaseBranchSpy, - } = await setupFollowMonorepoWorkflow({ - sandbox, - doesReleaseSpecFileExist: false, - isEditorAvailable: false, - }); + const { project, stdout, stderr, captureChangesInReleaseBranchSpy } = + await setupFollowMonorepoWorkflow({ + sandbox, + doesReleaseSpecFileExist: false, + isEditorAvailable: false, + }); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1128,18 +1084,18 @@ describe('monorepo-workflow-operations', () => { it('prints a message', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr } = - await setupFollowMonorepoWorkflow({ + const { project, stdout, stderr } = await setupFollowMonorepoWorkflow( + { sandbox, doesReleaseSpecFileExist: false, isEditorAvailable: false, - }); + }, + ); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1152,7 +1108,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: false, @@ -1163,7 +1119,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1178,7 +1133,6 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, generateReleaseSpecificationTemplateForMonorepoSpy, @@ -1192,7 +1146,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1210,7 +1163,6 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, executeReleasePlanSpy, @@ -1225,7 +1177,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1242,37 +1193,35 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, captureChangesInReleaseBranchSpy, projectDirectoryPath, - releaseName, } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, isEditorAvailable: true, + releaseVersion: '4.38.0', }); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); expect(captureChangesInReleaseBranchSpy).toHaveBeenCalledWith( projectDirectoryPath, - releaseName, + { releaseVersion: '4.38.0' }, ); }); }); it('removes the release spec file after editing, validating, and executing the release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1283,7 +1232,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1294,7 +1242,7 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to execute the release spec if it was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, executeReleasePlanSpy } = + const { project, stdout, stderr, executeReleasePlanSpy } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1307,7 +1255,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1319,25 +1266,19 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to create a new branch if the release spec was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { - project, - today, - stdout, - stderr, - captureChangesInReleaseBranchSpy, - } = await setupFollowMonorepoWorkflow({ - sandbox, - doesReleaseSpecFileExist: true, - isEditorAvailable: true, - errorUponEditingReleaseSpec: new Error('oops'), - }); + const { project, stdout, stderr, captureChangesInReleaseBranchSpy } = + await setupFollowMonorepoWorkflow({ + sandbox, + doesReleaseSpecFileExist: true, + isEditorAvailable: true, + errorUponEditingReleaseSpec: new Error('oops'), + }); await expect( followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1349,7 +1290,7 @@ describe('monorepo-workflow-operations', () => { it('removes the release spec file even if it was not successfully edited', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1362,7 +1303,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1374,20 +1314,20 @@ describe('monorepo-workflow-operations', () => { it('throws an error produced while editing the release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr } = - await setupFollowMonorepoWorkflow({ + const { project, stdout, stderr } = await setupFollowMonorepoWorkflow( + { sandbox, doesReleaseSpecFileExist: true, isEditorAvailable: true, errorUponEditingReleaseSpec: new Error('oops'), - }); + }, + ); await expect( followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1398,7 +1338,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while validating the release spec', async () => { await withSandbox(async (sandbox) => { const errorUponValidatingReleaseSpec = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1411,7 +1351,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1424,7 +1363,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while planning the release', async () => { await withSandbox(async (sandbox) => { const errorUponPlanningRelease = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1437,7 +1376,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1450,7 +1388,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file if it was successfully edited but an error is thrown while executing the release plan', async () => { await withSandbox(async (sandbox) => { const errorUponExecutingReleasePlan = new Error('oops'); - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1463,7 +1401,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }), @@ -1479,7 +1416,6 @@ describe('monorepo-workflow-operations', () => { await withSandbox(async (sandbox) => { const { project, - today, stdout, stderr, generateReleaseSpecificationTemplateForMonorepoSpy, @@ -1493,7 +1429,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1509,7 +1444,7 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to execute the edited release spec', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, executeReleasePlanSpy } = + const { project, stdout, stderr, executeReleasePlanSpy } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1520,7 +1455,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1531,23 +1465,17 @@ describe('monorepo-workflow-operations', () => { it('does not attempt to create a new branch', async () => { await withSandbox(async (sandbox) => { - const { - project, - today, - stdout, - stderr, - captureChangesInReleaseBranchSpy, - } = await setupFollowMonorepoWorkflow({ - sandbox, - doesReleaseSpecFileExist: true, - isEditorAvailable: false, - }); + const { project, stdout, stderr, captureChangesInReleaseBranchSpy } = + await setupFollowMonorepoWorkflow({ + sandbox, + doesReleaseSpecFileExist: true, + isEditorAvailable: false, + }); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1558,18 +1486,18 @@ describe('monorepo-workflow-operations', () => { it('prints a message', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr } = - await setupFollowMonorepoWorkflow({ + const { project, stdout, stderr } = await setupFollowMonorepoWorkflow( + { sandbox, doesReleaseSpecFileExist: true, isEditorAvailable: false, - }); + }, + ); await followMonorepoWorkflow({ project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); @@ -1582,7 +1510,7 @@ describe('monorepo-workflow-operations', () => { it('does not remove the generated release spec file', async () => { await withSandbox(async (sandbox) => { - const { project, today, stdout, stderr, releaseSpecificationPath } = + const { project, stdout, stderr, releaseSpecificationPath } = await setupFollowMonorepoWorkflow({ sandbox, doesReleaseSpecFileExist: true, @@ -1593,7 +1521,6 @@ describe('monorepo-workflow-operations', () => { project, tempDirectoryPath: sandbox.directoryPath, firstRemovingExistingReleaseSpecification: true, - today, stdout, stderr, }); diff --git a/src/monorepo-workflow-operations.ts b/src/monorepo-workflow-operations.ts index ffc8e9dc..6466dabe 100644 --- a/src/monorepo-workflow-operations.ts +++ b/src/monorepo-workflow-operations.ts @@ -43,7 +43,6 @@ import { * possible for a release specification that was created in a previous run to * stick around (due to an error). This will ensure that the file is removed * first. - * @param options.today - The current date. * @param options.stdout - A stream that can be used to write to standard out. * @param options.stderr - A stream that can be used to write to standard error. */ @@ -51,14 +50,12 @@ export async function followMonorepoWorkflow({ project, tempDirectoryPath, firstRemovingExistingReleaseSpecification, - today, stdout, stderr, }: { project: Project; tempDirectoryPath: string; firstRemovingExistingReleaseSpecification: boolean; - today: Date; stdout: Pick; stderr: Pick; }) { @@ -110,12 +107,10 @@ export async function followMonorepoWorkflow({ const releasePlan = await planRelease({ project, releaseSpecification, - today, }); await executeReleasePlan(project, releasePlan, stderr); await removeFile(releaseSpecificationPath); - await captureChangesInReleaseBranch( - project.directoryPath, - releasePlan.releaseName, - ); + await captureChangesInReleaseBranch(project.directoryPath, { + releaseVersion: releasePlan.newVersion, + }); } diff --git a/src/project.test.ts b/src/project.test.ts index 77673d70..2701efdf 100644 --- a/src/project.test.ts +++ b/src/project.test.ts @@ -12,11 +12,11 @@ jest.mock('./repo'); describe('project', () => { describe('readProject', () => { - it('collects information about a monorepo project', async () => { + it('collects information about the repository URL, release version, and packages in the project', async () => { await withSandbox(async (sandbox) => { const projectDirectoryPath = sandbox.directoryPath; const projectRepositoryUrl = 'https://github.com/some-org/some-repo'; - const rootPackage = buildMockPackage('root', { + const rootPackage = buildMockPackage('root', '4.38.0', { directoryPath: projectDirectoryPath, validatedManifest: buildMockManifest({ workspaces: ['packages/a', 'packages/subpackages/*'], @@ -66,30 +66,10 @@ describe('project', () => { rootPackage, workspacePackages, isMonorepo: true, - }); - }); - }); - - it('collects information about a polyrepo project', async () => { - await withSandbox(async (sandbox) => { - const projectDirectoryPath = sandbox.directoryPath; - const projectRepositoryUrl = 'https://github.com/some-org/some-repo'; - const rootPackage = buildMockPackage('root', { - directoryPath: projectDirectoryPath, - }); - when(jest.spyOn(repoModule, 'getRepositoryHttpsUrl')) - .calledWith(projectDirectoryPath) - .mockResolvedValue(projectRepositoryUrl); - when(jest.spyOn(packageModule, 'readPackage')) - .calledWith(projectDirectoryPath) - .mockResolvedValue(rootPackage); - - expect(await readProject(projectDirectoryPath)).toStrictEqual({ - directoryPath: projectDirectoryPath, - repositoryUrl: projectRepositoryUrl, - rootPackage, - workspacePackages: {}, - isMonorepo: false, + releaseVersion: { + ordinaryNumber: 4, + backportNumber: 38, + }, }); }); }); diff --git a/src/project.ts b/src/project.ts index 7c33c3ac..1d22ec4f 100644 --- a/src/project.ts +++ b/src/project.ts @@ -3,6 +3,25 @@ import glob from 'glob'; import { Package, readPackage } from './package'; import { PackageManifestFieldNames } from './package-manifest'; import { getRepositoryHttpsUrl } from './repo'; +import { SemVer } from './semver'; + +/** + * The release version of the root package of a monorepo extracted from its + * version string. + * + * @property ordinaryNumber - The number assigned to the release if it + * introduces new changes that haven't appeared in any previous release; it will + * be 0 if there haven't been any releases yet. + * @property backportNumber - A backport release is a change ported from one + * ordinary release to a previous ordinary release. This, then, is the number + * which identifies this release relative to other backport releases under the + * same ordinary release, starting from 1; it will be 0 if there aren't any + * backport releases for the ordinary release yet. + */ +interface ReleaseVersion { + ordinaryNumber: number; + backportNumber: number; +} /** * Represents the entire codebase on which this tool is operating. @@ -21,6 +40,7 @@ export interface Project { rootPackage: Package; workspacePackages: Record; isMonorepo: boolean; + releaseVersion: ReleaseVersion; } /** @@ -28,6 +48,22 @@ export interface Project { */ const promisifiedGlob = util.promisify(glob); +/** + * Given a SemVer version object, interprets the "major" part of the version + * as the ordinary release number and the "minor" part as the backport release + * number in the context of the ordinary release. + * + * @param packageVersion - The version of the package. + * @returns An object containing the ordinary and backport numbers in the + * version. + */ +function examineReleaseVersion(packageVersion: SemVer): ReleaseVersion { + return { + ordinaryNumber: packageVersion.major, + backportNumber: packageVersion.minor, + }; +} + /** * Collects information about a project. For a polyrepo, this information will * only cover the project's `package.json` file; for a monorepo, it will cover @@ -44,6 +80,9 @@ export async function readProject( ): Promise { const repositoryUrl = await getRepositoryHttpsUrl(projectDirectoryPath); const rootPackage = await readPackage(projectDirectoryPath); + const releaseVersion = examineReleaseVersion( + rootPackage.validatedManifest.version, + ); const workspaceDirectories = ( await Promise.all( @@ -76,5 +115,6 @@ export async function readProject( rootPackage, workspacePackages, isMonorepo, + releaseVersion, }; } diff --git a/src/release-plan.test.ts b/src/release-plan.test.ts index 0fcda290..03ef4db3 100644 --- a/src/release-plan.test.ts +++ b/src/release-plan.test.ts @@ -11,7 +11,7 @@ describe('release-plan-utils', () => { describe('planRelease', () => { it('calculates final versions for all packages in the release spec', async () => { const project = buildMockProject({ - rootPackage: buildMockPackage('root', '2022.1.1'), + rootPackage: buildMockPackage('root', '1.0.0'), workspacePackages: { a: buildMockPackage('a', '1.0.0'), b: buildMockPackage('b', '1.0.0'), @@ -28,20 +28,18 @@ describe('release-plan-utils', () => { }, path: '/path/to/release/spec', }; - const today = new Date('2022-07-21'); const releasePlan = await planRelease({ project, releaseSpecification, - today, }); expect(releasePlan).toMatchObject({ - releaseName: '2022-07-21', + newVersion: '2.0.0', packages: [ { package: project.rootPackage, - newVersion: '2022.7.21', + newVersion: '2.0.0', }, { package: project.workspacePackages.a, @@ -75,23 +73,20 @@ describe('release-plan-utils', () => { }); const releaseSpecification = { packages: { - a: IncrementableVersionParts.major, - b: IncrementableVersionParts.major, - c: IncrementableVersionParts.patch, - d: new SemVer('1.2.3'), + a: new SemVer('2.0.0'), + b: new SemVer('2.0.0'), + c: new SemVer('2.0.0'), + d: new SemVer('2.0.0'), }, path: '/path/to/release/spec', }; - const today = new Date('2022-07-21'); const releasePlan = await planRelease({ project, releaseSpecification, - today, }); expect(releasePlan).toMatchObject({ - releaseName: '2022-07-21', packages: [ { package: project.rootPackage, @@ -122,7 +117,7 @@ describe('release-plan-utils', () => { it('runs updatePackage for each package in the release plan', async () => { const project = buildMockProject(); const releasePlan = { - releaseName: 'some-release-name', + newVersion: '1.0.0', packages: [ { package: buildMockPackage(), diff --git a/src/release-plan.ts b/src/release-plan.ts index bcac7bdb..f8408078 100644 --- a/src/release-plan.ts +++ b/src/release-plan.ts @@ -9,17 +9,21 @@ import { ReleaseSpecification } from './release-specification'; * Instructions for how to update the project in order to prepare it for a new * release. * - * @property releaseName - The name of the new release. For a polyrepo or a - * monorepo with fixed versions, this will be a version string with the shape - * `..`; for a monorepo with independent versions, this - * will be a version string with the shape `..-`. - * @property packages - Information about all of the packages in the project. - * For a polyrepo, this consists of the self-same package; for a monorepo it - * consists of the root package and any workspace packages. + * @property newVersion - The new version that should be released, encompassing + * one or more updates to packages within the project. This is always a + * SemVer-compatible string, though the meaning of each number depends on the + * type of project. For a polyrepo package or a monorepo with fixed versions, + * the format of the version string is "MAJOR.MINOR.PATCH"; for a monorepo with + * independent versions, it is "ORDINARY.BACKPORT.0", where `BACKPORT` is used + * to name a release that sits between two ordinary releases, and `ORDINARY` is + * used to name any other (non-backport) release. + * @property packages - Describes how the packages in the project should be + * updated. For a polyrepo package, this list will only contain the package + * itself; for a monorepo package it will consist of the root package and any + * workspace packages that will be included in the release. */ export interface ReleasePlan { - releaseName: string; + newVersion: string; packages: PackageReleasePlan[]; } @@ -28,8 +32,8 @@ export interface ReleasePlan { * it for a new release. * * @property package - Information about the package. - * @property newVersion - The new version to which the package should be - * updated. + * @property newVersion - The new version for the package, as a + * SemVer-compatible string. * @property shouldUpdateChangelog - Whether or not the changelog for the * package should get updated. For a polyrepo, this will always be true; for a * monorepo, this will be true only for workspace packages (the root package @@ -50,28 +54,20 @@ export interface PackageReleasePlan { * packages and where they can found). * @param args.releaseSpecification - A parsed version of the release spec * entered by the user. - * @param args.today - The current date. * @returns A promise for information about the new release. */ export async function planRelease({ project, releaseSpecification, - today, }: { project: Project; releaseSpecification: ReleaseSpecification; - today: Date; }): Promise { - const newReleaseName = today.toISOString().replace(/T.+$/u, ''); - const newRootVersion = [ - today.getUTCFullYear(), - today.getUTCMonth() + 1, - today.getUTCDate(), - ].join('.'); + const newReleaseVersion = `${project.releaseVersion.ordinaryNumber + 1}.0.0`; const rootReleasePlan: PackageReleasePlan = { package: project.rootPackage, - newVersion: newRootVersion, + newVersion: newReleaseVersion, shouldUpdateChangelog: false, }; @@ -94,7 +90,7 @@ export async function planRelease({ }); return { - releaseName: newReleaseName, + newVersion: newReleaseVersion, packages: [rootReleasePlan, ...workspaceReleasePlans], }; } diff --git a/src/repo.test.ts b/src/repo.test.ts index b406b2c0..ffeb5b09 100644 --- a/src/repo.test.ts +++ b/src/repo.test.ts @@ -96,14 +96,13 @@ describe('git-utils', () => { 'getStdoutFromCommand', ); - await captureChangesInReleaseBranch( - '/path/to/project', - 'some-release-name', - ); + await captureChangesInReleaseBranch('/path/to/project', { + releaseVersion: '1.0.0', + }); expect(getStdoutFromCommandSpy).toHaveBeenCalledWith( 'git', - ['checkout', '-b', 'release/some-release-name'], + ['checkout', '-b', 'release/1.0.0'], { cwd: '/path/to/project' }, ); expect(getStdoutFromCommandSpy).toHaveBeenCalledWith( @@ -113,7 +112,7 @@ describe('git-utils', () => { ); expect(getStdoutFromCommandSpy).toHaveBeenCalledWith( 'git', - ['commit', '-m', 'Release some-release-name'], + ['commit', '-m', 'Release 1.0.0'], { cwd: '/path/to/project' }, ); }); diff --git a/src/repo.ts b/src/repo.ts index e9eb3c70..bd5fdcfb 100644 --- a/src/repo.ts +++ b/src/repo.ts @@ -88,23 +88,23 @@ export async function getRepositoryHttpsUrl( * of the new release). * 3. Switches to that branch. * - * @param repositoryDirectoryPath - The path to the repository directory. - * @param releaseName - The name of the release, which will be used to name the - * commit and the branch. + * @param projectRepositoryPath - The path to the repository directory. + * @param args - The arguments. + * @param args.releaseVersion - The release version. */ export async function captureChangesInReleaseBranch( - repositoryDirectoryPath: string, - releaseName: string, + projectRepositoryPath: string, + { releaseVersion }: { releaseVersion: string }, ) { - await getStdoutFromGitCommandWithin(repositoryDirectoryPath, [ + await getStdoutFromGitCommandWithin(projectRepositoryPath, [ 'checkout', '-b', - `release/${releaseName}`, + `release/${releaseVersion}`, ]); - await getStdoutFromGitCommandWithin(repositoryDirectoryPath, ['add', '-A']); - await getStdoutFromGitCommandWithin(repositoryDirectoryPath, [ + await getStdoutFromGitCommandWithin(projectRepositoryPath, ['add', '-A']); + await getStdoutFromGitCommandWithin(projectRepositoryPath, [ 'commit', '-m', - `Release ${releaseName}`, + `Release ${releaseVersion}`, ]); } diff --git a/tests/functional/helpers/monorepo-environment.ts b/tests/functional/helpers/monorepo-environment.ts index 12c3497a..fc082467 100644 --- a/tests/functional/helpers/monorepo-environment.ts +++ b/tests/functional/helpers/monorepo-environment.ts @@ -17,15 +17,12 @@ import { debug, knownKeysOf } from './utils'; * @property packages - The known packages within this repo (including the * root). * @property workspaces - The known workspaces within this repo. - * @property today - The date that will be used for new releases. Will be - * translated to the TODAY environment variables. */ export interface MonorepoEnvironmentOptions< WorkspacePackageNickname extends string, > extends EnvironmentOptions { packages: Record; workspaces: Record; - today?: Date; } /** @@ -55,15 +52,9 @@ export default class MonorepoEnvironment< #packages: MonorepoEnvironmentOptions['packages']; - #today: MonorepoEnvironmentOptions['today']; - - constructor({ - today, - ...rest - }: MonorepoEnvironmentOptions) { - super(rest); - this.#packages = rest.packages; - this.#today = today; + constructor(options: MonorepoEnvironmentOptions) { + super(options); + this.#packages = options.packages; this.readFileWithinPackage = this.localRepo.readFileWithinPackage.bind( this.localRepo, ); @@ -133,9 +124,6 @@ cat "${releaseSpecificationPath}" > "$1" const env = { EDITOR: releaseSpecificationEditorPath, - ...(this.#today === undefined - ? {} - : { TODAY: this.#today.toISOString().replace(/T.+$/u, '') }), }; const result = await this.localRepo.runCommand( diff --git a/tests/unit/helpers.ts b/tests/unit/helpers.ts index 3b0adb60..dfb5f050 100644 --- a/tests/unit/helpers.ts +++ b/tests/unit/helpers.ts @@ -44,6 +44,10 @@ export function buildMockProject(overrides: Partial = {}): Project { rootPackage: buildMockPackage('root'), workspacePackages: {}, isMonorepo: false, + releaseVersion: { + ordinaryNumber: 1, + backportNumber: 0, + }, ...overrides, }; }