Skip to content
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Use `Release X.Y.Z` as the single `--interactive` release preparation commit message; non-interactive release preparation now keeps `Initialize release X.Y.Z by bumping all packages` followed by `Filter release to only selected packages`.

## [4.2.1]

### Changed
Expand Down
8 changes: 4 additions & 4 deletions src/functional.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -590,8 +590,8 @@ describe('create-release-branch (functional)', () => {
});

// Tests five things:
// * The latest commit should be called "Update Release 2.0.0"
// * The before latest commit should be called "Initialize Release 2.0.0"
// * The latest commit should be called "Filter release to only selected packages"
// * The before latest commit should be called "Initialize release 2.0.0 by bumping all packages"
// * The latest commit should be the current commit (HEAD)
// * The latest branch should be called "release/2.0.0"
// * The latest branch should point to the latest commit
Expand All @@ -618,10 +618,10 @@ describe('create-release-branch (functional)', () => {
])
).stdout;
expect(latestCommitsInReverse[0].subject).toBe(
'Update Release 2.0.0',
'Filter release to only selected packages',
);
expect(latestCommitsInReverse[1].subject).toBe(
'Initialize Release 2.0.0',
'Initialize release 2.0.0 by bumping all packages',
);

expect(latestCommitsInReverse[0].revs).toContain('HEAD');
Expand Down
36 changes: 18 additions & 18 deletions src/monorepo-workflow-operations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,12 +468,12 @@ describe('monorepo-workflow-operations', () => {
expect(commitAllChangesSpy).toHaveBeenNthCalledWith(
1,
projectDirectoryPath,
`Initialize Release ${releaseVersion}`,
`Initialize release ${releaseVersion} by bumping all packages`,
);
expect(commitAllChangesSpy).toHaveBeenNthCalledWith(
2,
projectDirectoryPath,
`Update Release ${releaseVersion}`,
'Filter release to only selected packages',
);

expect(fixConstraintsSpy).toHaveBeenCalledTimes(1);
Expand Down Expand Up @@ -517,7 +517,7 @@ describe('monorepo-workflow-operations', () => {
expect(commitAllChangesSpy).toHaveBeenNthCalledWith(
3,
projectDirectoryPath,
`Update Release ${releaseVersion}`,
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -587,12 +587,12 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Initialize Release 2.0.0',
'Initialize release 2.0.0 by bumping all packages',
);

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -684,11 +684,11 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Initialize Release 2.0.0',
'Initialize release 2.0.0 by bumping all packages',
);
expect(commitAllChangesSpy).not.toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -905,7 +905,7 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).not.toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -1126,12 +1126,12 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Initialize Release 2.0.0',
'Initialize release 2.0.0 by bumping all packages',
);

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -1397,12 +1397,12 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Initialize Release 2.0.0',
'Initialize release 2.0.0 by bumping all packages',
);

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -1494,7 +1494,7 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).not.toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -1711,7 +1711,7 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).not.toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -1940,12 +1940,12 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Initialize Release 2.0.0',
'Initialize release 2.0.0 by bumping all packages',
);

