Skip to content

Commit 914dd96

Browse files
authored
feat(version): add option to force version update (#3852)
1 parent be1f4e3 commit 914dd96

File tree

10 files changed

+189
-6
lines changed

10 files changed

+189
-6
lines changed

e2e/version/src/conventional-commits.spec.ts

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,55 @@ describe("lerna-version-conventional-commits", () => {
289289
`);
290290
});
291291

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

@@ -532,6 +581,61 @@ describe("lerna-version-conventional-commits", () => {
532581
(await fixture.exec("git ls-remote origin refs/tags/[email protected]")).combinedOutput
533582
).toMatchInlineSnapshot(``);
534583
});
584+
585+
it("should correctly bump versions when using --conventional-graduate and --force-conventional-graduate", async () => {
586+
await fixture.createInitialGitCommit();
587+
588+
fixture.updateJson("lerna.json", (json) => {
589+
// eslint-disable-next-line no-param-reassign
590+
json.version = "independent";
591+
return json;
592+
});
593+
594+
await fixture.lerna("create package-a -y");
595+
await fixture.exec("git add --all");
596+
await fixture.exec("git commit -m 'feat: add package-a'");
597+
598+
await fixture.lerna("create package-b -y");
599+
await fixture.exec("git add --all");
600+
await fixture.exec("git commit -m 'feat: add package-b'");
601+
602+
await fixture.exec("git push origin test-main");
603+
604+
// Initial versioning with two packages created
605+
await fixture.lerna("version --conventional-commits -y", { silenceError: true });
606+
607+
// Update and version just package-a
608+
await fixture.exec("echo update_package_a > packages/package-a/new_file.txt");
609+
await fixture.exec("git add --all");
610+
await fixture.exec("git commit -m 'feat: update package-a'");
611+
612+
// Create a version with force-conventional-graduate
613+
const output = await fixture.lerna(
614+
"version --conventional-commits --conventional-graduate --force-conventional-graduate -y",
615+
{
616+
silenceError: true,
617+
}
618+
);
619+
620+
expect(output.combinedOutput).toMatchInlineSnapshot(`
621+
lerna notice cli v999.9.9-e2e.0
622+
lerna info versioning independent
623+
lerna WARN conventional-graduate all packages
624+
lerna info Graduating all prereleased packages
625+
lerna info Looking for changed packages since [email protected]
626+
lerna info getChangelogConfig Successfully resolved preset "conventional-changelog-angular"
627+
628+
Changes:
629+
- package-a: 1.1.0 => 1.2.0
630+
- package-b: 1.1.0 => 1.1.1
631+
632+
lerna info auto-confirmed
633+
lerna info execute Skipping releases
634+
lerna info git Pushing tags...
635+
lerna success version finished
636+
637+
`);
638+
});
535639
});
536640
});
537641
});

libs/commands/changed/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Unlike `lerna ls`, however, `lerna changed` **does not** support [filter options
3434
`lerna changed` supports the following options of [`lerna version`](https://github.com/lerna/lerna/tree/main/libs/commands/version#options) (the others are irrelevant):
3535

3636
- [`--conventional-graduate`](https://github.com/lerna/lerna/tree/main/libs/commands/version#--conventional-graduate).
37+
- [`--force-conventional-graduate`](https://github.com/lerna/lerna/tree/main/libs/commands/version#--force-conventional-graduate).
3738
- [`--force-publish`](https://github.com/lerna/lerna/tree/main/libs/commands/version#--force-publish).
3839
- [`--ignore-changes`](https://github.com/lerna/lerna/tree/main/libs/commands/version#--ignore-changes).
3940
- [`--include-merged-tags`](https://github.com/lerna/lerna/tree/main/libs/commands/version#--include-merged-tags).

libs/commands/changed/src/command.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ const command: CommandModule = {
2020
describe: "Detect currently prereleased packages that would change to a non-prerelease version.",
2121
// type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)
2222
},
23+
"force-conventional-graduate": {
24+
describe:
25+
"Always include all packages by specified by --conventional-graduate whether or not they are a prerelease or have changes since the previous version.",
26+
type: "boolean",
27+
},
2328
"force-publish": {
2429
describe: "Always include targeted packages when detecting changed packages, skipping default logic.",
2530
// type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)

libs/commands/changed/src/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ module.exports = function factory(argv: NodeJS.Process["argv"]) {
1212

1313
interface ChangedCommandOptions extends CommandConfigOptions {
1414
conventionalCommits: boolean;
15-
conventionalGraduate: boolean;
16-
forcePublish: boolean;
15+
conventionalGraduate: boolean | string;
16+
forceConventionalGraduate: boolean;
17+
forcePublish: boolean | string;
1718
}
1819

1920
class ChangedCommand extends Command<ChangedCommandOptions> {

libs/commands/version/README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ Running `lerna version --conventional-commits` without the above flags will rele
5656
- [`--changelog-entry-additional-markdown`](#--changelog-entry-additional-markdown)
5757
- [`--conventional-commits`](#--conventional-commits)
5858
- [`--conventional-graduate`](#--conventional-graduate)
59+
- [`--force-conventional-graduate`](#--force-conventional-graduate)
5960
- [`--conventional-prerelease`](#--conventional-prerelease)
6061
- [`--conventional-bump-prerelease`](#--conventional-bump-prerelease)
6162
- [`--create-release <type>`](#--create-release-type)
@@ -226,6 +227,17 @@ When run with this flag, `lerna version` will graduate the specified packages (c
226227

227228
> NOTE: when specifying packages, dependents of specified packages will be released, but will not be graduated.
228229
230+
### `--force-conventional-graduate`
231+
232+
```sh
233+
lerna version --conventional-commits --conventional-graduate=package-2,package-4 --force-conventional-graduate
234+
235+
# force all prerelease packages to be graduated and updated if not a prerelease or having no change
236+
lerna version --conventional-commits --conventional-graduate --force-conventional-graduate
237+
```
238+
239+
When run with this flag, `lerna version` will graduate all packages specified by `--conventional-graduate`. Non-prerelease packages will not be ignored as it would be the case without the flag. In combination with single version mode this can be used to force all specified packages to be updated to a single version despite having no change or being a non-prerelease version. It works similar to `--force-publish` but is not ignored when `--conventional-commits` and `--conventional-graduate` are enabled. This flag is only applicable when having `--conventional-graduate` set, otherwise the option is ignored.
240+
229241
### `--conventional-prerelease`
230242

231243
```sh

libs/commands/version/src/command.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ const command: CommandModule = {
5555
describe: "Version currently prereleased packages to a non-prerelease version.",
5656
// type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)
5757
},
58+
"force-conventional-graduate": {
59+
describe:
60+
"Forces all packages specified by --conventional-graduate to bump their version whether or not they are a prerelease or have changes since the previous version.",
61+
type: "boolean",
62+
},
5863
"conventional-prerelease": {
5964
describe: "Version changed packages as prereleases when using --conventional-commits.",
6065
// type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)

libs/commands/version/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ interface VersionCommandConfigOptions extends CommandConfigOptions {
7575
exact?: boolean;
7676
conventionalPrerelease?: string;
7777
conventionalGraduate?: string;
78+
forceConventionalGraduate?: boolean;
7879
private?: boolean;
7980
forcePublish?: boolean | string | string[];
8081
bump?: string;

libs/core/src/lib/collect-updates/collect-project-updates.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,42 @@ describe("collectProjectUpdates", () => {
332332
]);
333333
});
334334

335+
it("always includes all nodes targeted by --conventional-graduate = pkg despite having no prerelease version when having -froce-convention-graduate set", () => {
336+
changedPackages.add("package-dag-3");
337+
338+
const graph = buildGraph();
339+
const nodes = Object.values(graph.nodes);
340+
const execOpts = { cwd: "/test" };
341+
setPrereleaseVersions(graph, ["package-standalone"]);
342+
343+
const updates = collectProjectUpdates(nodes, graph, execOpts, {
344+
forceConventionalGraduate: true,
345+
conventionalCommits: true,
346+
conventionalGraduate: "package-dag-2b,package-dag-3",
347+
});
348+
349+
expect(updates).toEqual([
350+
expect.objectContaining({ name: "package-dag-2b" }),
351+
expect.objectContaining({ name: "package-dag-3" }),
352+
]);
353+
});
354+
355+
it("always includes all nodes targeted by --conventional-graduate = * despite having no prerelease version when having -froce-convention-graduate set", () => {
356+
changedPackages.add("package-dag-3");
357+
358+
const graph = buildGraph();
359+
const nodes = Object.values(graph.nodes);
360+
const execOpts = { cwd: "/test" };
361+
362+
const updates = collectProjectUpdates(nodes, graph, execOpts, {
363+
forceConventionalGraduate: true,
364+
conventionalCommits: true,
365+
conventionalGraduate: "*",
366+
});
367+
368+
expect(updates).toEqual(ALL_NODES);
369+
});
370+
335371
it("uses revision range with --canary", () => {
336372
changedPackages.add("package-dag-2a");
337373

libs/core/src/lib/collect-updates/collect-project-updates.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export interface ProjectUpdateCollectorOptions {
2727
since?: string;
2828
conventionalCommits?: boolean;
2929
conventionalGraduate?: string | boolean;
30+
forceConventionalGraduate?: boolean;
3031
excludeDependents?: boolean;
3132
}
3233

@@ -39,10 +40,16 @@ export function collectProjectUpdates(
3940
execOpts: ExecOptions,
4041
commandOptions: ProjectUpdateCollectorOptions
4142
): ProjectGraphProjectNodeWithPackage[] {
42-
const { forcePublish, conventionalCommits, conventionalGraduate, excludeDependents } = commandOptions;
43+
const {
44+
forcePublish,
45+
conventionalCommits,
46+
forceConventionalGraduate,
47+
conventionalGraduate,
48+
excludeDependents,
49+
} = commandOptions;
4350

44-
// If --conventional-commits and --conventional-graduate are both set, ignore --force-publish
45-
const useConventionalGraduate = conventionalCommits && conventionalGraduate;
51+
// If --conventional-commits and --conventional-graduate are both set, ignore --force-publish but consider --force-conventional-graduate
52+
const useConventionalGraduate = conventionalCommits && (conventionalGraduate || forceConventionalGraduate);
4653
const forced = getPackagesForOption(useConventionalGraduate ? conventionalGraduate : forcePublish);
4754

4855
let committish = commandOptions.since ?? "";
@@ -107,7 +114,8 @@ export function collectProjectUpdates(
107114
const isForced = (node: ProjectGraphProjectNodeWithPackage, name: string) =>
108115
!!(
109116
(forced.has("*") || forced.has(name)) &&
110-
(useConventionalGraduate ? prereleaseIdFromVersion(getPackage(node).version) : true)
117+
((useConventionalGraduate ? prereleaseIdFromVersion(getPackage(node).version) : true) ||
118+
forceConventionalGraduate)
111119
);
112120

113121
return collectProjects(filteredProjects, projectGraph, {

packages/lerna/schemas/lerna-schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@
200200
"conventionalGraduate": {
201201
"$ref": "#/$defs/commandOptions/shared/conventionalGraduate"
202202
},
203+
"forceConventionalGraduate": {
204+
"$ref": "#/$defs/commandOptions/shared/forceConventionalGraduate"
205+
},
203206
"forcePublish": {
204207
"$ref": "#/$defs/commandOptions/shared/forcePublish"
205208
},
@@ -961,6 +964,9 @@
961964
"conventionalGraduate": {
962965
"$ref": "#/$defs/commandOptions/shared/conventionalGraduate"
963966
},
967+
"forceConventionalGraduate": {
968+
"$ref": "#/$defs/commandOptions/shared/forceConventionalGraduate"
969+
},
964970
"conventionalPrerelease": {
965971
"$ref": "#/$defs/commandOptions/version/conventionalPrerelease"
966972
},
@@ -1752,6 +1758,10 @@
17521758
],
17531759
"description": "Detect currently prereleased packages that would change to a non-prerelease version. Relevant for `lerna changed` and `lerna version`."
17541760
},
1761+
"forceConventionalGraduate": {
1762+
"type": "boolean",
1763+
"description": "Forces all packages specified by --conventional-graduate to bump their version whether or not they are a prerelease or have changes since the previous version. Relevant for `lerna changed` and `lerna version`."
1764+
},
17551765
"forceLocal": {
17561766
"type": "boolean",
17571767
"description": "During `lerna bootstrap` and `lerna link`, when true, force local sibling links regardless of version range match."

0 commit comments

Comments
 (0)