diff --git a/__snapshots__/cli.js b/__snapshots__/cli.js index ceb474cd1..17886a2e3 100644 --- a/__snapshots__/cli.js +++ b/__snapshots__/cli.js @@ -220,6 +220,9 @@ Options: the first major release [boolean] [default: false] --prerelease-type type of the prerelease, e.g., alpha [string] + --prerelease-initial-number starting number to append to the first + generated prerelease, e.g., 1 results in a + first prerelease of 1.0.0-alpha.1 [number] --extra-files extra files for the strategy to consider [string] --version-file path to version file to update, e.g., diff --git a/docs/cli.md b/docs/cli.md index f714d6d22..65bacb171 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -52,6 +52,7 @@ Extra options: | `--bump-minor-pre-major` | `boolean` | Configuration option for the versioning strategy. If set, will bump the minor version for breaking changes for versions < 1.0.0 | | `--bump-patch-for-minor-pre-major` | `boolean` | Configuration option for the versioning strategy. If set, will bump the patch version for features for versions < 1.0.0 | | `--prerelease-type` | `string` | Configuration option for the prerelease versioning strategy. If prerelease strategy used and type set, will set the prerelease part of the version to the provided value in case prerelease part is not present. | +| `--prerelease-initial-number` | `number` | Configuration option for the prerelease versioning strategy. Sets the starting number appended to the first generated prerelease, e.g. `1` results in a first prerelease of `1.0.0-alpha.1` instead of `1.0.0-alpha`. Must be a non-negative integer. | | `--draft` | `boolean` | If set, create releases as drafts | | `--prerelease` | `boolean` | If set, create releases that are pre-major or pre-release version marked as pre-release on Github | | `--force-tag-creation` | `boolean` | Force the creation of a Git tag for the release. Useful when `--draft` is enabled, because GitHub does not create a Git tag for draft releases until they are published. This causes release-please to fail to find the previous release, potentially generating incorrect changelogs. Setting this option ensures the tag is created immediately. | @@ -107,6 +108,7 @@ need to specify your release options: | `--bump-minor-pre-major` | boolean | Configuration option for the versioning strategy. If set, will bump the minor version for breaking changes for versions < 1.0.0 | | `--bump-patch-for-minor-pre-major` | boolean | Configuration option for the versioning strategy. If set, will bump the patch version for features for versions < 1.0.0 | | `--prerelease-type` | `string` | Configuration option for the prerelease versioning strategy. If prerelease strategy used and type set, will set the prerelease part of the version to the provided value in case prerelease part is not present. | +| `--prerelease-initial-number` | `number` | Configuration option for the prerelease versioning strategy. Sets the starting number appended to the first generated prerelease, e.g. `1` results in a first prerelease of `1.0.0-alpha.1` instead of `1.0.0-alpha`. Must be a non-negative integer. | | `--draft-pull-request` | boolean | If set, create pull requests as drafts | | `--label` | string | Comma-separated list of labels to apply to the release pull requests. Defaults to `autorelease: pending` |`autorelease: tagged` | | `--changelog-path` | `string` | Override the path to the managed CHANGELOG. Defaults to `CHANGELOG.md` | diff --git a/docs/manifest-releaser.md b/docs/manifest-releaser.md index 62221a474..f67c4e80b 100644 --- a/docs/manifest-releaser.md +++ b/docs/manifest-releaser.md @@ -181,6 +181,13 @@ defaults (those are documented in comments) // setting the type of prerelease in case of prerelease strategy "prerelease-type": "beta", + // starting number appended to the first generated prerelease when using + // the "prerelease" versioning strategy. For example, setting this to `1` + // results in a first prerelease of `1.0.0-beta.1` instead of `1.0.0-beta`. + // Must be a non-negative integer. Absence preserves the current behavior of + // omitting a number from the first prerelease. + "prerelease-initial-number": 1, + // set default conventional commit => changelog sections mapping/appearance. // absence defaults to https://git.io/JqCZL "changelog-sections": [...], diff --git a/schemas/config.json b/schemas/config.json index 2a751312b..0415cfad7 100644 --- a/schemas/config.json +++ b/schemas/config.json @@ -24,6 +24,11 @@ "description": "Configuration option for the prerelease versioning strategy. If prerelease strategy used and type set, will set the prerelease part of the version to the provided value in case prerelease part is not present.", "type": "string" }, + "prerelease-initial-number": { + "description": "Configuration option for the prerelease versioning strategy. Sets the starting number appended to the first generated prerelease, e.g. `1` results in a first prerelease of `1.0.0-alpha.1` instead of `1.0.0-alpha`. Must be a non-negative integer.", + "type": "integer", + "minimum": 0 + }, "versioning": { "description": "Versioning strategy. Defaults to `default`", "type": "string" diff --git a/src/bin/release-please.ts b/src/bin/release-please.ts index d1eaf4d70..f683711b6 100644 --- a/src/bin/release-please.ts +++ b/src/bin/release-please.ts @@ -14,25 +14,25 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {coerceOption} from '../util/coerce-option'; +import {createPatch} from 'diff'; import * as yargs from 'yargs'; -import {GitHub} from '../github'; -import {GH_API_URL, GH_GRAPHQL_URL} from '../github-api'; -import {Manifest, ManifestOptions, ROOT_PROJECT_PATH} from '../manifest'; +import {Bootstrapper} from '../bootstrapper'; import {ChangelogSection, buildChangelogSections} from '../changelog-notes'; -import {logger, setLogger, CheckpointLogger} from '../util/logger'; import { - getReleaserTypes, + ChangelogNotesType, ReleaseType, VersioningStrategyType, - getVersioningStrategyTypes, - ChangelogNotesType, getChangelogTypes, + getReleaserTypes, + getVersioningStrategyTypes, } from '../factory'; -import {Bootstrapper} from '../bootstrapper'; -import {createPatch} from 'diff'; -import {Scm} from '../scm'; +import {GitHub} from '../github'; +import {GH_API_URL, GH_GRAPHQL_URL} from '../github-api'; import {LocalGitHub} from '../local-github'; +import {Manifest, ManifestOptions, ROOT_PROJECT_PATH} from '../manifest'; +import {Scm} from '../scm'; +import {coerceOption} from '../util/coerce-option'; +import {CheckpointLogger, logger, setLogger} from '../util/logger'; // eslint-disable-next-line @typescript-eslint/no-var-requires const parseGithubRepoUrl = require('parse-github-repo-url'); @@ -70,6 +70,7 @@ interface VersioningArgs { bumpMinorPreMajor?: boolean; bumpPatchForMinorPreMajor?: boolean; prereleaseType?: string; + prereleaseInitialNumber?: number; releaseAs?: string; // only for Ruby: TODO replace with generic bootstrap option @@ -308,6 +309,12 @@ function pullRequestStrategyOptions(yargs: yargs.Argv): yargs.Argv { describe: 'type of the prerelease, e.g., alpha', type: 'string', }) + .option('prerelease-initial-number', { + describe: + 'starting number to append to the first generated prerelease, ' + + 'e.g., 1 results in a first prerelease of 1.0.0-alpha.1', + type: 'number', + }) .option('extra-files', { describe: 'extra files for the strategy to consider', type: 'string', @@ -495,6 +502,7 @@ const createReleasePullRequestCommand: yargs.CommandModule< bumpMinorPreMajor: argv.bumpMinorPreMajor, bumpPatchForMinorPreMajor: argv.bumpPatchForMinorPreMajor, prereleaseType: argv.prereleaseType, + prereleaseInitialNumber: argv.prereleaseInitialNumber, changelogPath: argv.changelogPath, changelogType: argv.changelogType, changelogHost: argv.changelogHost, @@ -764,6 +772,7 @@ const bootstrapCommand: yargs.CommandModule<{}, BootstrapArgs> = { bumpMinorPreMajor: argv.bumpMinorPreMajor, bumpPatchForMinorPreMajor: argv.bumpPatchForMinorPreMajor, prereleaseType: argv.prereleaseType, + prereleaseInitialNumber: argv.prereleaseInitialNumber, changelogPath: argv.changelogPath, changelogHost: argv.changelogHost, changelogSections: argv.changelogSections, diff --git a/src/factories/versioning-strategy-factory.ts b/src/factories/versioning-strategy-factory.ts index c2484813b..305f283d1 100644 --- a/src/factories/versioning-strategy-factory.ts +++ b/src/factories/versioning-strategy-factory.ts @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -import {VersioningStrategy} from '../versioning-strategy'; -import {DefaultVersioningStrategy} from '../versioning-strategies/default'; -import {AlwaysBumpPatch} from '../versioning-strategies/always-bump-patch'; -import {AlwaysBumpMinor} from '../versioning-strategies/always-bump-minor'; -import {AlwaysBumpMajor} from '../versioning-strategies/always-bump-major'; -import {ServicePackVersioningStrategy} from '../versioning-strategies/service-pack'; -import {Scm} from '../scm'; import {ConfigurationError} from '../errors'; +import {Scm} from '../scm'; +import {AlwaysBumpMajor} from '../versioning-strategies/always-bump-major'; +import {AlwaysBumpMinor} from '../versioning-strategies/always-bump-minor'; +import {AlwaysBumpPatch} from '../versioning-strategies/always-bump-patch'; +import {DefaultVersioningStrategy} from '../versioning-strategies/default'; import {PrereleaseVersioningStrategy} from '../versioning-strategies/prerelease'; +import {ServicePackVersioningStrategy} from '../versioning-strategies/service-pack'; +import {VersioningStrategy} from '../versioning-strategy'; export type VersioningStrategyType = string; @@ -30,6 +30,7 @@ export interface VersioningStrategyFactoryOptions { bumpPatchForMinorPreMajor?: boolean; prereleaseType?: string; prerelease?: boolean; + prereleaseInitialNumber?: number; github: Scm; } @@ -49,6 +50,17 @@ const versioningTypes: Record = { export function buildVersioningStrategy( options: VersioningStrategyFactoryOptions ): VersioningStrategy { + if ( + options.prereleaseInitialNumber !== undefined && + (!Number.isInteger(options.prereleaseInitialNumber) || + options.prereleaseInitialNumber < 0) + ) { + throw new ConfigurationError( + `Invalid prerelease-initial-number: ${options.prereleaseInitialNumber}. Must be a non-negative integer.`, + 'core', + `${options.github.repository.owner}/${options.github.repository.repo}` + ); + } const builder = versioningTypes[options.type || 'default']; if (builder) { return builder(options); diff --git a/src/factory.ts b/src/factory.ts index 8a72788de..83522a97c 100644 --- a/src/factory.ts +++ b/src/factory.ts @@ -15,8 +15,8 @@ import {ConfigurationError} from './errors'; import {buildChangelogNotes} from './factories/changelog-notes-factory'; import {buildVersioningStrategy} from './factories/versioning-strategy-factory'; -import {Scm} from './scm'; import {ReleaserConfig} from './manifest'; +import {Scm} from './scm'; import {BaseStrategyOptions} from './strategies/base'; import {Bazel} from './strategies/bazel'; import {Dart} from './strategies/dart'; @@ -24,8 +24,8 @@ import {DotnetYoshi} from './strategies/dotnet-yoshi'; import {Elixir} from './strategies/elixir'; import {Expo} from './strategies/expo'; import {Go} from './strategies/go'; -import {GoYoshi} from './strategies/go-yoshi'; import {GoLibrarian} from './strategies/go-librarian'; +import {GoYoshi} from './strategies/go-yoshi'; import {Helm} from './strategies/helm'; import {Java} from './strategies/java'; import {JavaYoshi} from './strategies/java-yoshi'; @@ -36,14 +36,14 @@ import {Node} from './strategies/node'; import {NodeLibrarian} from './strategies/node-librarian'; import {OCaml} from './strategies/ocaml'; import {PHP} from './strategies/php'; -import {PHPYoshi} from './strategies/php-yoshi'; import {PHPLibrarian} from './strategies/php-librarian'; +import {PHPYoshi} from './strategies/php-yoshi'; import {Python} from './strategies/python'; import {PythonLibrarian} from './strategies/python-librarian'; import {R} from './strategies/r'; import {Ruby} from './strategies/ruby'; -import {RubyYoshi} from './strategies/ruby-yoshi'; import {RubyLibrarian} from './strategies/ruby-librarian'; +import {RubyYoshi} from './strategies/ruby-yoshi'; import {Rust} from './strategies/rust'; import {Sfdx} from './strategies/sfdx'; import {Simple} from './strategies/simple'; @@ -135,6 +135,7 @@ export async function buildStrategy( bumpPatchForMinorPreMajor: options.bumpPatchForMinorPreMajor, prereleaseType: options.prereleaseType, prerelease: options.prerelease, + prereleaseInitialNumber: options.prereleaseInitialNumber, }); const changelogNotes = buildChangelogNotes({ type: options.changelogType || 'default', diff --git a/src/manifest.ts b/src/manifest.ts index 18343780b..f36db91b9 100644 --- a/src/manifest.ts +++ b/src/manifest.ts @@ -13,40 +13,40 @@ // limitations under the License. import {ChangelogSection} from './changelog-notes'; -import {Scm, ScmRelease, ScmTag} from './scm'; -import {Version, VersionsMap} from './version'; import {Commit, parseConventionalCommits} from './commit'; -import {PullRequest} from './pull-request'; -import {logger as defaultLogger, Logger} from './util/logger'; -import {CommitSplit} from './util/commit-split'; -import {TagName} from './util/tag-name'; -import {Repository} from './repository'; -import {BranchName} from './util/branch-name'; -import {PullRequestTitle} from './util/pull-request-title'; -import {ReleasePullRequest} from './release-pull-request'; import { + ConfigurationError, + DuplicateReleaseError, + FileNotFoundError, +} from './errors'; +import { + buildPlugin, buildStrategy, + ChangelogNotesType, ReleaseType, VersioningStrategyType, - buildPlugin, - ChangelogNotesType, } from './factory'; +import {ManifestPlugin} from './plugin'; +import {Merge, MergeOptions} from './plugins/merge'; +import {PullRequest} from './pull-request'; import {Release} from './release'; +import {ReleasePullRequest} from './release-pull-request'; +import {Repository} from './repository'; +import {Scm, ScmRelease, ScmTag} from './scm'; import {Strategy} from './strategy'; -import {MergeOptions, Merge} from './plugins/merge'; import {ReleasePleaseManifest} from './updaters/release-please-manifest'; +import {BranchName} from './util/branch-name'; +import {CommitExclude} from './util/commit-exclude'; +import {CommitSplit} from './util/commit-split'; +import {logger as defaultLogger, Logger} from './util/logger'; import { - DuplicateReleaseError, - FileNotFoundError, - ConfigurationError, -} from './errors'; -import {ManifestPlugin} from './plugin'; -import { - PullRequestOverflowHandler, FilePullRequestOverflowHandler, + PullRequestOverflowHandler, } from './util/pull-request-overflow-handler'; +import {PullRequestTitle} from './util/pull-request-title'; import {signoffCommitMessage} from './util/signoff-commit-message'; -import {CommitExclude} from './util/commit-exclude'; +import {TagName} from './util/tag-name'; +import {Version, VersionsMap} from './version'; type ExtraGenericFile = { type: 'generic'; @@ -101,6 +101,7 @@ export interface ReleaserConfig { bumpMinorPreMajor?: boolean; bumpPatchForMinorPreMajor?: boolean; prereleaseType?: string; + prereleaseInitialNumber?: number; // Strategy options releaseAs?: string; @@ -164,6 +165,7 @@ interface ReleaserConfigJson { 'bump-minor-pre-major'?: boolean; 'bump-patch-for-minor-pre-major'?: boolean; 'prerelease-type'?: string; + 'prerelease-initial-number'?: number; 'changelog-sections'?: ChangelogSection[]; 'release-as'?: string; 'skip-github-release'?: boolean; @@ -1395,6 +1397,7 @@ function extractReleaserConfig( bumpMinorPreMajor: config['bump-minor-pre-major'], bumpPatchForMinorPreMajor: config['bump-patch-for-minor-pre-major'], prereleaseType: config['prerelease-type'], + prereleaseInitialNumber: config['prerelease-initial-number'], versioning: config['versioning'], changelogSections: config['changelog-sections'], changelogPath: config['changelog-path'], @@ -1751,6 +1754,9 @@ function mergeReleaserConfig( pathConfig.bumpPatchForMinorPreMajor ?? defaultConfig.bumpPatchForMinorPreMajor, prereleaseType: pathConfig.prereleaseType ?? defaultConfig.prereleaseType, + prereleaseInitialNumber: + pathConfig.prereleaseInitialNumber ?? + defaultConfig.prereleaseInitialNumber, versioning: pathConfig.versioning ?? defaultConfig.versioning, changelogSections: pathConfig.changelogSections ?? defaultConfig.changelogSections, diff --git a/src/versioning-strategies/prerelease.ts b/src/versioning-strategies/prerelease.ts index 977cbd9dd..7473ddb2b 100644 --- a/src/versioning-strategies/prerelease.ts +++ b/src/versioning-strategies/prerelease.ts @@ -12,23 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { - DefaultVersioningStrategyOptions, - DefaultVersioningStrategy, -} from './default'; -import {Version} from '../version'; import {ConventionalCommit} from '..'; +import {Version} from '../version'; import { - VersionUpdater, CustomVersionUpdate, - MinorVersionUpdate, MajorVersionUpdate, + MinorVersionUpdate, + VersionUpdater, } from '../versioning-strategy'; +import { + DefaultVersioningStrategy, + DefaultVersioningStrategyOptions, +} from './default'; interface PrereleaseVersioningStrategyOptions extends DefaultVersioningStrategyOptions { prereleaseType?: string; prerelease?: boolean; + prereleaseInitialNumber?: number; } /** @@ -39,9 +40,25 @@ const PRERELEASE_NUMBER = /(?\d+)(?=\D*$)/; abstract class AbstractPrereleaseVersionUpdate implements VersionUpdater { protected readonly prereleaseType?: string; + protected readonly prereleaseInitialNumber?: number; - constructor(prereleaseType?: string) { + constructor(prereleaseType?: string, prereleaseInitialNumber?: number) { this.prereleaseType = prereleaseType; + this.prereleaseInitialNumber = prereleaseInitialNumber; + } + + /** + * Returns the prerelease part of the version used the first time an + * artifact transitions into a prerelease, e.g. "alpha" or, if a starting + * number is configured, "alpha.1". + * + * @returns {string | undefined} The initial prerelease value. + */ + protected initialPrerelease(): string | undefined { + if (this.prereleaseType && this.prereleaseInitialNumber !== undefined) { + return `${this.prereleaseType}.${this.prereleaseInitialNumber}`; + } + return this.prereleaseType; } /** @@ -100,7 +117,7 @@ class PrereleasePatchVersionUpdate extends AbstractPrereleaseVersionUpdate { version.major, version.minor, version.patch + 1, - this.prereleaseType, + this.initialPrerelease(), version.build ); } @@ -133,7 +150,7 @@ class PrereleaseMinorVersionUpdate extends AbstractPrereleaseVersionUpdate { version.major, version.minor + 1, 0, - this.prereleaseType, + this.initialPrerelease(), version.build ); } @@ -164,7 +181,7 @@ class PrereleaseMajorVersionUpdate extends AbstractPrereleaseVersionUpdate { version.major + 1, 0, 0, - this.prereleaseType, + this.initialPrerelease(), version.build ); } @@ -178,11 +195,13 @@ class PrereleaseMajorVersionUpdate extends AbstractPrereleaseVersionUpdate { export class PrereleaseVersioningStrategy extends DefaultVersioningStrategy { readonly prereleaseType?: string; readonly prerelease: boolean; + readonly prereleaseInitialNumber?: number; constructor(options: PrereleaseVersioningStrategyOptions = {}) { super(options); this.prereleaseType = options.prereleaseType; this.prerelease = options.prerelease === true; + this.prereleaseInitialNumber = options.prereleaseInitialNumber; } determineReleaseType( @@ -214,26 +233,31 @@ export class PrereleaseVersioningStrategy extends DefaultVersioningStrategy { if (breaking > 0) { if (version.isPreMajor && this.bumpMinorPreMajor) { bumpedVersionUpdater = new PrereleaseMinorVersionUpdate( - this.prereleaseType + this.prereleaseType, + this.prereleaseInitialNumber ); } else { bumpedVersionUpdater = new PrereleaseMajorVersionUpdate( - this.prereleaseType + this.prereleaseType, + this.prereleaseInitialNumber ); } } else if (features > 0) { if (version.isPreMajor && this.bumpPatchForMinorPreMajor) { bumpedVersionUpdater = new PrereleasePatchVersionUpdate( - this.prereleaseType + this.prereleaseType, + this.prereleaseInitialNumber ); } else { bumpedVersionUpdater = new PrereleaseMinorVersionUpdate( - this.prereleaseType + this.prereleaseType, + this.prereleaseInitialNumber ); } } else { bumpedVersionUpdater = new PrereleasePatchVersionUpdate( - this.prereleaseType + this.prereleaseType, + this.prereleaseInitialNumber ); } if (!this.prerelease) { diff --git a/test/cli.ts b/test/cli.ts index 93e03381a..4e0b99ede 100644 --- a/test/cli.ts +++ b/test/cli.ts @@ -13,18 +13,18 @@ // limitations under the License. import {expect} from 'chai'; -import {describe, it, afterEach, beforeEach} from 'mocha'; +import {afterEach, beforeEach, describe, it} from 'mocha'; import * as sinon from 'sinon'; -import {parser, handleError} from '../src/bin/release-please'; +import {ParseCallback} from 'yargs'; +import {handleError, parser} from '../src/bin/release-please'; +import {GitHub} from '../src/github'; import { - Manifest, DEFAULT_RELEASE_PLEASE_CONFIG, DEFAULT_RELEASE_PLEASE_MANIFEST, + Manifest, } from '../src/manifest'; import snapshot = require('snap-shot-it'); -import {GitHub} from '../src/github'; -import {ParseCallback} from 'yargs'; const sandbox = sinon.createSandbox(); @@ -866,6 +866,33 @@ describe('CLI', () => { sinon.assert.calledOnce(createPullRequestsStub); }); + it('handles --prerelease-initial-number', async () => { + await parser.parseAsync( + 'release-pr --repo-url=googleapis/release-please-cli --release-type=java-yoshi --prerelease-type=alpha --prerelease-initial-number=1' + ); + + sinon.assert.calledOnceWithExactly(gitHubCreateStub, { + owner: 'googleapis', + repo: 'release-please-cli', + token: undefined, + apiUrl: 'https://api.github.com', + graphqlUrl: 'https://api.github.com', + }); + sinon.assert.calledOnceWithExactly( + fromConfigStub, + fakeGitHub, + 'main', + sinon.match({ + releaseType: 'java-yoshi', + prereleaseType: 'alpha', + prereleaseInitialNumber: 1, + }), + sinon.match.any, + undefined + ); + sinon.assert.calledOnce(createPullRequestsStub); + }); + it('handles java --extra-files', async () => { await parser.parseAsync( 'release-pr --repo-url=googleapis/release-please-cli --release-type=java-yoshi --extra-files=foo/bar.java,asdf/qwer.java' diff --git a/test/factories/versioning-strategy-factory.ts b/test/factories/versioning-strategy-factory.ts index 124bef865..dd5ce328c 100644 --- a/test/factories/versioning-strategy-factory.ts +++ b/test/factories/versioning-strategy-factory.ts @@ -11,19 +11,19 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import {describe, it} from 'mocha'; import {expect} from 'chai'; +import {describe, it} from 'mocha'; import { getVersioningStrategyTypes, + GitHub, registerVersioningStrategy, VersioningStrategyType, - GitHub, } from '../../src'; -import {DefaultVersioningStrategy} from '../../src/versioning-strategies/default'; import { buildVersioningStrategy, unregisterVersioningStrategy, } from '../../src/factories/versioning-strategy-factory'; +import {DefaultVersioningStrategy} from '../../src/versioning-strategies/default'; describe('VersioningStrategyFactory', () => { const defaultTypes: VersioningStrategyType[] = [ @@ -58,6 +58,36 @@ describe('VersioningStrategyFactory', () => { }) ).to.throw(); }); + it('should build a prerelease strategy with prereleaseInitialNumber', () => { + const versioningStrategy = buildVersioningStrategy({ + github, + type: 'prerelease', + prereleaseType: 'alpha', + prereleaseInitialNumber: 1, + }); + expect(versioningStrategy).to.not.be.undefined; + }); + it('should build a prerelease strategy with prereleaseInitialNumber=0', () => { + const versioningStrategy = buildVersioningStrategy({ + github, + type: 'prerelease', + prereleaseType: 'alpha', + prereleaseInitialNumber: 0, + }); + expect(versioningStrategy).to.not.be.undefined; + }); + for (const invalidValue of [-1, 1.5]) { + it(`should throw for prereleaseInitialNumber=${invalidValue}`, () => { + expect(() => + buildVersioningStrategy({ + github, + type: 'prerelease', + prereleaseType: 'alpha', + prereleaseInitialNumber: invalidValue, + }) + ).to.throw(/prerelease-initial-number/); + }); + } }); describe('getVersioningStrategyTypes', () => { it('should return default types', () => { diff --git a/test/factory.ts b/test/factory.ts index 1de07c1e4..0cc384b68 100644 --- a/test/factory.ts +++ b/test/factory.ts @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +import {expect} from 'chai'; import {beforeEach, describe, it} from 'mocha'; +import {DefaultChangelogNotes} from '../src/changelog-notes/default'; +import {GitHubChangelogNotes} from '../src/changelog-notes/github'; import { buildStrategy, getReleaserTypes, @@ -20,19 +23,16 @@ import { unregisterReleaseType, } from '../src/factory'; import {GitHub} from '../src/github'; -import {expect} from 'chai'; +import {Java} from '../src/strategies/java'; +import {JavaYoshi} from '../src/strategies/java-yoshi'; +import {Ruby} from '../src/strategies/ruby'; import {Simple} from '../src/strategies/simple'; -import {DefaultVersioningStrategy} from '../src/versioning-strategies/default'; import {AlwaysBumpPatch} from '../src/versioning-strategies/always-bump-patch'; -import {Ruby} from '../src/strategies/ruby'; -import {JavaYoshi} from '../src/strategies/java-yoshi'; -import {JavaSnapshot} from '../src/versioning-strategies/java-snapshot'; -import {ServicePackVersioningStrategy} from '../src/versioning-strategies/service-pack'; +import {DefaultVersioningStrategy} from '../src/versioning-strategies/default'; import {DependencyManifest} from '../src/versioning-strategies/dependency-manifest'; -import {GitHubChangelogNotes} from '../src/changelog-notes/github'; -import {DefaultChangelogNotes} from '../src/changelog-notes/default'; -import {Java} from '../src/strategies/java'; +import {JavaSnapshot} from '../src/versioning-strategies/java-snapshot'; import {PrereleaseVersioningStrategy} from '../src/versioning-strategies/prerelease'; +import {ServicePackVersioningStrategy} from '../src/versioning-strategies/service-pack'; describe('factory', () => { let github: GitHub; @@ -94,6 +94,22 @@ describe('factory', () => { expect(versioningStrategy.bumpPatchForMinorPreMajor).to.be.true; expect(versioningStrategy.prereleaseType).to.eql('alpha'); }); + it('should build with prerelease initial number', async () => { + const strategy = await buildStrategy({ + github, + releaseType: 'simple', + versioning: 'prerelease', + prereleaseType: 'alpha', + prereleaseInitialNumber: 5, + }); + expect(strategy).instanceof(Simple); + expect(strategy.versioningStrategy).instanceof( + PrereleaseVersioningStrategy + ); + const versioningStrategy = + strategy.versioningStrategy as PrereleaseVersioningStrategy; + expect(versioningStrategy.prereleaseInitialNumber).to.eql(5); + }); it('should throw for unknown type', async () => { try { await buildStrategy({ diff --git a/test/versioning-strategies/prerelease.ts b/test/versioning-strategies/prerelease.ts index 50b55226a..944501b1e 100644 --- a/test/versioning-strategies/prerelease.ts +++ b/test/versioning-strategies/prerelease.ts @@ -15,8 +15,8 @@ import {describe, it} from 'mocha'; import {expect} from 'chai'; -import {PrereleaseVersioningStrategy} from '../../src/versioning-strategies/prerelease'; import {Version} from '../../src/version'; +import {PrereleaseVersioningStrategy} from '../../src/versioning-strategies/prerelease'; describe('PrereleaseVersioningStrategy', () => { describe('with breaking change', () => { @@ -484,4 +484,88 @@ describe('PrereleaseVersioningStrategy', () => { expect(newVersion.toString()).to.equal('3.1.2'); }); }); + + describe('with prerelease initial number', () => { + const breakingCommits = [ + { + sha: 'sha1', + message: 'fix!: some breaking bugfix', + files: ['path1/file1.rb'], + type: 'fix', + scope: null, + bareMessage: 'some breaking bugfix', + notes: [{title: 'BREAKING CHANGE', text: 'some breaking bugfix'}], + references: [], + breaking: true, + }, + ]; + const fixCommits = [ + { + sha: 'sha2', + message: 'fix: some bugfix', + files: ['path1/file1.rb'], + type: 'fix', + scope: null, + bareMessage: 'some bugfix', + notes: [], + references: [], + breaking: false, + }, + ]; + + it('defaults to unnumbered first prerelease when unset', async () => { + const strategy = new PrereleaseVersioningStrategy({ + prerelease: true, + prereleaseType: 'alpha', + }); + const newVersion = await strategy.bump( + Version.parse('0.5.0'), + breakingCommits + ); + expect(newVersion.toString()).to.equal('1.0.0-alpha'); + }); + + it('starts the first prerelease at the configured number', async () => { + const strategy = new PrereleaseVersioningStrategy({ + prerelease: true, + prereleaseType: 'alpha', + prereleaseInitialNumber: 1, + }); + const newVersion = await strategy.bump( + Version.parse('0.5.0'), + breakingCommits + ); + expect(newVersion.toString()).to.equal('1.0.0-alpha.1'); + }); + + it('starts the first prerelease at a configured number of 0', async () => { + const strategy = new PrereleaseVersioningStrategy({ + prerelease: true, + prereleaseType: 'alpha', + prereleaseInitialNumber: 0, + }); + const newVersion = await strategy.bump( + Version.parse('0.5.0'), + breakingCommits + ); + expect(newVersion.toString()).to.equal('1.0.0-alpha.0'); + }); + + it('continues incrementing from the configured starting number', async () => { + const strategy = new PrereleaseVersioningStrategy({ + prerelease: true, + prereleaseType: 'alpha', + prereleaseInitialNumber: 0, + }); + let version = await strategy.bump( + Version.parse('0.5.0'), + breakingCommits + ); + expect(version.toString()).to.equal('1.0.0-alpha.0'); + version = await strategy.bump(version, fixCommits); + expect(version.toString()).to.equal('1.0.0-alpha.1'); + version = await strategy.bump(version, fixCommits); + expect(version.toString()).to.equal('1.0.0-alpha.2'); + }); + }); });