diff --git a/cli.js b/cli.js index ecdd5d43e2..29370f62b8 100755 --- a/cli.js +++ b/cli.js @@ -37,6 +37,7 @@ Usage: .option("debug", { describe: "Output debugging information", type: "boolean", group: "Options" }) .option("d", { alias: "dry-run", describe: "Skip publishing", type: "boolean", group: "Options" }) .option("h", { alias: "help", group: "Options" }) + .option("prerelease-build-format", {describe: "Prerelease build number format", type: "string", group: "Options"}) .strict(false) .exitProcess(false); diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md index b898a3ea83..7854bc1207 100644 --- a/docs/usage/configuration.md +++ b/docs/usage/configuration.md @@ -115,6 +115,25 @@ The [Git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) format used by **Note**: The `tagFormat` must contain the `version` variable exactly once and compile to a [valid Git reference](https://git-scm.com/docs/git-check-ref-format#_description). +### prereleaseBuildFormat + +Type: `String`
+Default: `undefined`
+CLI arguments: `--prerelease-build-format` + +The format used by **semantic-release** when appending prerelease build information after the auto increment prerelease number. If left unspecified, no build information will be appended, otherwise the parsed value of this formatter will be appended after a `+` in the resulting version, so there is no need to add one yourself. Using this option is useful if you want extra insight into which version of the project has been released, or if you need to avoid tag conflicts caused by more complex git workflows, like ones that utilize history rewrites. The build number is generated with [Lodash template](https://lodash.com/docs#template) and will be compiled with the following variables. + +- `commit` The current commit hash. Can use `commit.substr(0,7)` to use the short hash. +- `build` The current CI build number. This is useful when you need atomically increasing build numbers. + +**Examples**: + +| Previous Version | Prerelease Build Format | Branch | Commit Sha | Result | +| ---------------------- | ----------------------- | ------- | ---------- | ----------------------- | +| `1.1.3` | `${commit}` | `alpha` | `1a2b3c4` | `1.2.3-alpha.1+1a2b3c4` | +| `1.2.3-alpha.1+abcdef` | `${commit}` | `alpha` | `4d5e6f7` | `1.2.3-alpha.2+4d5e6f7` | +| `1.1.3` | | `alpha` | `1a2b3c4` | `1.2.3-alpha.1` | + ### plugins Type: `Array`
diff --git a/index.js b/index.js index 19c9f7057e..0b7be4e00f 100644 --- a/index.js +++ b/index.js @@ -182,6 +182,7 @@ async function run(context, plugins) { } context.nextRelease = nextRelease; + context.prereleaseBuildFormat = options.prereleaseBuildFormat; nextRelease.version = getNextVersion(context); nextRelease.gitTag = makeTag(options.tagFormat, nextRelease.version); nextRelease.name = nextRelease.gitTag; diff --git a/lib/get-next-version.js b/lib/get-next-version.js index c657f10d9e..9a66a3072c 100644 --- a/lib/get-next-version.js +++ b/lib/get-next-version.js @@ -1,8 +1,16 @@ import semver from "semver"; +const {template} = require('lodash'); import { FIRST_RELEASE, FIRSTPRERELEASE } from "./definitions/constants.js"; import { getLatestVersion, highest, isSameChannel, tagsToVersions } from "./utils.js"; -export default ({ branch, nextRelease: { type, channel }, lastRelease, logger }) => { +module.exports = ({ + branch, + envCi: {commit, build}, + nextRelease: {type, channel}, + lastRelease, + logger, + prereleaseBuildFormat +}) => { let version; if (lastRelease.version) { const { major, minor, patch } = semver.parse(lastRelease.version); @@ -24,12 +32,16 @@ export default ({ branch, nextRelease: { type, channel }, lastRelease, logger }) } else { version = semver.inc(lastRelease.version, type); } - - logger.log("The next release version is %s", version); } else { version = branch.type === "prerelease" ? `${FIRST_RELEASE}-${branch.prerelease}.${FIRSTPRERELEASE}` : FIRST_RELEASE; - logger.log(`There is no previous release, the next release version is ${version}`); + logger.log("There is no previous release"); + } + + if (branch.type === "prerelease" && prereleaseBuildFormat) { + version += `+${template(prereleaseBuildFormat)({build, commit})}`; } + logger.log('The next release version is %s', version); + return version; }; diff --git a/test/get-next-version.test.js b/test/get-next-version.test.js index a50526c783..11514ff797 100644 --- a/test/get-next-version.test.js +++ b/test/get-next-version.test.js @@ -12,6 +12,7 @@ test("Increase version for patch release", (t) => { t.is( getNextVersion({ branch: { name: "master", type: "release", tags: [{ gitTag: "v1.0.0", version: "1.0.0", channels: [null] }] }, + envCi: {}, nextRelease: { type: "patch" }, lastRelease: { version: "1.0.0", channels: [null] }, logger: t.context.logger, @@ -24,6 +25,7 @@ test("Increase version for minor release", (t) => { t.is( getNextVersion({ branch: { name: "master", type: "release", tags: [{ gitTag: "v1.0.0", version: "1.0.0", channels: [null] }] }, + envCi: {}, nextRelease: { type: "minor" }, lastRelease: { version: "1.0.0", channels: [null] }, logger: t.context.logger, @@ -36,6 +38,7 @@ test("Increase version for major release", (t) => { t.is( getNextVersion({ branch: { name: "master", type: "release", tags: [{ gitTag: "v1.0.0", version: "1.0.0", channels: [null] }] }, + envCi: {}, nextRelease: { type: "major" }, lastRelease: { version: "1.0.0", channels: [null] }, logger: t.context.logger, @@ -48,6 +51,7 @@ test("Return 1.0.0 if there is no previous release", (t) => { t.is( getNextVersion({ branch: { name: "master", type: "release", tags: [] }, + envCi: {}, nextRelease: { type: "minor" }, lastRelease: {}, logger: t.context.logger, @@ -65,6 +69,7 @@ test("Increase version for patch release on prerelease branch", (t) => { prerelease: "beta", tags: [{ gitTag: "v1.0.0", version: "1.0.0", channels: [null] }], }, + envCi: {}, nextRelease: { type: "patch", channel: "beta" }, lastRelease: { version: "1.0.0", channels: [null] }, logger: t.context.logger, @@ -83,6 +88,7 @@ test("Increase version for patch release on prerelease branch", (t) => { { gitTag: "v1.0.1-beta.1", version: "1.0.1-beta.1", channels: ["beta"] }, ], }, + envCi: {}, nextRelease: { type: "patch", channel: "beta" }, lastRelease: { version: "1.0.1-beta.1", channels: ["beta"] }, logger: t.context.logger, @@ -98,6 +104,7 @@ test("Increase version for patch release on prerelease branch", (t) => { prerelease: "alpha", tags: [{ gitTag: "v1.0.1-beta.1", version: "1.0.1-beta.1", channels: ["beta"] }], }, + envCi: {}, nextRelease: { type: "patch", channel: "alpha" }, lastRelease: { version: "1.0.1-beta.1", channels: ["beta"] }, logger: t.context.logger, @@ -115,6 +122,7 @@ test("Increase version for minor release on prerelease branch", (t) => { prerelease: "beta", tags: [{ gitTag: "v1.0.0", version: "1.0.0", channels: [null] }], }, + envCi: {}, nextRelease: { type: "minor", channel: "beta" }, lastRelease: { version: "1.0.0", channels: [null] }, logger: t.context.logger, @@ -133,6 +141,7 @@ test("Increase version for minor release on prerelease branch", (t) => { { gitTag: "v1.1.0-beta.1", version: "1.1.0-beta.1", channels: ["beta"] }, ], }, + envCi: {}, nextRelease: { type: "minor", channel: "beta" }, lastRelease: { version: "1.1.0-beta.1", channels: ["beta"] }, logger: t.context.logger, @@ -148,6 +157,7 @@ test("Increase version for minor release on prerelease branch", (t) => { prerelease: "alpha", tags: [{ gitTag: "v1.1.0-beta.1", version: "1.1.0-beta.1", channels: ["beta"] }], }, + envCi: {}, nextRelease: { type: "minor", channel: "alpha" }, lastRelease: { version: "1.1.0-beta.1", channels: ["beta"] }, logger: t.context.logger, @@ -165,6 +175,7 @@ test("Increase version for major release on prerelease branch", (t) => { prerelease: "beta", tags: [{ gitTag: "v1.0.0", version: "1.0.0", channels: [null] }], }, + envCi: {}, nextRelease: { type: "major", channel: "beta" }, lastRelease: { version: "1.0.0", channels: [null] }, logger: t.context.logger, @@ -183,6 +194,7 @@ test("Increase version for major release on prerelease branch", (t) => { { gitTag: "v2.0.0-beta.1", version: "2.0.0-beta.1", channels: ["beta"] }, ], }, + envCi: {}, nextRelease: { type: "major", channel: "beta" }, lastRelease: { version: "2.0.0-beta.1", channels: ["beta"] }, logger: t.context.logger, @@ -198,6 +210,7 @@ test("Increase version for major release on prerelease branch", (t) => { prerelease: "alpha", tags: [{ gitTag: "v2.0.0-beta.1", version: "2.0.0-beta.1", channels: ["beta"] }], }, + envCi: {}, nextRelease: { type: "major", channel: "alpha" }, lastRelease: { version: "2.0.0-beta.1", channels: ["beta"] }, logger: t.context.logger, @@ -210,6 +223,7 @@ test("Return 1.0.0 if there is no previous release on prerelease branch", (t) => t.is( getNextVersion({ branch: { name: "beta", type: "prerelease", prerelease: "beta", tags: [] }, + envCi: {}, nextRelease: { type: "minor" }, lastRelease: {}, logger: t.context.logger, @@ -231,6 +245,7 @@ test("Increase version for release on prerelease branch after previous commits w { gitTag: "v1.1.0-beta.1", version: "1.1.0-beta.1", channels: [null, "beta"] }, ], }, + envCi: {}, nextRelease: { type: "minor" }, lastRelease: { version: "1.1.0", channels: [null] }, logger: t.context.logger, @@ -251,6 +266,7 @@ test("Increase version for release on prerelease branch based on highest commit { gitTag: "v1.1.0-beta.1", version: "1.1.0-beta.1", channels: [null, "beta"] }, ], }, + envCi: {}, nextRelease: { type: "major" }, lastRelease: { version: "v1.1.0-beta.1", channels: [null] }, logger: t.context.logger, @@ -268,6 +284,7 @@ test("Increase version for release on prerelease branch when there is no regular prerelease: "beta", tags: [{ gitTag: "v1.0.0-beta.1", version: "1.0.0-beta.1", channels: ["beta"] }], }, + envCi: {}, nextRelease: { type: "minor", channel: "beta" }, lastRelease: { version: "v1.0.0-beta.1", channels: ["beta"] }, logger: t.context.logger, @@ -289,6 +306,7 @@ test("Increase patch when previous version shares HEAD with other releases", (t) { gitTag: "v1.0.0-alpha.1", version: "1.0.0-alpha.1", channels: ["alpha", "beta"] }, ], }, + envCi: {}, nextRelease: { type: "patch", channel: "alpha" }, lastRelease: { version: "v1.0.0-alpha.1", channels: ["alpha", "beta"] }, logger: t.context.logger, @@ -296,3 +314,60 @@ test("Increase patch when previous version shares HEAD with other releases", (t) "1.0.0-alpha.2" ); }); + +test("Append commit build number to prerelease version", (t) => { + t.is( + getNextVersion({ + branch: { + name: "beta", + type: "prerelease", + prerelease: "beta", + tags: [{gitTag: "v1.0.0", version: "1.0.0", channels: [null]}], + }, + envCi: {commit: "1a2b3c4", build: "1234"}, + nextRelease: {type: "minor"}, + lastRelease: {version: "1.0.0", channels: [null]}, + logger: t.context.logger, + prereleaseBuildFormat: `\${commit}`, + }), + "1.1.0-beta.1+1a2b3c4" + ); +}); + +test("Append CI build number to prerelease version", (t) => { + t.is( + getNextVersion({ + branch: { + name: "beta", + type: "prerelease", + prerelease: "beta", + tags: [{gitTag: "v1.0.0", version: "1.0.0", channels: [null]}], + }, + envCi: {commit: "1a2b3c4", build: "1234"}, + nextRelease: {type: "minor"}, + lastRelease: {version: "1.0.0", channels: [null]}, + logger: t.context.logger, + prereleaseBuildFormat: `\${build}`, + }), + "1.1.0-beta.1+1234" + ); +}); + +test("Append new commit build number to next prerelease version", (t) => { + t.is( + getNextVersion({ + branch: { + name: "beta", + type: "prerelease", + prerelease: "beta", + tags: [{gitTag: "v1.1.0-beta.1+1a2b3c4", version: "1.1.0-beta.1+1a2b3c4", channels: ["beta"]}], + }, + envCi: {commit: "4d5e6f7", build: "1234"}, + nextRelease: {type: "minor", channel: "beta"}, + lastRelease: {version: "v1.1.0-beta.1+1a2b3c4", channels: ["beta"]}, + logger: t.context.logger, + prereleaseBuildFormat: `\${commit}`, + }), + "1.1.0-beta.2+4d5e6f7" + ); +}); diff --git a/test/integration.test.js b/test/integration.test.js index 16915c9625..7fe1b23964 100644 --- a/test/integration.test.js +++ b/test/integration.test.js @@ -398,7 +398,8 @@ test("Dry-run", async (t) => { await gitCommits(["feat: Initial commit"], { cwd }); t.log("$ semantic-release -d"); const { stdout, exitCode } = await execa(cli, ["-d"], { env, cwd, extendEnv: false }); - t.regex(stdout, new RegExp(`There is no previous release, the next release version is ${version}`)); + t.regex(stdout, /There is no previous release/); + t.regex(stdout, new RegExp(`The next release version is ${version}`)); t.regex(stdout, new RegExp(`Release note for version ${version}`)); t.regex(stdout, /Initial commit/); t.is(exitCode, 0);