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 1 commit
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-bumpprerelease`](#--conventional-bumpprerelease)
- [`--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-bumpprerelease`
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved

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

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-bumpprerelease"
);

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-bumpprerelease": {
describe: "Bumps a prerelease versions if convetional commits require it.",
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
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;
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
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
12 changes: 8 additions & 4 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 }
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
) {
log.silly(type, "for %s at %s", pkg.name, pkg.location);

const options = {
Expand All @@ -26,8 +30,8 @@ function recommendVersion(pkg, type, { changelogPreset, rootPath, tagPrefix, pre
options.tagPrefix = tagPrefix;
}

const shouldBumpPrerelease = (releaseType, version) => {
if (!semver.prerelease(version)) {
const shouldBumpPrerelease = (releaseType, version, bumpPrerelease) => {
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
if (!semver.prerelease(version) || bumpPrerelease) {
return true;
}
switch (releaseType) {
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 = shouldBumpPrerelease(releaseType, pkg.version, conventionalBumpprerelease);
JamesHenry marked this conversation as resolved.
Show resolved Hide resolved
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