Skip to content

Commit 4974b78

Browse files
lbennett-stackievocateur
authored andcommitted
feat(version): Add --create-release=[gitlab|github] option (#2073)
Deprecates `--github-release`, replacing with `--create-release=github`
1 parent b22345b commit 4974b78

20 files changed

Lines changed: 547 additions & 112 deletions

File tree

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
"use strict";
2+
3+
const client = {
4+
repos: {
5+
createRelease: jest.fn(),
6+
},
7+
};
8+
9+
module.exports = () => client;

commands/version/README.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ Running `lerna version --conventional-commits` without the above flags will rele
5151
- [`--exact`](#--exact)
5252
- [`--force-publish`](#--force-publish)
5353
- [`--git-remote`](#--git-remote-name)
54-
- [`--github-release`](#--github-release)
54+
- [`--create-release`](#--create-release-type)
5555
- [`--ignore-changes`](#--ignore-changes)
5656
- [`--include-merged-tags`](#--include-merged-tags)
5757
- [`--message`](#--message-msg)
@@ -194,19 +194,25 @@ lerna version --git-remote upstream
194194

195195
When run with this flag, `lerna version` will push the git changes to the specified remote instead of `origin`.
196196

197-
### `--github-release`
197+
### `--create-release <type>`
198198

199199
```sh
200-
lerna version --github-release --conventional-commits
200+
lerna version --conventional-commits --create-release github
201+
lerna version --conventional-commits --create-release gitlab
201202
```
202203

203-
When run with this flag, `lerna version` will create an official GitHub release based on the changed packages. Requires `--conventional-commits` to be passed so that changelogs can be generated.
204+
When run with this flag, `lerna version` will create an official GitHub or GitLab release based on the changed packages. Requires `--conventional-commits` to be passed so that changelogs can be generated.
204205

205206
To authenticate with GitHub, the following environment variables can be defined.
206207

207208
- `GH_TOKEN` (required) - Your GitHub authentication token (under Settings > Developer settings > Personal access tokens).
208209
- `GHE_API_URL` - When using GitHub Enterprise, an absolute URL to the API.
209210
- `GHE_VERSION` - When using GitHub Enterprise, the currently installed GHE version. [Supports the following versions](https://github.com/octokit/plugin-enterprise-rest.js).
211+
-
212+
To authenticate with GitLab, the following environment variables can be defined.
213+
214+
- `GL_TOKEN` (required) - Your GitLab authentication token (under User Settings > Access Tokens).
215+
- `GL_API_URL` - An absolute URL to the API, including the version. (Default: https://gitlab.com/api/v4)
210216

211217
> NOTE: When using this option, you cannot pass [`--no-changelog`](#--no-changelog).
212218
@@ -288,7 +294,7 @@ lerna version --conventional-commits --no-changelog
288294

289295
When using `conventional-commits`, do not generate any `CHANGELOG.md` files.
290296

291-
> NOTE: When using this option, you cannot pass [`--github-release`](#--github-release).
297+
> NOTE: When using this option, you cannot pass [`--create-release`](#--create-release-type).
292298
293299
### `--no-commit-hooks`
294300

commands/version/__tests__/version-github-release.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ test("--github-release throws an error if --conventional-commits is not passed",
3333
try {
3434
await lernaVersion(cwd)("--github-release");
3535
} catch (err) {
36-
expect(err.message).toBe("To create a Github Release, you must enable --conventional-commits");
36+
expect(err.message).toBe("To create a release, you must enable --conventional-commits");
3737
expect(client.repos.createRelease).not.toHaveBeenCalled();
3838
}
3939

@@ -46,7 +46,7 @@ test("--github-release throws an error if --no-changelog also passed", async ()
4646
try {
4747
await lernaVersion(cwd)("--github-release", "--conventional-commits", "--no-changelog");
4848
} catch (err) {
49-
expect(err.message).toBe("To create a Github Release, you cannot pass --no-changelog");
49+
expect(err.message).toBe("To create a release, you cannot pass --no-changelog");
5050
expect(client.repos.createRelease).not.toHaveBeenCalled();
5151
}
5252

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
"use strict";
2+
3+
// local modules _must_ be explicitly mocked
4+
jest.mock("../lib/git-add");
5+
jest.mock("../lib/git-commit");
6+
jest.mock("../lib/git-push");
7+
jest.mock("../lib/git-tag");
8+
jest.mock("../lib/is-anything-committed");
9+
jest.mock("../lib/is-behind-upstream");
10+
jest.mock("../lib/remote-branch-exists");
11+
12+
// mocked modules
13+
const client = require("@lerna/gitlab-client")();
14+
const { recommendVersion } = require("@lerna/conventional-commits");
15+
16+
// helpers
17+
const initFixture = require("@lerna-test/init-fixture")(__dirname);
18+
19+
// test command
20+
const lernaVersion = require("@lerna-test/command-runner")(require("../command"));
21+
22+
test("--create-release=gitlab does not create a release if --no-push is passed", async () => {
23+
const cwd = await initFixture("independent");
24+
25+
await lernaVersion(cwd)("--create-release=gitlab", "--conventional-commits", "--no-push");
26+
27+
expect(client.repos.createRelease).not.toHaveBeenCalled();
28+
});
29+
30+
test("--create-release=gitlab throws an error if --conventional-commits is not passed", async () => {
31+
const cwd = await initFixture("independent");
32+
33+
try {
34+
await lernaVersion(cwd)("--create-release=gitlab");
35+
} catch (err) {
36+
expect(err.message).toBe("To create a release, you must enable --conventional-commits");
37+
expect(client.repos.createRelease).not.toHaveBeenCalled();
38+
}
39+
40+
expect.hasAssertions();
41+
});
42+
43+
test("--create-release=gitlab throws an error if --no-changelog also passed", async () => {
44+
const cwd = await initFixture("independent");
45+
46+
try {
47+
await lernaVersion(cwd)("--create-release=gitlab", "--conventional-commits", "--no-changelog");
48+
} catch (err) {
49+
expect(err.message).toBe("To create a release, you cannot pass --no-changelog");
50+
expect(client.repos.createRelease).not.toHaveBeenCalled();
51+
}
52+
53+
expect.hasAssertions();
54+
});
55+
56+
test("--create-release=gitlab marks a version as a pre-release if it contains a valid part", async () => {
57+
const cwd = await initFixture("normal");
58+
59+
recommendVersion.mockResolvedValueOnce("2.0.0-alpha.1");
60+
61+
await lernaVersion(cwd)("--create-release=gitlab", "--conventional-commits");
62+
63+
expect(client.repos.createRelease).toHaveBeenCalledTimes(1);
64+
expect(client.repos.createRelease).toHaveBeenCalledWith({
65+
owner: "lerna",
66+
repo: "lerna",
67+
tag_name: "v2.0.0-alpha.1",
68+
name: "v2.0.0-alpha.1",
69+
body: "normal",
70+
draft: false,
71+
prerelease: true,
72+
});
73+
});
74+
75+
test("--create-release=gitlab creates a release for every independent version", async () => {
76+
const cwd = await initFixture("independent");
77+
const versionBumps = new Map([
78+
["package-1", "1.0.1"],
79+
["package-2", "2.1.0"],
80+
["package-3", "4.0.0"],
81+
["package-4", "4.1.0"],
82+
["package-5", "5.0.1"],
83+
]);
84+
85+
versionBumps.forEach(bump => recommendVersion.mockResolvedValueOnce(bump));
86+
87+
await lernaVersion(cwd)("--create-release=gitlab", "--conventional-commits");
88+
89+
expect(client.repos.createRelease).toHaveBeenCalledTimes(5);
90+
versionBumps.forEach((version, name) => {
91+
expect(client.repos.createRelease).toHaveBeenCalledWith({
92+
owner: "lerna",
93+
repo: "lerna",
94+
tag_name: `${name}@${version}`,
95+
name: `${name}@${version}`,
96+
body: `${name} - ${version}`,
97+
draft: false,
98+
prerelease: false,
99+
});
100+
});
101+
});
102+
103+
test("--create-release=gitlab creates a single fixed release", async () => {
104+
const cwd = await initFixture("normal");
105+
106+
recommendVersion.mockResolvedValueOnce("1.1.0");
107+
108+
await lernaVersion(cwd)("--create-release=gitlab", "--conventional-commits");
109+
110+
expect(client.repos.createRelease).toHaveBeenCalledTimes(1);
111+
expect(client.repos.createRelease).toHaveBeenCalledWith({
112+
owner: "lerna",
113+
repo: "lerna",
114+
tag_name: "v1.1.0",
115+
name: "v1.1.0",
116+
body: "normal",
117+
draft: false,
118+
prerelease: false,
119+
});
120+
});

commands/version/command.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ exports.builder = (yargs, composed) => {
4040
requiresArg: true,
4141
defaultDescription: "origin",
4242
},
43-
"github-release": {
44-
describe: "Create an official GitHub release for every version.",
45-
type: "boolean",
43+
"create-release": {
44+
describe: "Create an official GitHub or GitLab release for every version.",
45+
type: "string",
46+
choices: ["gitlab", "github"],
4647
},
4748
"ignore-changes": {
4849
describe: [
@@ -171,6 +172,11 @@ exports.builder = (yargs, composed) => {
171172
hidden: true,
172173
type: "boolean",
173174
})
175+
.option("github-release", {
176+
// TODO: remove in next major release
177+
hidden: true,
178+
type: "boolean",
179+
})
174180
.check(argv => {
175181
/* eslint-disable no-param-reassign */
176182
if (argv.ignore) {
@@ -201,6 +207,12 @@ exports.builder = (yargs, composed) => {
201207
delete argv["skip-git"];
202208
log.warn("deprecated", "--skip-git has been replaced by --no-git-tag-version --no-push");
203209
}
210+
211+
if (argv.githubRelease) {
212+
argv.createRelease = "github";
213+
delete argv.githubRelease;
214+
log.warn("deprecated", "--release has been replaced by --create-release=github");
215+
}
204216
/* eslint-enable no-param-reassign */
205217

206218
return argv;

commands/version/index.js

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ const collectUpdates = require("@lerna/collect-updates");
1919
const { createRunner } = require("@lerna/run-lifecycle");
2020
const runTopologically = require("@lerna/run-topologically");
2121
const ValidationError = require("@lerna/validation-error");
22-
const { createGitHubClient, parseGitRepo } = require("@lerna/github-client");
2322
const prereleaseIdFromVersion = require("@lerna/prerelease-id-from-version");
2423

2524
const getCurrentBranch = require("./lib/get-current-branch");
@@ -32,6 +31,7 @@ const remoteBranchExists = require("./lib/remote-branch-exists");
3231
const isBreakingChange = require("./lib/is-breaking-change");
3332
const isAnythingCommitted = require("./lib/is-anything-committed");
3433
const makePromptVersion = require("./lib/prompt-version");
34+
const createRelease = require("./lib/create-release");
3535

3636
const { collectPackages, getPackagesForOption } = collectUpdates;
3737

@@ -75,18 +75,15 @@ class VersionCommand extends Command {
7575
this.pushToRemote = gitTagVersion && amend !== true && push;
7676
// never automatically push to remote when amending a commit
7777

78-
this.createReleases = this.pushToRemote && this.options.githubRelease;
78+
this.createRelease = this.pushToRemote && this.options.createRelease;
7979
this.releaseNotes = [];
8080

81-
if (this.createReleases && this.options.conventionalCommits !== true) {
82-
throw new ValidationError(
83-
"ERELEASE",
84-
"To create a Github Release, you must enable --conventional-commits"
85-
);
81+
if (this.createRelease && this.options.conventionalCommits !== true) {
82+
throw new ValidationError("ERELEASE", "To create a release, you must enable --conventional-commits");
8683
}
8784

88-
if (this.createReleases && this.options.changelog === false) {
89-
throw new ValidationError("ERELEASE", "To create a Github Release, you cannot pass --no-changelog");
85+
if (this.createRelease && this.options.changelog === false) {
86+
throw new ValidationError("ERELEASE", "To create a release, you cannot pass --no-changelog");
9087
}
9188

9289
this.gitOpts = {
@@ -269,10 +266,17 @@ class VersionCommand extends Command {
269266
this.logger.info("execute", "Skipping git push");
270267
}
271268

272-
if (this.createReleases) {
273-
tasks.push(() => this.createGitHubReleases());
269+
if (this.createRelease) {
270+
this.logger.info("execute", "Creating releases...");
271+
tasks.push(() =>
272+
createRelease(
273+
this.options.createRelease,
274+
{ tags: this.tags, releaseNotes: this.releaseNotes },
275+
{ gitRemote: this.options.gitRemote, execOpts: this.execOpts }
276+
)
277+
);
274278
} else {
275-
this.logger.info("execute", "Skipping GitHub releases");
279+
this.logger.info("execute", "Skipping releases");
276280
}
277281

278282
return pWaterfall(tasks).then(() => {
@@ -652,36 +656,6 @@ class VersionCommand extends Command {
652656

653657
return gitPush(this.gitRemote, this.currentBranch, this.execOpts);
654658
}
655-
656-
createGitHubReleases() {
657-
this.logger.info("github", "Creating GitHub releases...");
658-
659-
const client = createGitHubClient();
660-
const repo = parseGitRepo(this.options.gitRemote, this.execOpts);
661-
662-
return Promise.all(
663-
this.releaseNotes.map(({ notes, name }) => {
664-
const tag = name === "fixed" ? this.tags[0] : this.tags.find(t => t.startsWith(`${name}@`));
665-
666-
/* istanbul ignore if */
667-
if (!tag) {
668-
return Promise.resolve();
669-
}
670-
671-
const prereleaseParts = semver.prerelease(tag.replace(`${name}@`, "")) || [];
672-
673-
return client.repos.createRelease({
674-
owner: repo.owner,
675-
repo: repo.name,
676-
tag_name: tag,
677-
name: tag,
678-
body: notes,
679-
draft: false,
680-
prerelease: prereleaseParts.length > 0,
681-
});
682-
})
683-
);
684-
}
685659
}
686660

687661
module.exports.VersionCommand = VersionCommand;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"use strict";
2+
3+
const semver = require("semver");
4+
5+
const createGitLabClient = require("@lerna/gitlab-client");
6+
const { createGitHubClient, parseGitRepo } = require("@lerna/github-client");
7+
const ValidationError = require("@lerna/validation-error");
8+
9+
module.exports = createRelease;
10+
11+
function createClient(type) {
12+
switch (type) {
13+
case "gitlab":
14+
return createGitLabClient();
15+
case "github":
16+
return createGitHubClient();
17+
default:
18+
throw new ValidationError("ERELEASE", "Invalid release client type");
19+
}
20+
}
21+
22+
function createRelease(type, { tags, releaseNotes }, { gitRemote, execOpts }) {
23+
const repo = parseGitRepo(gitRemote, execOpts);
24+
const client = createClient(type);
25+
26+
return Promise.all(
27+
releaseNotes.map(({ notes, name }) => {
28+
const tag = name === "fixed" ? tags[0] : tags.find(t => t.startsWith(`${name}@`));
29+
30+
/* istanbul ignore if */
31+
if (!tag) {
32+
return Promise.resolve();
33+
}
34+
35+
const prereleaseParts = semver.prerelease(tag.replace(`${name}@`, "")) || [];
36+
37+
return client.repos.createRelease({
38+
owner: repo.owner,
39+
repo: repo.name,
40+
tag_name: tag,
41+
name: tag,
42+
body: notes,
43+
draft: false,
44+
prerelease: prereleaseParts.length > 0,
45+
});
46+
})
47+
);
48+
}

commands/version/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"@lerna/command": "file:../../core/command",
4141
"@lerna/conventional-commits": "file:../../core/conventional-commits",
4242
"@lerna/github-client": "file:../../utils/github-client",
43+
"@lerna/gitlab-client": "file:../../utils/gitlab-client",
4344
"@lerna/output": "file:../../utils/output",
4445
"@lerna/prerelease-id-from-version": "file:../../utils/prerelease-id-from-version",
4546
"@lerna/prompt": "file:../../core/prompt",

core/project/__fixtures__/extends-deprecated/lerna.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"commands": {
44
"bootstrap": {
55
"hoist": true
6+
},
7+
"version": {
8+
"githubRelease": true
69
}
710
},
811
"npmTag": "next",

0 commit comments

Comments
 (0)