expect(commitAllChangesSpy).toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -2037,7 +2037,7 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).not.toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down Expand Up @@ -2288,7 +2288,7 @@ describe('monorepo-workflow-operations', () => {

expect(commitAllChangesSpy).not.toHaveBeenCalledWith(
projectDirectoryPath,
'Update Release 2.0.0',
'Filter release to only selected packages',
);
});
});
Expand Down
4 changes: 2 additions & 2 deletions src/monorepo-workflow-operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export async function followMonorepoWorkflow({
await updateChangelogsForChangedPackages({ project, formatter, stderr });
await commitAllChanges(
project.directoryPath,
`Initialize Release ${newReleaseVersion}`,
`Initialize release ${newReleaseVersion} by bumping all packages`,
);
}

Expand Down Expand Up @@ -160,6 +160,6 @@ export async function followMonorepoWorkflow({
await deduplicateDependencies(project.directoryPath);
await commitAllChanges(
project.directoryPath,
`Update Release ${newReleaseVersion}`,
'Filter release to only selected packages',
);
}
146 changes: 146 additions & 0 deletions src/ui.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import { MockWritable } from 'stdio-mock';
import { buildMockPackage, buildMockProject } from '../tests/unit/helpers.js';
import {
finalizeInteractiveRelease,
prepareInteractiveReleaseBranch,
} from './ui.js';
import * as projectModule from './project.js';
import * as releasePlanModule from './release-plan.js';
import * as repoModule from './repo.js';
import * as yarnCommands from './yarn-commands.js';
import * as workflowOperations from './workflow-operations.js';

jest.mock('./project');
jest.mock('./release-plan');
jest.mock('./repo');
jest.mock('./yarn-commands');
jest.mock('./workflow-operations');
jest.mock('./dirname', () => ({
getCurrentDirectoryPath: jest.fn().mockReturnValue('/path/to/somewhere'),
}));
jest.mock('open', () => ({
__esModule: true,
default: jest.fn(),
}));

describe('ui', () => {
describe('prepareInteractiveReleaseBranch', () => {
it('updates changelogs without committing when creating an interactive release branch', async () => {
const project = buildMockProject({ directoryPath: '/path/to/project' });
const stderr = new MockWritable();
const updateChangelogsForChangedPackagesSpy = jest.spyOn(
projectModule,
'updateChangelogsForChangedPackages',
);
const commitAllChangesSpy = jest.spyOn(repoModule, 'commitAllChanges');
jest.spyOn(workflowOperations, 'createReleaseBranch').mockResolvedValue({
version: '2.0.0',
firstRun: true,
});

const result = await prepareInteractiveReleaseBranch({
project,
releaseType: 'ordinary',
formatter: 'prettier',
stderr,
});

expect(result).toStrictEqual({
version: '2.0.0',
firstRun: true,
});

expect(updateChangelogsForChangedPackagesSpy).toHaveBeenCalledWith({
project,
formatter: 'prettier',
stderr,
});
expect(commitAllChangesSpy).not.toHaveBeenCalled();
});
});

describe('finalizeInteractiveRelease', () => {
it('commits an interactive first run without resetting HEAD', async () => {
const project = buildMockProject({
directoryPath: '/path/to/project',
workspacePackages: {
'@scope/a': buildMockPackage('@scope/a', {
hasChangesSinceLatestRelease: true,
}),
},
});
const releasePlan = { newVersion: '2.0.0', packages: [] };
const stderr = new MockWritable();
const commitAllChangesSpy = jest.spyOn(repoModule, 'commitAllChanges');
jest
.spyOn(releasePlanModule, 'planRelease')
.mockResolvedValue(releasePlan);
jest.spyOn(releasePlanModule, 'executeReleasePlan').mockResolvedValue();

const result = await finalizeInteractiveRelease({
project,
defaultBranch: 'main',
formatter: 'prettier',
stderr,
version: '2.0.0',
releasedPackages: { '@scope/a': 'major' },
});

expect(result).toStrictEqual({ status: 'success' });
expect(
projectModule.restoreChangelogsForSkippedPackages,
).toHaveBeenCalledWith({
project,
releaseSpecificationPackages: { '@scope/a': 'major' },
defaultBranch: 'main',
});
expect(yarnCommands.fixConstraints).toHaveBeenCalledWith(
project.directoryPath,
);
expect(yarnCommands.updateYarnLockfile).toHaveBeenCalledWith(
project.directoryPath,
);
expect(yarnCommands.deduplicateDependencies).toHaveBeenCalledWith(
project.directoryPath,
);
expect(commitAllChangesSpy).toHaveBeenCalledTimes(1);
expect(commitAllChangesSpy).toHaveBeenCalledWith(
project.directoryPath,
'Release 2.0.0',
);
});

it('does not reset HEAD before committing an existing interactive release branch', async () => {
const project = buildMockProject({
directoryPath: '/path/to/project',
workspacePackages: {
'@scope/a': buildMockPackage('@scope/a', {
hasChangesSinceLatestRelease: true,
}),
},
});
const releasePlan = { newVersion: '2.0.0', packages: [] };
const stderr = new MockWritable();
const commitAllChangesSpy = jest.spyOn(repoModule, 'commitAllChanges');
jest
.spyOn(releasePlanModule, 'planRelease')
.mockResolvedValue(releasePlan);
jest.spyOn(releasePlanModule, 'executeReleasePlan').mockResolvedValue();

const result = await finalizeInteractiveRelease({
project,
defaultBranch: 'main',
formatter: 'prettier',
stderr,
version: '2.0.0',
releasedPackages: { '@scope/a': 'major' },
});

expect(result).toStrictEqual({ status: 'success' });
expect(commitAllChangesSpy).toHaveBeenCalledWith(
project.directoryPath,
'Release 2.0.0',
);
});
});
});
Loading
Loading