diff --git a/.gitignore b/.gitignore index 9ae5b4665ca..f6950b31b50 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ coverage/ .nyc_output/ .eslintcache .env +yarn.lock diff --git a/commands/version/README.md b/commands/version/README.md index 2869048052e..32e0778d739 100644 --- a/commands/version/README.md +++ b/commands/version/README.md @@ -58,6 +58,7 @@ Running `lerna version --conventional-commits` without the above flags will rele - [`--create-release `](#--create-release-type) - [`--exact`](#--exact) - [`--force-publish`](#--force-publish) + - [`--git-tag-command `](#--git-tag-command-cmd) - [`--git-remote `](#--git-remote-name) - [`--ignore-changes`](#--ignore-changes) - [`--ignore-scripts`](#--ignore-scripts) @@ -246,6 +247,26 @@ When run with this flag, `lerna version` will force publish the specified packag > This will skip the `lerna changed` check for changed packages and forces a package that didn't have a `git diff` change to be updated. +### `--git-tag-command ` + +Allows users to specify a wrapper command in CD/CI pipelines that have no direct write access. + +```sh +lerna version --git-tag-command "git gh-tag %s -m %s" +``` + +This can also be configured in `lerna.json`. + +```json +{ + "command": { + "version": { + "gitTagCommand": "git gh-tag %s -m %s" + } + } +} +``` + ### `--git-remote ` ```sh diff --git a/commands/version/__tests__/git-tag.test.js b/commands/version/__tests__/git-tag.test.js index ea1474fe92f..e550e2f503a 100644 --- a/commands/version/__tests__/git-tag.test.js +++ b/commands/version/__tests__/git-tag.test.js @@ -34,4 +34,13 @@ describe("gitTag", () => { expect(childProcess.exec).toHaveBeenLastCalledWith("git", ["tag", tag, "-m", tag, "--force"], opts); }); + + it("creates an annotated git tag using the wrapper arguments", async () => { + const tag = "v1.2.4"; + const opts = { cwd: "default" }; + + await gitTag(tag, {}, opts, "git-wrapper gh-tag %s -m %s"); + + expect(mockExec).toHaveBeenLastCalledWith("git-wrapper", ["gh-tag", tag, "-m", tag], opts); + }); }); diff --git a/commands/version/command.js b/commands/version/command.js index d82bd24c856..98f18e47a8e 100644 --- a/commands/version/command.js +++ b/commands/version/command.js @@ -160,6 +160,11 @@ exports.builder = (yargs, composed) => { requiresArg: true, defaultDescription: "v", }, + "git-tag-command": { + describe: + "Allows users to specify a wrapper command in CD/CI pipelines that have no direct write access.", + type: "string", + }, y: { describe: "Skip all confirmation prompts.", alias: "yes", diff --git a/commands/version/index.js b/commands/version/index.js index 3fcea7cf5d2..038d820af62 100644 --- a/commands/version/index.js +++ b/commands/version/index.js @@ -655,7 +655,7 @@ class VersionCommand extends Command { return Promise.resolve() .then(() => gitCommit(message, this.gitOpts, this.execOpts)) - .then(() => Promise.all(tags.map((tag) => gitTag(tag, this.gitOpts, this.execOpts)))) + .then(() => Promise.all(tags.map((tag) => gitTag(tag, this.gitOpts, this.execOpts, this.options.gitTagCommand)))) .then(() => tags); } @@ -668,7 +668,7 @@ class VersionCommand extends Command { return Promise.resolve() .then(() => gitCommit(message, this.gitOpts, this.execOpts)) - .then(() => gitTag(tag, this.gitOpts, this.execOpts)) + .then(() => gitTag(tag, this.gitOpts, this.execOpts, this.options.gitTagCommand)) .then(() => [tag]); } diff --git a/commands/version/lib/git-tag.js b/commands/version/lib/git-tag.js index 6686c686b8f..b70ae2dc49e 100644 --- a/commands/version/lib/git-tag.js +++ b/commands/version/lib/git-tag.js @@ -10,19 +10,21 @@ module.exports.gitTag = gitTag; * @param {{ forceGitTag: boolean; signGitTag: boolean; }} gitOpts * @param {import("@lerna/child-process").ExecOpts} opts */ -function gitTag(tag, { forceGitTag, signGitTag }, opts) { - log.silly("gitTag", tag); +function gitTag(tag, { forceGitTag, signGitTag }, opts, command = "git tag %s -m %s") { + log.silly("gitTag", tag, command); - const args = ["tag", tag, "-m", tag]; + const [cmd, ...args] = command.split(" "); + + const interpolatedArgs = args.map(arg => arg.replace(/%s/, tag)); if (forceGitTag) { - args.push("--force"); + interpolatedArgs.push("--force"); } if (signGitTag) { - args.push("--sign"); + interpolatedArgs.push("--sign"); } - log.verbose("git", args); - return childProcess.exec("git", args, opts); + log.verbose(cmd, interpolatedArgs); + return childProcess.exec(cmd, interpolatedArgs, opts); }