Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(version): bump prerelease versions from conventional commits (#2… #3362

Merged
merged 3 commits into from Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions commands/version/README.md
Expand Up @@ -55,6 +55,7 @@ Running `lerna version --conventional-commits` without the above flags will rele
- [`--conventional-commits`](#--conventional-commits)
- [`--conventional-graduate`](#--conventional-graduate)
- [`--conventional-prerelease`](#--conventional-prerelease)
- [`--conventional-bump-prerelease`](#--conventional-bump-prerelease)
- [`--create-release <type>`](#--create-release-type)
- [`--exact`](#--exact)
- [`--force-publish`](#--force-publish)
Expand Down Expand Up @@ -201,6 +202,21 @@ lerna version --conventional-commits --conventional-prerelease

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`.

### `--conventional-bump-prerelease`

```sh
lerna version --conventional-commits --conventional-prerelease --conventional-bump-prerelease
```

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.

```sh
Changes:
- major: 1.0.0-alpha.0 => 2.0.0-alpha.0
- minor: 1.0.0-alpha.0 => 1.1.0-alpha.0
- patch: 1.0.0-alpha.0 => 1.0.1-alpha.0
```

### `--create-release <type>`

```sh
Expand Down
27 changes: 27 additions & 0 deletions commands/version/__tests__/version-conventional-commits.test.js
Expand Up @@ -91,6 +91,33 @@ describe("--conventional-commits", () => {
});
});

it("should call recommended version with conventionalBumpPrerelease set", async () => {
prereleaseVersionBumps.forEach((bump) => recommendVersion.mockResolvedValueOnce(bump));
const cwd = await initFixture("prerelease-independent");

await lernaVersion(cwd)(
"--conventional-commits",
"--conventional-prerelease",
"--conventional-bump-prerelease"
);

prereleaseVersionBumps.forEach((version, name) => {
const prereleaseId = semver.prerelease(version)[0];
expect(recommendVersion).toHaveBeenCalledWith(expect.objectContaining({ name }), "independent", {
changelogPreset: undefined,
rootPath: cwd,
tagPrefix: "v",
prereleaseId,
conventionalBumpPrerelease: true,
});
expect(updateChangelog).toHaveBeenCalledWith(
expect.objectContaining({ name, version }),
"independent",
{ changelogPreset: undefined, rootPath: cwd, tagPrefix: "v" }
);
});
});

it("should graduate prerelease version bumps and generate CHANGELOG", async () => {
versionBumps.forEach((bump) => recommendVersion.mockResolvedValueOnce(bump));
const cwd = await initFixture("prerelease-independent");
Expand Down
4 changes: 4 additions & 0 deletions commands/version/command.js
Expand Up @@ -32,6 +32,10 @@ exports.builder = (yargs, composed) => {
describe: "Version changed packages as prereleases when using --conventional-commits.",
// type must remain ambiguous because it is overloaded (boolean _or_ string _or_ array)
},
"conventional-bump-prerelease": {
describe: "Bumps prerelease versions if conventional commits requires it.",
type: "boolean",
},
"changelog-preset": {
describe: "Custom conventional-changelog preset.",
type: "string",
Expand Down
3 changes: 2 additions & 1 deletion commands/version/index.js
Expand Up @@ -367,7 +367,7 @@ class VersionCommand extends Command {

recommendVersions(resolvePrereleaseId) {
const independentVersions = this.project.isIndependent();
const { changelogPreset, conventionalGraduate } = this.options;
const { changelogPreset, conventionalGraduate, conventionalBumpPrerelease } = this.options;
const rootPath = this.project.manifest.location;
const type = independentVersions ? "independent" : "fixed";
const prereleasePackageNames = this.getPrereleasePackageNames();
Expand All @@ -394,6 +394,7 @@ class VersionCommand extends Command {
rootPath,
tagPrefix: this.tagPrefix,
prereleaseId: getPrereleaseId(node),
conventionalBumpPrerelease,
})
)
);
Expand Down
@@ -0,0 +1,8 @@
{
"command": {
"publish": {
"conventionalCommits": true
}
},
"version": "independent"
}
@@ -0,0 +1,5 @@
{
"name": "conventional-commits-independent",
"repository": "lerna/conventional-commits-independent",
"version": "0.0.0-root"
}
@@ -0,0 +1,5 @@
{
"name": "package-1",
"repository": "lerna/conventional-commits-independent",
"version": "1.0.0-alpha.0"
}
@@ -0,0 +1,8 @@
{
"name": "package-2",
"repository": "lerna/conventional-commits-independent",
"version": "1.0.0-beta.0",
"dependencies": {
"package-1": "^1.0.0"
}
}
@@ -0,0 +1,8 @@
{
"name": "package-3",
"repository": "lerna/conventional-commits-independent",
"version": "1.0.0-beta.0",
"dependencies": {
"package-1": "^1.0.0"
}
}
74 changes: 74 additions & 0 deletions core/conventional-commits/__tests__/conventional-commits.test.js
Expand Up @@ -95,6 +95,80 @@ describe("conventional-commits", () => {
expect(bump2).toBe("1.1.0-beta.0");
});

it("returns package-specific version bumps from prereleases with prereleaseId", async () => {
const cwd = await initFixture("prerelease-independent");
const [pkg1, pkg2, pkg3] = await getPackages(cwd);
const opts = { changelogPreset: "angular" };

// make a change in package-1, package-2 and package-3
await pkg1.set("changed", 1).serialize();
await pkg2.set("changed", 2).serialize();
await pkg3.set("changed", 3).serialize();

await gitAdd(cwd, pkg1.manifestLocation);
await gitCommit(cwd, "fix: changed 1");

await gitAdd(cwd, pkg2.manifestLocation);
await gitCommit(cwd, "feat: changed 2");

await gitAdd(cwd, pkg3.manifestLocation);
await gitCommit(cwd, "feat!: changed\n\nBREAKING CHANGE: changed");

const [bump1, bump2, bump3] = await Promise.all([
recommendVersion(
pkg1,
"independent",
Object.assign(opts, { prereleaseId: "alpha", conventionalBumpPrerelease: true })
),
recommendVersion(
pkg2,
"independent",
Object.assign(opts, { prereleaseId: "beta", conventionalBumpPrerelease: true })
),
recommendVersion(
pkg3,
"independent",
Object.assign(opts, { prereleaseId: "beta", conventionalBumpPrerelease: true })
),
]);

// all versions should be bumped
expect(bump1).toBe("1.0.1-alpha.0");
expect(bump2).toBe("1.1.0-beta.0");
expect(bump3).toBe("2.0.0-beta.0");
});

it("returns package-specific prerelease bumps from prereleases with prereleaseId", async () => {
const cwd = await initFixture("prerelease-independent");
const [pkg1, pkg2, pkg3] = await getPackages(cwd);
const opts = { changelogPreset: "angular" };

// make a change in package-1, package-2 and package-3
await pkg1.set("changed", 1).serialize();
await pkg2.set("changed", 2).serialize();
await pkg3.set("changed", 3).serialize();

await gitAdd(cwd, pkg1.manifestLocation);
await gitCommit(cwd, "fix: changed 1");

await gitAdd(cwd, pkg2.manifestLocation);
await gitCommit(cwd, "feat: changed 2");

await gitAdd(cwd, pkg3.manifestLocation);
await gitCommit(cwd, "feat!: changed\n\nBREAKING CHANGE: changed");

const [bump1, bump2, bump3] = await Promise.all([
recommendVersion(pkg1, "independent", Object.assign(opts, { prereleaseId: "alpha" })),
recommendVersion(pkg2, "independent", Object.assign(opts, { prereleaseId: "beta" })),
recommendVersion(pkg3, "independent", Object.assign(opts, { prereleaseId: "beta" })),
]);

// we just have a bump in the prerelease
expect(bump1).toBe("1.0.0-alpha.1");
expect(bump2).toBe("1.0.0-beta.1");
expect(bump3).toBe("1.0.0-beta.1");
});

it("falls back to patch bumps for non-bumping commit types", async () => {
const cwd = await initFixture("independent");
const [pkg1, pkg2] = await getPackages(cwd);
Expand Down
8 changes: 6 additions & 2 deletions core/conventional-commits/lib/recommend-version.js
Expand Up @@ -12,7 +12,11 @@ module.exports.recommendVersion = recommendVersion;
* @param {import("..").VersioningStrategy} type
* @param {import("..").BaseChangelogOptions & { prereleaseId?: string }} commandOptions
*/
function recommendVersion(pkg, type, { changelogPreset, rootPath, tagPrefix, prereleaseId }) {
function recommendVersion(
pkg,
type,
{ changelogPreset, rootPath, tagPrefix, prereleaseId, conventionalBumpPrerelease }
) {
log.silly(type, "for %s at %s", pkg.name, pkg.location);

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

if (prereleaseId) {
const shouldBump = shouldBumpPrerelease(releaseType, pkg.version);
const shouldBump = conventionalBumpPrerelease || shouldBumpPrerelease(releaseType, pkg.version);
const prereleaseType = shouldBump ? `pre${releaseType}` : "prerelease";
log.verbose(type, "increment %s by %s", pkg.version, prereleaseType);
resolve(semver.inc(pkg.version, prereleaseType, prereleaseId));
Expand Down
10 changes: 10 additions & 0 deletions core/lerna/schemas/lerna-schema.json
Expand Up @@ -985,6 +985,9 @@
"conventionalPrerelease": {
"$ref": "#/$defs/commandOptions/version/conventionalPrerelease"
},
"conventionalBumpPrerelease": {
"$ref": "#/$defs/commandOptions/version/conventionalBumpPrerelease"
},
"changelogPreset": {
"$ref": "#/$defs/commandOptions/version/changelogPreset"
},
Expand Down Expand Up @@ -1287,6 +1290,9 @@
"conventionalPrerelease": {
"$ref": "#/$defs/commandOptions/version/conventionalPrerelease"
},
"conventionalBumpPrerelease": {
"$ref": "#/$defs/commandOptions/version/conventionalBumpPrerelease"
},
"changelogPreset": {
"$ref": "#/$defs/commandOptions/version/changelogPreset"
},
Expand Down Expand Up @@ -1673,6 +1679,10 @@
],
"description": "During `lerna version`, version changed packages as prereleases when using --conventional-commits."
},
"conventionalBumpPrerelease": {
"type": "boolean",
"description": "During `lerna version`, bumps version of changed prereleased packages when using --conventional-commits."
},
"changelogPreset": {
"type": "string",
"description": "For `lerna version`, the custom conventional-changelog preset."
Expand Down
45 changes: 45 additions & 0 deletions e2e/tests/lerna-version/conventional-commits.spec.ts
Expand Up @@ -288,6 +288,51 @@ describe("lerna-version-conventional-commits", () => {
`);
});

it("should correctly generate and bump prerelease versions when using --conventional-prerelease and --conventional-bump-prerelease", async () => {
await fixture.createInitialGitCommit();

await fixture.lerna("create package-a -y");
await fixture.exec("git add --all");
await fixture.exec("git commit -m 'feat: add package-a'");

await fixture.lerna("create package-b -y");
await fixture.exec("git add --all");
await fixture.exec("git commit -m 'feat: add package-b'");

await fixture.exec("git push origin test-main");

// Initial versioning with two packages created
await fixture.lerna("version --conventional-commits --conventional-prerelease -y", {
silenceError: true,
});

// Update and version just package-a
await fixture.exec("echo update_package_a > packages/package-a/new_file.txt");
await fixture.exec("git add --all");
await fixture.exec("git commit -m 'fix: update package-a'");

// Bump a prerelease version
const output = await fixture.lerna("version --conventional-commits --conventional-bump-prerelease -y", {
silenceError: true,
});

expect(output.combinedOutput).toMatchInlineSnapshot(`
lerna notice cli v999.9.9-e2e.0
lerna info current version 0.1.0-alpha.0
lerna info Looking for changed packages since v0.1.0-alpha.0
lerna info getChangelogConfig Successfully resolved preset "conventional-changelog-angular"

Changes:
- package-a: 0.1.0-alpha.0 => 0.1.1-alpha.0

lerna info auto-confirmed
lerna info execute Skipping releases
lerna info git Pushing tags...
lerna success version finished

`);
});

describe("independent packages", () => {
it("should correctly generate changelog and version information when releasing packages independently", async () => {
await fixture.createInitialGitCommit();
Expand Down