diff --git a/packages/cli/src/parse-args.ts b/packages/cli/src/parse-args.ts index 7bbbefe58..98dcab926 100644 --- a/packages/cli/src/parse-args.ts +++ b/packages/cli/src/parse-args.ts @@ -216,6 +216,13 @@ const noChangelog: AutoOption = { config: true, }; +const force: AutoOption = { + name: "force", + type: Boolean, + group: "main", + config: true, +}; + interface AutoCommand extends Command { /** Options for the command */ options?: AutoOption[]; @@ -566,12 +573,9 @@ export const commands: AutoCommand[] = [ config: true, }, { - name: "force", - type: Boolean, - group: "main", + ...force, description: - "Force a canary release, even if the PR is marked to skip the release", - config: true, + "Force a next release, even if the last commit is marked to skip the release", }, quiet, ], @@ -596,6 +600,11 @@ export const commands: AutoCommand[] = [ "The message used when attaching the prerelease version to a PR", config: true, }, + { + ...force, + description: + "Force a canary release, even if the PR is marked to skip the release", + }, quiet, ], }, diff --git a/packages/core/src/__tests__/auto.test.ts b/packages/core/src/__tests__/auto.test.ts index 893de02f6..9f0ab8e47 100644 --- a/packages/core/src/__tests__/auto.test.ts +++ b/packages/core/src/__tests__/auto.test.ts @@ -1382,6 +1382,33 @@ describe("Auto", () => { await auto.next({}); expect(next).not.toHaveBeenCalled(); }); + + test("can --force release", async () => { + const auto = new Auto({ ...defaults, plugins: [] }); + + // @ts-ignore + auto.checkClean = () => Promise.resolve(true); + auto.logger = dummyLog(); + await auto.loadConfig(); + auto.remote = "origin"; + auto.git!.publish = () => Promise.resolve({ data: {} } as any); + auto.git!.getLastTagNotInBaseBranch = () => + Promise.reject(new Error("Test")); + auto.git!.getLatestTagInBranch = () => Promise.reject(new Error("Test")); + auto.git!.getLatestRelease = () => Promise.resolve("abcd"); + auto.release!.generateReleaseNotes = () => Promise.resolve("notes"); + auto.release!.getCommitsInRelease = () => + Promise.resolve([ + makeCommitFromMsg("Test Commit", { labels: ["skip-release"] }), + ]); + + const next = jest.fn(); + auto.hooks.next.tap("test", next); + jest.spyOn(auto.release!, "getCommits").mockImplementation(); + + await auto.next({ force: true }); + expect(next).toHaveBeenCalled(); + }); }); describe("shipit", () => { diff --git a/packages/core/src/auto-args.ts b/packages/core/src/auto-args.ts index c9ed1f93e..184ca4cc5 100644 --- a/packages/core/src/auto-args.ts +++ b/packages/core/src/auto-args.ts @@ -154,7 +154,12 @@ export type IShipItOptions = ILatestOptions & { onlyGraduateWithReleaseLabel?: boolean; }; -export type ICanaryOptions = QuietOption & { +interface ForceOption { + /** Always deploy even if marked as skip release */ + force?: boolean; +} + +export type ICanaryOptions = QuietOption & ForceOption & { /** Do not actually do anything */ dryRun?: boolean; /** THe PR to attach the canary to */ @@ -163,11 +168,9 @@ export type ICanaryOptions = QuietOption & { build?: number; /** The message used when attaching the canary version to a PR */ message?: string | "false"; - /** Always deploy a canary, even if the PR is marked as skip release */ - force?: boolean; }; -export type INextOptions = QuietOption & { +export type INextOptions = QuietOption & ForceOption & { /** Do not actually do anything */ dryRun?: boolean; /** The message used when attaching the prerelease version to a PR */ diff --git a/packages/core/src/auto.ts b/packages/core/src/auto.ts index 0c40adc59..0a341e82f 100644 --- a/packages/core/src/auto.ts +++ b/packages/core/src/auto.ts @@ -1395,11 +1395,15 @@ export default class Auto { const commits = await this.release.getCommitsInRelease(lastTag); const releaseNotes = await this.release.generateReleaseNotes(lastTag); const labels = commits.map((commit) => commit.labels); - const bump = calculateSemVerBump(labels, this.semVerLabels!, this.config); + let bump = calculateSemVerBump(labels, this.semVerLabels!, this.config); - if (bump === "") { - this.logger.log.info("No version published."); - return; + if (bump === SEMVER.noVersion) { + if (options.force) { + bump = SEMVER.patch; + } else {; + this.logger.log.info("No version published."); + return; + } } if (!args.quiet) { diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 108d0bbba..bdb283aaa 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -102,6 +102,7 @@ export const globalOptions = t.partial({ }), /** Options to pass to "auto next" */ next: t.partial({ + force: t.boolean, message: t.string, }), });