Skip to content

Commit 2288b3a

Browse files
authoredNov 8, 2022
feat(version): bump prerelease versions from conventional commits (#3362)
1 parent 0f785e4 commit 2288b3a

File tree

16 files changed

+218
-3
lines changed

16 files changed

+218
-3
lines changed
 

‎commands/version/README.md

+16
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ Running `lerna version --conventional-commits` without the above flags will rele
5555
- [`--conventional-commits`](#--conventional-commits)
5656
- [`--conventional-graduate`](#--conventional-graduate)
5757
- [`--conventional-prerelease`](#--conventional-prerelease)
58+
- [`--conventional-bump-prerelease`](#--conventional-bump-prerelease)
5859
- [`--create-release <type>`](#--create-release-type)
5960
- [`--exact`](#--exact)
6061
- [`--force-publish`](#--force-publish)
@@ -201,6 +202,21 @@ lerna version --conventional-commits --conventional-prerelease
201202

202203
When run with this flag, `lerna version` will release with prerelease versions the specified packages (comma-separated) or all packages using `*`. Releases all unreleased changes as pre(patch/minor/major/release) by prefixing the version recommendation from `conventional-commits` with `pre`, eg. if present changes include a feature commit, the recommended bump will be `minor`, so this flag will result in a `preminor` release. If changes are present for packages that are not specified (if specifying packages), or for packages that are already in prerelease, those packages will be versioned as they normally would using `--conventional-commits`.
203204

205+
### `--conventional-bump-prerelease`
206+
207+
```sh
208+
lerna version --conventional-commits --conventional-prerelease --conventional-bump-prerelease
209+
```
210+
211+
When run with this flag, `lerna version` will release with bumped prerelease versions even if already released packages are prereleases. Releases all unreleased changes as pre(patch/minor/major/release) by prefixing the version recommendation from `conventional-commits` with `pre`, eg. if present changes include a feature commit, the recommended bump will be `minor`, so this flag will result in a `preminor` release. If not used just a prerelease bump will be applied to prereleased packages.
212+
213+
```sh
214+
Changes:
215+
- major: 1.0.0-alpha.0 => 2.0.0-alpha.0
216+
- minor: 1.0.0-alpha.0 => 1.1.0-alpha.0
217+
- patch: 1.0.0-alpha.0 => 1.0.1-alpha.0
218+
```
219+
204220
### `--create-release <type>`
205221

206222
```sh

‎commands/version/__tests__/version-conventional-commits.test.js

+27
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,33 @@ describe("--conventional-commits", () => {
9191
});
9292
});
9393

94+
it("should call recommended version with conventionalBumpPrerelease set", async () => {
95+
prereleaseVersionBumps.forEach((bump) => recommendVersion.mockResolvedValueOnce(bump));
96+
const cwd = await initFixture("prerelease-independent");
97+
98+
await lernaVersion(cwd)(
99+
"--conventional-commits",
100+
"--conventional-prerelease",
101+
"--conventional-bump-prerelease"
102+
);
103+
104+
prereleaseVersionBumps.forEach((version, name) => {
105+
const prereleaseId = semver.prerelease(version)[0];
106+
expect(recommendVersion).toHaveBeenCalledWith(expect.objectContaining({ name }), "independent", {
107+
changelogPreset: undefined,
108+
rootPath: cwd,
109+
tagPrefix: "v",
110+
prereleaseId,
111+
conventionalBumpPrerelease: true,
112+
});
113+
expect(updateChangelog).toHaveBeenCalledWith(
114+
expect.objectContaining({ name, version }),
115+
"independent",
116+
{ changelogPreset: undefined, rootPath: cwd, tagPrefix: "v" }
117+
);
118+
});
119+
});
120+
94121
it("should graduate prerelease version bumps and generate CHANGELOG", async () => {
95122
versionBumps.forEach((bump) => recommendVersion.mockResolvedValueOnce(bump));
96123
const cwd = await initFixture("prerelease-independent");

‎commands/version/command.js

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ exports.builder = (yargs, composed) => {
3232
describe: "Version changed packages as prereleases when using --conventional-commits.",
3333
// type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)
3434
},
35+
"conventional-bump-prerelease": {
36+
describe: "Bumps prerelease versions if conventional commits requires it.",
37+
type: "boolean",
38+
},
3539
"changelog-preset": {
3640
describe: "Custom conventional-changelog preset.",
3741
type: "string",

‎commands/version/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ class VersionCommand extends Command {
367367

368368
recommendVersions(resolvePrereleaseId) {
369369
const independentVersions = this.project.isIndependent();
370-
const { changelogPreset, conventionalGraduate } = this.options;
370+
const { changelogPreset, conventionalGraduate, conventionalBumpPrerelease } = this.options;
371371
const rootPath = this.project.manifest.location;
372372
const type = independentVersions ? "independent" : "fixed";
373373
const prereleasePackageNames = this.getPrereleasePackageNames();
@@ -394,6 +394,7 @@ class VersionCommand extends Command {
394394
rootPath,
395395
tagPrefix: this.tagPrefix,
396396
prereleaseId: getPrereleaseId(node),
397+
conventionalBumpPrerelease,
397398
})
398399
)
399400
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"command": {
3+
"publish": {
4+
"conventionalCommits": true
5+
}
6+
},
7+
"version": "independent"
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "conventional-commits-independent",
3+
"repository": "lerna/conventional-commits-independent",
4+
"version": "0.0.0-root"
5+
}

‎core/conventional-commits/__tests__/__fixtures__/prerelease-independent/packages/package-1/CHANGELOG.md

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"name": "package-1",
3+
"repository": "lerna/conventional-commits-independent",
4+
"version": "1.0.0-alpha.0"
5+
}

‎core/conventional-commits/__tests__/__fixtures__/prerelease-independent/packages/package-2/CHANGELOG.md

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "package-2",
3+
"repository": "lerna/conventional-commits-independent",
4+
"version": "1.0.0-beta.0",
5+
"dependencies": {
6+
"package-1": "^1.0.0"
7+
}
8+
}

‎core/conventional-commits/__tests__/__fixtures__/prerelease-independent/packages/package-3/CHANGELOG.md

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"name": "package-3",
3+
"repository": "lerna/conventional-commits-independent",
4+
"version": "1.0.0-beta.0",
5+
"dependencies": {
6+
"package-1": "^1.0.0"
7+
}
8+
}

‎core/conventional-commits/__tests__/conventional-commits.test.js

+74
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,80 @@ describe("conventional-commits", () => {
9595
expect(bump2).toBe("1.1.0-beta.0");
9696
});
9797

98+
it("returns package-specific version bumps from prereleases with prereleaseId", async () => {
99+
const cwd = await initFixture("prerelease-independent");
100+
const [pkg1, pkg2, pkg3] = await getPackages(cwd);
101+
const opts = { changelogPreset: "angular" };
102+
103+
// make a change in package-1, package-2 and package-3
104+
await pkg1.set("changed", 1).serialize();
105+
await pkg2.set("changed", 2).serialize();
106+
await pkg3.set("changed", 3).serialize();
107+
108+
await gitAdd(cwd, pkg1.manifestLocation);
109+
await gitCommit(cwd, "fix: changed 1");
110+
111+
await gitAdd(cwd, pkg2.manifestLocation);
112+
await gitCommit(cwd, "feat: changed 2");
113+
114+
await gitAdd(cwd, pkg3.manifestLocation);
115+
await gitCommit(cwd, "feat!: changed\n\nBREAKING CHANGE: changed");
116+
117+
const [bump1, bump2, bump3] = await Promise.all([
118+
recommendVersion(
119+
pkg1,
120+
"independent",
121+
Object.assign(opts, { prereleaseId: "alpha", conventionalBumpPrerelease: true })
122+
),
123+
recommendVersion(
124+
pkg2,
125+
"independent",
126+
Object.assign(opts, { prereleaseId: "beta", conventionalBumpPrerelease: true })
127+
),
128+
recommendVersion(
129+
pkg3,
130+
"independent",
131+
Object.assign(opts, { prereleaseId: "beta", conventionalBumpPrerelease: true })
132+
),
133+
]);
134+
135+
// all versions should be bumped
136+
expect(bump1).toBe("1.0.1-alpha.0");
137+
expect(bump2).toBe("1.1.0-beta.0");
138+
expect(bump3).toBe("2.0.0-beta.0");
139+
});
140+
141+
it("returns package-specific prerelease bumps from prereleases with prereleaseId", async () => {
142+
const cwd = await initFixture("prerelease-independent");
143+
const [pkg1, pkg2, pkg3] = await getPackages(cwd);
144+
const opts = { changelogPreset: "angular" };
145+
146+
// make a change in package-1, package-2 and package-3
147+
await pkg1.set("changed", 1).serialize();
148+
await pkg2.set("changed", 2).serialize();
149+
await pkg3.set("changed", 3).serialize();
150+
151+
await gitAdd(cwd, pkg1.manifestLocation);
152+
await gitCommit(cwd, "fix: changed 1");
153+
154+
await gitAdd(cwd, pkg2.manifestLocation);
155+
await gitCommit(cwd, "feat: changed 2");
156+
157+
await gitAdd(cwd, pkg3.manifestLocation);
158+
await gitCommit(cwd, "feat!: changed\n\nBREAKING CHANGE: changed");
159+
160+
const [bump1, bump2, bump3] = await Promise.all([
161+
recommendVersion(pkg1, "independent", Object.assign(opts, { prereleaseId: "alpha" })),
162+
recommendVersion(pkg2, "independent", Object.assign(opts, { prereleaseId: "beta" })),
163+
recommendVersion(pkg3, "independent", Object.assign(opts, { prereleaseId: "beta" })),
164+
]);
165+
166+
// we just have a bump in the prerelease
167+
expect(bump1).toBe("1.0.0-alpha.1");
168+
expect(bump2).toBe("1.0.0-beta.1");
169+
expect(bump3).toBe("1.0.0-beta.1");
170+
});
171+
98172
it("falls back to patch bumps for non-bumping commit types", async () => {
99173
const cwd = await initFixture("independent");
100174
const [pkg1, pkg2] = await getPackages(cwd);

‎core/conventional-commits/lib/recommend-version.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,11 @@ module.exports.recommendVersion = recommendVersion;
1212
* @param {import("..").VersioningStrategy} type
1313
* @param {import("..").BaseChangelogOptions & { prereleaseId?: string }} commandOptions
1414
*/
15-
function recommendVersion(pkg, type, { changelogPreset, rootPath, tagPrefix, prereleaseId }) {
15+
function recommendVersion(
16+
pkg,
17+
type,
18+
{ changelogPreset, rootPath, tagPrefix, prereleaseId, conventionalBumpPrerelease }
19+
) {
1620
log.silly(type, "for %s at %s", pkg.name, pkg.location);
1721

1822
const options = {
@@ -59,7 +63,7 @@ function recommendVersion(pkg, type, { changelogPreset, rootPath, tagPrefix, pre
5963
let releaseType = data.releaseType || "patch";
6064

6165
if (prereleaseId) {
62-
const shouldBump = shouldBumpPrerelease(releaseType, pkg.version);
66+
const shouldBump = conventionalBumpPrerelease || shouldBumpPrerelease(releaseType, pkg.version);
6367
const prereleaseType = shouldBump ? `pre${releaseType}` : "prerelease";
6468
log.verbose(type, "increment %s by %s", pkg.version, prereleaseType);
6569
resolve(semver.inc(pkg.version, prereleaseType, prereleaseId));

‎core/lerna/schemas/lerna-schema.json

+10
Original file line numberDiff line numberDiff line change
@@ -988,6 +988,9 @@
988988
"conventionalPrerelease": {
989989
"$ref": "#/$defs/commandOptions/version/conventionalPrerelease"
990990
},
991+
"conventionalBumpPrerelease": {
992+
"$ref": "#/$defs/commandOptions/version/conventionalBumpPrerelease"
993+
},
991994
"changelogPreset": {
992995
"$ref": "#/$defs/commandOptions/version/changelogPreset"
993996
},
@@ -1290,6 +1293,9 @@
12901293
"conventionalPrerelease": {
12911294
"$ref": "#/$defs/commandOptions/version/conventionalPrerelease"
12921295
},
1296+
"conventionalBumpPrerelease": {
1297+
"$ref": "#/$defs/commandOptions/version/conventionalBumpPrerelease"
1298+
},
12931299
"changelogPreset": {
12941300
"$ref": "#/$defs/commandOptions/version/changelogPreset"
12951301
},
@@ -1680,6 +1686,10 @@
16801686
],
16811687
"description": "During `lerna version`, version changed packages as prereleases when using --conventional-commits."
16821688
},
1689+
"conventionalBumpPrerelease": {
1690+
"type": "boolean",
1691+
"description": "During `lerna version`, bumps version of changed prereleased packages when using --conventional-commits."
1692+
},
16831693
"changelogPreset": {
16841694
"type": "string",
16851695
"description": "For `lerna version`, the custom conventional-changelog preset."

‎e2e/tests/lerna-version/conventional-commits.spec.ts

+45
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,51 @@ describe("lerna-version-conventional-commits", () => {
288288
`);
289289
});
290290

291+
it("should correctly generate and bump prerelease versions when using --conventional-prerelease and --conventional-bump-prerelease", async () => {
292+
await fixture.createInitialGitCommit();
293+
294+
await fixture.lerna("create package-a -y");
295+
await fixture.exec("git add --all");
296+
await fixture.exec("git commit -m 'feat: add package-a'");
297+
298+
await fixture.lerna("create package-b -y");
299+
await fixture.exec("git add --all");
300+
await fixture.exec("git commit -m 'feat: add package-b'");
301+
302+
await fixture.exec("git push origin test-main");
303+
304+
// Initial versioning with two packages created
305+
await fixture.lerna("version --conventional-commits --conventional-prerelease -y", {
306+
silenceError: true,
307+
});
308+
309+
// Update and version just package-a
310+
await fixture.exec("echo update_package_a > packages/package-a/new_file.txt");
311+
await fixture.exec("git add --all");
312+
await fixture.exec("git commit -m 'fix: update package-a'");
313+
314+
// Bump a prerelease version
315+
const output = await fixture.lerna("version --conventional-commits --conventional-bump-prerelease -y", {
316+
silenceError: true,
317+
});
318+
319+
expect(output.combinedOutput).toMatchInlineSnapshot(`
320+
lerna notice cli v999.9.9-e2e.0
321+
lerna info current version 0.1.0-alpha.0
322+
lerna info Looking for changed packages since v0.1.0-alpha.0
323+
lerna info getChangelogConfig Successfully resolved preset "conventional-changelog-angular"
324+
325+
Changes:
326+
- package-a: 0.1.0-alpha.0 => 0.1.1-alpha.0
327+
328+
lerna info auto-confirmed
329+
lerna info execute Skipping releases
330+
lerna info git Pushing tags...
331+
lerna success version finished
332+
333+
`);
334+
});
335+
291336
describe("independent packages", () => {
292337
it("should correctly generate changelog and version information when releasing packages independently", async () => {
293338
await fixture.createInitialGitCommit();

0 commit comments

Comments
 (0)
Please sign in to comment.