From 7639cd13752188eacd40fd88d175b76d33cf6c73 Mon Sep 17 00:00:00 2001 From: Harris Borawski Date: Wed, 27 Jan 2021 16:05:34 -0800 Subject: [PATCH 1/5] feat: conventional commit plugin will label an unlabeled PR --- .../__tests__/conventional-commits.test.ts | 120 ++++++++++++++++++ plugins/conventional-commits/src/index.ts | 50 ++++++++ 2 files changed, 170 insertions(+) diff --git a/plugins/conventional-commits/__tests__/conventional-commits.test.ts b/plugins/conventional-commits/__tests__/conventional-commits.test.ts index e88108425..231084ac5 100644 --- a/plugins/conventional-commits/__tests__/conventional-commits.test.ts +++ b/plugins/conventional-commits/__tests__/conventional-commits.test.ts @@ -313,3 +313,123 @@ test("should skip when not a fix/feat/breaking change commit", async () => { labels: ["skip-release"], }); }); + +test("should not semver label to pr if semver label exists", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const auto = ({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: { + getLabels: async () => ["minor"], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "fix: normal commit", + }, + }, + ]; + }, + addLabelToPr, + }, + } as unknown) as Auto; + + conventionalCommitsPlugin.apply(auto); + + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); + + expect(addLabelToPr).toHaveBeenCalledTimes(0); +}); + +test("should add correct semver label to pr - one commit", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const auto = ({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: { + getLabels: async () => [], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "fix: normal commit", + }, + }, + ]; + }, + addLabelToPr, + }, + } as unknown) as Auto; + + conventionalCommitsPlugin.apply(auto); + + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); + + expect(addLabelToPr).toHaveBeenCalledWith(1, "patch"); +}); + +test("should add correct semver label to pr - multiple commit", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const auto = ({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: { + getLabels: async () => [], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "fix: normal commit", + }, + }, + { + sha: "12345", + commit: { + message: "minor: normal commit", + }, + }, + { + sha: "123456", + commit: { + message: "minor: normal commit", + }, + }, + ]; + }, + addLabelToPr, + }, + } as unknown) as Auto; + + conventionalCommitsPlugin.apply(auto); + + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); + + expect(addLabelToPr).toHaveBeenCalledWith(1, "patch"); +}); diff --git a/plugins/conventional-commits/src/index.ts b/plugins/conventional-commits/src/index.ts index c95522d1e..76cb06055 100644 --- a/plugins/conventional-commits/src/index.ts +++ b/plugins/conventional-commits/src/index.ts @@ -135,6 +135,56 @@ export default class ConventionalCommitsPlugin implements IPlugin { return this.storedGetBump(message); }; + auto.hooks.prCheck.tapPromise(this.name, async ({ pr }) => { + if (!auto.git) { + return; + } + + const VERSIONS = [ + SEMVER.major, + SEMVER.minor, + SEMVER.patch, + "skip", + ] as const; + + const labels = await auto.git.getLabels(pr.number); + + // check if semver label is already on PR + if (labels.filter((l) => VERSIONS.includes(l as any)).length > 0) { + return; + } + + const commits = await auto.git?.getCommitsForPR(pr.number); + + const bumps = await Promise.all( + commits.map(async (commit) => { + try { + return await getBump(commit.commit.message); + } catch (error) { + auto.logger.verbose.info( + `No conventional commit message found for ${commit.sha}` + ); + } + }) + ); + + const sorted = bumps.sort((bump1, bump2) => { + if (bump1 === undefined) { + return -1; + } + + if (bump2 === undefined) { + return 1; + } + + return VERSIONS.indexOf(bump1) - VERSIONS.indexOf(bump2); + }); + + if (sorted[0] !== undefined) { + await auto.git.addLabelToPr(pr.number, sorted[0]); + } + }); + auto.hooks.onCreateLogParse.tap(this.name, (logParse) => { logParse.hooks.parseCommit.tapPromise(this.name, async (commit) => { if (!auto.semVerLabels) { From 9e021a5354392a5d96b330554f5768d10766d58c Mon Sep 17 00:00:00 2001 From: Harris Borawski Date: Thu, 28 Jan 2021 11:09:41 -0800 Subject: [PATCH 2/5] fix: use auto.semverLabels for finding/setting PR labels --- .../__tests__/conventional-commits.test.ts | 6 +-- plugins/conventional-commits/src/index.ts | 41 ++++++++----------- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/plugins/conventional-commits/__tests__/conventional-commits.test.ts b/plugins/conventional-commits/__tests__/conventional-commits.test.ts index 231084ac5..6c13c7564 100644 --- a/plugins/conventional-commits/__tests__/conventional-commits.test.ts +++ b/plugins/conventional-commits/__tests__/conventional-commits.test.ts @@ -408,13 +408,13 @@ test("should add correct semver label to pr - multiple commit", async () => { { sha: "12345", commit: { - message: "minor: normal commit", + message: "feat: normal commit", }, }, { sha: "123456", commit: { - message: "minor: normal commit", + message: "feat: normal commit", }, }, ]; @@ -431,5 +431,5 @@ test("should add correct semver label to pr - multiple commit", async () => { } as any, }); - expect(addLabelToPr).toHaveBeenCalledWith(1, "patch"); + expect(addLabelToPr).toHaveBeenCalledWith(1, "minor"); }); diff --git a/plugins/conventional-commits/src/index.ts b/plugins/conventional-commits/src/index.ts index 76cb06055..a53d3d8b4 100644 --- a/plugins/conventional-commits/src/index.ts +++ b/plugins/conventional-commits/src/index.ts @@ -73,6 +73,8 @@ const optionalOptions = t.partial({ preset: t.string, }); +const VERSIONS = [SEMVER.major, SEMVER.minor, SEMVER.patch, "skip"] as const; + export type ConventionalCommitsOptions = t.TypeOf; /** @@ -117,12 +119,6 @@ export default class ConventionalCommitsPlugin implements IPlugin { const whatBump = config.recommendedBumpOpts?.whatBump || defaultPreset.recommendedBumpOpts.whatBump; - const VERSIONS = [ - SEMVER.major, - SEMVER.minor, - SEMVER.patch, - "skip", - ] as const; const result = whatBump([conventionalCommit]); if (result?.level !== null && result?.level !== undefined) { @@ -140,17 +136,10 @@ export default class ConventionalCommitsPlugin implements IPlugin { return; } - const VERSIONS = [ - SEMVER.major, - SEMVER.minor, - SEMVER.patch, - "skip", - ] as const; - const labels = await auto.git.getLabels(pr.number); // check if semver label is already on PR - if (labels.filter((l) => VERSIONS.includes(l as any)).length > 0) { + if (labels.filter((l) => auto.semVerLabels?.get(l as any)).length > 0) { return; } @@ -168,19 +157,21 @@ export default class ConventionalCommitsPlugin implements IPlugin { }) ); - const sorted = bumps.sort((bump1, bump2) => { - if (bump1 === undefined) { - return -1; - } - - if (bump2 === undefined) { - return 1; - } + const sorted = bumps + .filter( + (bump): bump is SEMVER.major | SEMVER.minor | SEMVER.patch | "skip" => + bump !== undefined + ) + .sort((bump1, bump2) => { + return VERSIONS.indexOf(bump1) - VERSIONS.indexOf(bump2); + }); - return VERSIONS.indexOf(bump1) - VERSIONS.indexOf(bump2); - }); + if (!sorted[0]) { + return; + } - if (sorted[0] !== undefined) { + const label = auto.semVerLabels?.get(sorted[0]); + if (label) { await auto.git.addLabelToPr(pr.number, sorted[0]); } }); From 46eb6dbaf3b7ed26a044439fbc9a57d9a3de1fa3 Mon Sep 17 00:00:00 2001 From: Andrew Lisowski Date: Fri, 29 Jan 2021 09:17:13 -0800 Subject: [PATCH 3/5] fix not adding label when there is a custom release label --- .../__tests__/conventional-commits.test.ts | 57 +++++++++++++++++-- plugins/conventional-commits/src/index.ts | 3 +- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/plugins/conventional-commits/__tests__/conventional-commits.test.ts b/plugins/conventional-commits/__tests__/conventional-commits.test.ts index 6c13c7564..f185defab 100644 --- a/plugins/conventional-commits/__tests__/conventional-commits.test.ts +++ b/plugins/conventional-commits/__tests__/conventional-commits.test.ts @@ -1,4 +1,4 @@ -import Auto, { DEFAULT_PRERELEASE_BRANCHES } from "@auto-it/core"; +import Auto, { SEMVER, DEFAULT_PRERELEASE_BRANCHES } from "@auto-it/core"; import makeCommitFromMsg from "@auto-it/core/dist/__tests__/make-commit-from-msg"; import Git from "@auto-it/core/dist/git"; import LogParse from "@auto-it/core/dist/log-parse"; @@ -314,17 +314,23 @@ test("should skip when not a fix/feat/breaking change commit", async () => { }); }); -test("should not semver label to pr if semver label exists", async () => { +test("should not add semver label to pr if semver label exists", async () => { const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const autoHooks = makeHooks(); const addLabelToPr = jest.fn(); + const customLabels = [ + ...defaultLabels, + { name: "my-major", releaseType: SEMVER.major } as any, + ]; + const versionLabelsCustom = getVersionMap(customLabels); + const auto = ({ hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, + labels: customLabels, + semVerLabels: versionLabelsCustom, logger: dummyLog(), git: { - getLabels: async () => ["minor"], + getLabels: async () => ["my-major"], getCommitsForPR: async () => { return [ { @@ -386,6 +392,47 @@ test("should add correct semver label to pr - one commit", async () => { expect(addLabelToPr).toHaveBeenCalledWith(1, "patch"); }); +// test("should add correct semver label to pr - custom labels", async () => { +// const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); +// const customLabels = [ +// ...defaultLabels, +// { name: "my-major", releaseType: SEMVER.major } as any, +// ]; +// const versionLabelsCustom = getVersionMap(customLabels); +// const autoHooks = makeHooks(); +// const addLabelToPr = jest.fn(); +// const auto = ({ +// hooks: autoHooks, +// labels: customLabels, +// semVerLabels: versionLabelsCustom, +// logger: dummyLog(), +// git: { +// getLabels: async () => ['my-major'], +// getCommitsForPR: async () => { +// return [ +// { +// sha: "1234", +// commit: { +// message: "normal commit", +// }, +// }, +// ]; +// }, +// addLabelToPr, +// }, +// } as unknown) as Auto; + +// conventionalCommitsPlugin.apply(auto); + +// await auto.hooks.prCheck.promise({ +// pr: { +// number: 1, +// } as any, +// }); + +// expect(addLabelToPr).toHaveBeenCalledWith(1, "my-major"); +// }); + test("should add correct semver label to pr - multiple commit", async () => { const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); const autoHooks = makeHooks(); diff --git a/plugins/conventional-commits/src/index.ts b/plugins/conventional-commits/src/index.ts index a53d3d8b4..9364d7f40 100644 --- a/plugins/conventional-commits/src/index.ts +++ b/plugins/conventional-commits/src/index.ts @@ -137,9 +137,10 @@ export default class ConventionalCommitsPlugin implements IPlugin { } const labels = await auto.git.getLabels(pr.number); + const semVerLabels = [...auto.semVerLabels!.values()].flat(); // check if semver label is already on PR - if (labels.filter((l) => auto.semVerLabels?.get(l as any)).length > 0) { + if (labels.some((l) => semVerLabels.includes(l))) { return; } From 82932b95735ab3ce967419e1bf411c4562c47cfe Mon Sep 17 00:00:00 2001 From: Andrew Lisowski Date: Fri, 29 Jan 2021 09:52:53 -0800 Subject: [PATCH 4/5] add a configured label rather than the release type --- packages/core/src/auto.ts | 3 +- packages/core/src/semver.ts | 8 +- .../__tests__/conventional-commits.test.ts | 81 ++++++++++--------- plugins/conventional-commits/package.json | 1 + plugins/conventional-commits/src/index.ts | 44 ++++++---- 5 files changed, 78 insertions(+), 59 deletions(-) diff --git a/packages/core/src/auto.ts b/packages/core/src/auto.ts index 6fd69efa5..7692232b1 100644 --- a/packages/core/src/auto.ts +++ b/packages/core/src/auto.ts @@ -2226,7 +2226,8 @@ export { IPlugin } from "./utils/load-plugins"; export { ICommitAuthor, IExtendedCommit } from "./log-parse"; export { default as Auto } from "./auto"; -export { default as SEMVER, VersionLabel } from "./semver"; +export { default as SEMVER } from "./semver"; +export * from "./semver"; export { default as execPromise } from "./utils/exec-promise"; export { default as getLernaPackages, diff --git a/packages/core/src/semver.ts b/packages/core/src/semver.ts index 1c5b2c79b..2e0fea2e0 100644 --- a/packages/core/src/semver.ts +++ b/packages/core/src/semver.ts @@ -164,7 +164,7 @@ export function getHigherSemverTag(left: SEMVER, right: SEMVER): SEMVER { } /** Get the semver bump for a release type */ -const getBump = (releaseType?: ReleaseType) => +export const getReleaseType = (releaseType?: ReleaseType) => releaseType === "none" || releaseType === "skip" ? SEMVER.noVersion : releaseType === "release" @@ -202,7 +202,7 @@ export function calculateSemVerBump( // 2. It has labels but none of them are auto labels if ( index === 0 && - (pr.length === 0 || !pr.find((label) => getLabelEntry(label))) + (pr.length === 0 || !pr.some((label) => Boolean(getLabelEntry(label)))) ) { releaseTypes.add(defaultReleaseType); } @@ -238,6 +238,6 @@ export function calculateSemVerBump( } return [...releaseTypes] - .map(getBump) - .reduce(getHigherSemverTag, getBump(defaultReleaseType)); + .map(getReleaseType) + .reduce(getHigherSemverTag, getReleaseType(defaultReleaseType)); } diff --git a/plugins/conventional-commits/__tests__/conventional-commits.test.ts b/plugins/conventional-commits/__tests__/conventional-commits.test.ts index f185defab..0dc69d129 100644 --- a/plugins/conventional-commits/__tests__/conventional-commits.test.ts +++ b/plugins/conventional-commits/__tests__/conventional-commits.test.ts @@ -392,46 +392,47 @@ test("should add correct semver label to pr - one commit", async () => { expect(addLabelToPr).toHaveBeenCalledWith(1, "patch"); }); -// test("should add correct semver label to pr - custom labels", async () => { -// const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); -// const customLabels = [ -// ...defaultLabels, -// { name: "my-major", releaseType: SEMVER.major } as any, -// ]; -// const versionLabelsCustom = getVersionMap(customLabels); -// const autoHooks = makeHooks(); -// const addLabelToPr = jest.fn(); -// const auto = ({ -// hooks: autoHooks, -// labels: customLabels, -// semVerLabels: versionLabelsCustom, -// logger: dummyLog(), -// git: { -// getLabels: async () => ['my-major'], -// getCommitsForPR: async () => { -// return [ -// { -// sha: "1234", -// commit: { -// message: "normal commit", -// }, -// }, -// ]; -// }, -// addLabelToPr, -// }, -// } as unknown) as Auto; - -// conventionalCommitsPlugin.apply(auto); - -// await auto.hooks.prCheck.promise({ -// pr: { -// number: 1, -// } as any, -// }); - -// expect(addLabelToPr).toHaveBeenCalledWith(1, "my-major"); -// }); +test("should add correct semver label to pr - custom labels", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const customLabels = [ + // Only use the custom major label + ...defaultLabels.filter((l) => l.name !== "major"), + { name: "my-major", releaseType: SEMVER.major } as any, + ]; + const versionLabelsCustom = getVersionMap(customLabels); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const auto = ({ + hooks: autoHooks, + labels: customLabels, + semVerLabels: versionLabelsCustom, + logger: dummyLog(), + git: { + getLabels: async () => [], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "BREAKING: normal commit", + }, + }, + ]; + }, + addLabelToPr, + }, + } as unknown) as Auto; + + conventionalCommitsPlugin.apply(auto); + + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); + + expect(addLabelToPr).toHaveBeenCalledWith(1, "my-major"); +}); test("should add correct semver label to pr - multiple commit", async () => { const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); diff --git a/plugins/conventional-commits/package.json b/plugins/conventional-commits/package.json index d71ddc9ed..16ece594c 100644 --- a/plugins/conventional-commits/package.json +++ b/plugins/conventional-commits/package.json @@ -38,6 +38,7 @@ }, "dependencies": { "@auto-it/core": "link:../../packages/core", + "array.prototype.flatmap": "^1.2.2", "conventional-changelog-core": "^4.2.0", "conventional-changelog-preset-loader": "^2.3.4", "conventional-commits-parser": "^3.1.0", diff --git a/plugins/conventional-commits/src/index.ts b/plugins/conventional-commits/src/index.ts index 9364d7f40..9931873a8 100644 --- a/plugins/conventional-commits/src/index.ts +++ b/plugins/conventional-commits/src/index.ts @@ -2,10 +2,16 @@ import { Options as CoreOptions } from "conventional-changelog-core"; import { Commit, sync as parse } from "conventional-commits-parser"; import { promisify } from "util"; import conventionalChangelogPresetLoader from "conventional-changelog-preset-loader"; +import flatMap from "array.prototype.flatmap"; import * as t from "io-ts"; -import { Auto, IPlugin, SEMVER } from "@auto-it/core"; -// import conventionalCommitsParser, { Commit } from "conventional-commits-parser"; +import { + Auto, + IPlugin, + SEMVER, + getReleaseType, + getHigherSemverTag, +} from "@auto-it/core"; /** Resolve a conventional commit preset */ function presetResolver(presetPackage: CoreOptions.Config) { @@ -137,7 +143,7 @@ export default class ConventionalCommitsPlugin implements IPlugin { } const labels = await auto.git.getLabels(pr.number); - const semVerLabels = [...auto.semVerLabels!.values()].flat(); + const semVerLabels = flatMap([...auto.semVerLabels!.values()], (x) => x); // check if semver label is already on PR if (labels.some((l) => semVerLabels.includes(l))) { @@ -158,22 +164,32 @@ export default class ConventionalCommitsPlugin implements IPlugin { }) ); - const sorted = bumps - .filter( - (bump): bump is SEMVER.major | SEMVER.minor | SEMVER.patch | "skip" => - bump !== undefined - ) - .sort((bump1, bump2) => { - return VERSIONS.indexOf(bump1) - VERSIONS.indexOf(bump2); - }); + const definedBumps = bumps.filter( + (bump): bump is SEMVER.major | SEMVER.minor | SEMVER.patch | "skip" => + bump !== undefined + ); + + if (definedBumps.length === 0) { + return; + } + + const bump = definedBumps + .map(getReleaseType) + .reduce(getHigherSemverTag, SEMVER.noVersion); - if (!sorted[0]) { + if ( + !bump || + bump === SEMVER.premajor || + bump === SEMVER.preminor || + bump === SEMVER.prepatch + ) { return; } - const label = auto.semVerLabels?.get(sorted[0]); + const label = auto.semVerLabels?.get(bump); + if (label) { - await auto.git.addLabelToPr(pr.number, sorted[0]); + await auto.git.addLabelToPr(pr.number, label[0]); } }); From a9e87c092372ee4d1550b930b7d9a909e9814fd7 Mon Sep 17 00:00:00 2001 From: Andrew Lisowski Date: Fri, 29 Jan 2021 09:56:24 -0800 Subject: [PATCH 5/5] organize tests --- .../__tests__/conventional-commits.test.ts | 853 +++++++++--------- 1 file changed, 438 insertions(+), 415 deletions(-) diff --git a/plugins/conventional-commits/__tests__/conventional-commits.test.ts b/plugins/conventional-commits/__tests__/conventional-commits.test.ts index 0dc69d129..5505e4b22 100644 --- a/plugins/conventional-commits/__tests__/conventional-commits.test.ts +++ b/plugins/conventional-commits/__tests__/conventional-commits.test.ts @@ -19,465 +19,488 @@ const config = { labels: defaultLabels, }; -test("should do nothing when conventional commit message is not present", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - } as Auto); - - const logParseHooks = makeLogParseHooks(); - autoHooks.onCreateLogParse.call({ - hooks: logParseHooks, - } as LogParse); - - const commit = makeCommitFromMsg("normal commit with no bump"); - expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual( - commit - ); -}); +describe("parseCommit", () => { + test("should do nothing when conventional commit message is not present", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + } as Auto); + + const logParseHooks = makeLogParseHooks(); + autoHooks.onCreateLogParse.call({ + hooks: logParseHooks, + } as LogParse); + + const commit = makeCommitFromMsg("normal commit with no bump"); + expect( + await logParseHooks.parseCommit.promise({ ...commit }) + ).toStrictEqual(commit); + }); -test("should add correct semver label to commit", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - } as Auto); - - const logParseHooks = makeLogParseHooks(); - autoHooks.onCreateLogParse.call({ - hooks: logParseHooks, - } as LogParse); - - const commit = makeCommitFromMsg("fix: normal commit with no bump"); - expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual({ - ...commit, - labels: ["patch"], + test("should add correct semver label to commit", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + } as Auto); + + const logParseHooks = makeLogParseHooks(); + autoHooks.onCreateLogParse.call({ + hooks: logParseHooks, + } as LogParse); + + const commit = makeCommitFromMsg("fix: normal commit with no bump"); + expect( + await logParseHooks.parseCommit.promise({ ...commit }) + ).toStrictEqual({ + ...commit, + labels: ["patch"], + }); }); -}); -test("should add correct semver label to commit - feat", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - } as Auto); - - const logParseHooks = makeLogParseHooks(); - autoHooks.onCreateLogParse.call({ - hooks: logParseHooks, - } as LogParse); - - const commit = makeCommitFromMsg("feat: normal commit with no bump"); - expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual({ - ...commit, - labels: ["minor"], + test("should add correct semver label to commit - feat", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + } as Auto); + + const logParseHooks = makeLogParseHooks(); + autoHooks.onCreateLogParse.call({ + hooks: logParseHooks, + } as LogParse); + + const commit = makeCommitFromMsg("feat: normal commit with no bump"); + expect( + await logParseHooks.parseCommit.promise({ ...commit }) + ).toStrictEqual({ + ...commit, + labels: ["minor"], + }); }); -}); -test("should add major semver label to commit", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - } as Auto); - - const logParseHooks = makeLogParseHooks(); - autoHooks.onCreateLogParse.call({ - hooks: logParseHooks, - } as LogParse); - - const commit = makeCommitFromMsg("BREAKING: normal commit with no bump"); - expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual({ - ...commit, - labels: ["major"], + test("should add major semver label to commit", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + } as Auto); + + const logParseHooks = makeLogParseHooks(); + autoHooks.onCreateLogParse.call({ + hooks: logParseHooks, + } as LogParse); + + const commit = makeCommitFromMsg("BREAKING: normal commit with no bump"); + expect( + await logParseHooks.parseCommit.promise({ ...commit }) + ).toStrictEqual({ + ...commit, + labels: ["major"], + }); }); -}); -test("should add major semver label to commit - !", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - } as Auto); - - const logParseHooks = makeLogParseHooks(); - autoHooks.onCreateLogParse.call({ - hooks: logParseHooks, - } as LogParse); - - const commit = makeCommitFromMsg("feat!: normal commit with no bump"); - expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual({ - ...commit, - labels: ["major"], + test("should add major semver label to commit - !", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + } as Auto); + + const logParseHooks = makeLogParseHooks(); + autoHooks.onCreateLogParse.call({ + hooks: logParseHooks, + } as LogParse); + + const commit = makeCommitFromMsg("feat!: normal commit with no bump"); + expect( + await logParseHooks.parseCommit.promise({ ...commit }) + ).toStrictEqual({ + ...commit, + labels: ["major"], + }); }); -}); -test("should not include label-less head commit if any other commit in PR has conventional commit message", async () => { - const commit = makeCommitFromMsg("Merge pull request #123 from some-pr\n\n"); - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const logParse = new LogParse(); - const autoHooks = makeHooks(); - const mockGit = ({ - getUserByEmail: jest.fn(), - searchRepo: jest.fn(), - getCommitDate: jest.fn(), - getFirstCommit: jest.fn(), - getPr: jest.fn(), - getCommitsForPR: () => - Promise.resolve([{ sha: "1", commit: { message: "fix: child commit" } }]), - } as unknown) as Git; - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - git: mockGit, - release: new Release(mockGit, config), - } as Auto); - - autoHooks.onCreateLogParse.call(logParse); - - const result = await logParse.normalizeCommit(commit); - expect(result).toBeUndefined(); + test("should skip when not a fix/feat/breaking change commit", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + } as Auto); + + const logParseHooks = makeLogParseHooks(); + autoHooks.onCreateLogParse.call({ + hooks: logParseHooks, + } as LogParse); + + const commit = makeCommitFromMsg("chore: i should not trigger a release"); + expect( + await logParseHooks.parseCommit.promise({ ...commit }) + ).toStrictEqual({ + ...commit, + labels: ["skip-release"], + }); + }); }); -test("should include labeled head commit", async () => { - const commit = makeCommitFromMsg("Merge pull request #123 from some-pr\n\n", { - labels: ["major"], +describe("normalizeCommit", () => { + test("should not include label-less head commit if any other commit in PR has conventional commit message", async () => { + const commit = makeCommitFromMsg( + "Merge pull request #123 from some-pr\n\n" + ); + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const logParse = new LogParse(); + const autoHooks = makeHooks(); + const mockGit = ({ + getUserByEmail: jest.fn(), + searchRepo: jest.fn(), + getCommitDate: jest.fn(), + getFirstCommit: jest.fn(), + getPr: jest.fn(), + getCommitsForPR: () => + Promise.resolve([ + { sha: "1", commit: { message: "fix: child commit" } }, + ]), + } as unknown) as Git; + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: mockGit, + release: new Release(mockGit, config), + } as Auto); + + autoHooks.onCreateLogParse.call(logParse); + + const result = await logParse.normalizeCommit(commit); + expect(result).toBeUndefined(); }); - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const logParse = new LogParse(); - const autoHooks = makeHooks(); - const mockGit = ({ - getUserByEmail: jest.fn(), - searchRepo: jest.fn(), - getCommitDate: jest.fn(), - getFirstCommit: jest.fn(), - getPr: jest.fn(), - getLatestRelease: () => Promise.resolve("1.2.3"), - getGitLog: () => - Promise.resolve([ - commit, - makeCommitFromMsg("fix: child commit", { hash: "1" }), - makeCommitFromMsg("unrelated", { hash: "2" }), - ]), - getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), - } as unknown) as Git; - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - git: mockGit, - release: new Release(mockGit, config), - } as Auto); - - autoHooks.onCreateLogParse.call(logParse); - - const result = await logParse.normalizeCommit(commit); - expect(result?.hash).toBe("foo"); -}); -test("should respect PR label if SEMVER", async () => { - const commit = makeCommitFromMsg("fix: a test", { - labels: ["major"], + test("should include labeled head commit", async () => { + const commit = makeCommitFromMsg( + "Merge pull request #123 from some-pr\n\n", + { + labels: ["major"], + } + ); + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const logParse = new LogParse(); + const autoHooks = makeHooks(); + const mockGit = ({ + getUserByEmail: jest.fn(), + searchRepo: jest.fn(), + getCommitDate: jest.fn(), + getFirstCommit: jest.fn(), + getPr: jest.fn(), + getLatestRelease: () => Promise.resolve("1.2.3"), + getGitLog: () => + Promise.resolve([ + commit, + makeCommitFromMsg("fix: child commit", { hash: "1" }), + makeCommitFromMsg("unrelated", { hash: "2" }), + ]), + getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), + } as unknown) as Git; + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: mockGit, + release: new Release(mockGit, config), + } as Auto); + + autoHooks.onCreateLogParse.call(logParse); + + const result = await logParse.normalizeCommit(commit); + expect(result?.hash).toBe("foo"); }); - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const logParse = new LogParse(); - const autoHooks = makeHooks(); - const mockGit = ({ - getUserByEmail: jest.fn(), - searchRepo: jest.fn(), - getCommitDate: jest.fn(), - getFirstCommit: jest.fn(), - getPr: jest.fn(), - getLatestRelease: () => Promise.resolve("1.2.3"), - getGitLog: () => Promise.resolve([commit]), - getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), - } as unknown) as Git; - - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - git: mockGit, - release: new Release(mockGit, config), - } as Auto); - - autoHooks.onCreateLogParse.call(logParse); - - const result = await logParse.normalizeCommit(commit); - expect(result?.labels).toStrictEqual(["major"]); -}); -test("should add conventional commit label if none/skip", async () => { - const commit = makeCommitFromMsg("fix: a test", { - labels: ["skip-release", "internal"], + test("should respect PR label if SEMVER", async () => { + const commit = makeCommitFromMsg("fix: a test", { + labels: ["major"], + }); + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const logParse = new LogParse(); + const autoHooks = makeHooks(); + const mockGit = ({ + getUserByEmail: jest.fn(), + searchRepo: jest.fn(), + getCommitDate: jest.fn(), + getFirstCommit: jest.fn(), + getPr: jest.fn(), + getLatestRelease: () => Promise.resolve("1.2.3"), + getGitLog: () => Promise.resolve([commit]), + getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), + } as unknown) as Git; + + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: mockGit, + release: new Release(mockGit, config), + } as Auto); + + autoHooks.onCreateLogParse.call(logParse); + + const result = await logParse.normalizeCommit(commit); + expect(result?.labels).toStrictEqual(["major"]); }); - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const logParse = new LogParse(); - const autoHooks = makeHooks(); - const mockGit = ({ - getUserByEmail: jest.fn(), - searchRepo: jest.fn(), - getCommitDate: jest.fn(), - getFirstCommit: jest.fn(), - getPr: jest.fn(), - getLatestRelease: () => Promise.resolve("1.2.3"), - getGitLog: () => Promise.resolve([commit]), - getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), - } as unknown) as Git; - - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - git: mockGit, - release: new Release(mockGit, config), - } as Auto); - - autoHooks.onCreateLogParse.call(logParse); - - const result = await logParse.normalizeCommit(commit); - expect(result?.labels).toStrictEqual(["skip-release", "internal", "patch"]); -}); -test("should not add skip when a non skip commit is present with a skip commit", async () => { - const commit = makeCommitFromMsg("fix: a test"); - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const logParse = new LogParse(); - const autoHooks = makeHooks(); - const mockGit = ({ - getUserByEmail: jest.fn(), - searchRepo: jest.fn(), - getCommitDate: jest.fn(), - getFirstCommit: jest.fn(), - getPr: jest.fn(), - getLatestRelease: () => Promise.resolve("1.2.3"), - getGitLog: () => - Promise.resolve([commit, makeCommitFromMsg("chore: a test 2")]), - getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), - } as unknown) as Git; - - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - git: mockGit, - release: new Release(mockGit, config), - } as Auto); - - autoHooks.onCreateLogParse.call(logParse); - - const result = await logParse.normalizeCommit(commit); - expect(result?.labels).toStrictEqual(["patch"]); -}); + test("should add conventional commit label if none/skip", async () => { + const commit = makeCommitFromMsg("fix: a test", { + labels: ["skip-release", "internal"], + }); + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const logParse = new LogParse(); + const autoHooks = makeHooks(); + const mockGit = ({ + getUserByEmail: jest.fn(), + searchRepo: jest.fn(), + getCommitDate: jest.fn(), + getFirstCommit: jest.fn(), + getPr: jest.fn(), + getLatestRelease: () => Promise.resolve("1.2.3"), + getGitLog: () => Promise.resolve([commit]), + getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), + } as unknown) as Git; + + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: mockGit, + release: new Release(mockGit, config), + } as Auto); + + autoHooks.onCreateLogParse.call(logParse); + + const result = await logParse.normalizeCommit(commit); + expect(result?.labels).toStrictEqual(["skip-release", "internal", "patch"]); + }); -test("should skip when not a fix/feat/breaking change commit", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - conventionalCommitsPlugin.apply({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - } as Auto); - - const logParseHooks = makeLogParseHooks(); - autoHooks.onCreateLogParse.call({ - hooks: logParseHooks, - } as LogParse); - - const commit = makeCommitFromMsg("chore: i should not trigger a release"); - expect(await logParseHooks.parseCommit.promise({ ...commit })).toStrictEqual({ - ...commit, - labels: ["skip-release"], + test("should not add skip when a non skip commit is present with a skip commit", async () => { + const commit = makeCommitFromMsg("fix: a test"); + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const logParse = new LogParse(); + const autoHooks = makeHooks(); + const mockGit = ({ + getUserByEmail: jest.fn(), + searchRepo: jest.fn(), + getCommitDate: jest.fn(), + getFirstCommit: jest.fn(), + getPr: jest.fn(), + getLatestRelease: () => Promise.resolve("1.2.3"), + getGitLog: () => + Promise.resolve([commit, makeCommitFromMsg("chore: a test 2")]), + getCommitsForPR: () => Promise.resolve([{ sha: "1" }]), + } as unknown) as Git; + + conventionalCommitsPlugin.apply({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: mockGit, + release: new Release(mockGit, config), + } as Auto); + + autoHooks.onCreateLogParse.call(logParse); + + const result = await logParse.normalizeCommit(commit); + expect(result?.labels).toStrictEqual(["patch"]); }); }); -test("should not add semver label to pr if semver label exists", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - const addLabelToPr = jest.fn(); - const customLabels = [ - ...defaultLabels, - { name: "my-major", releaseType: SEMVER.major } as any, - ]; - const versionLabelsCustom = getVersionMap(customLabels); - - const auto = ({ - hooks: autoHooks, - labels: customLabels, - semVerLabels: versionLabelsCustom, - logger: dummyLog(), - git: { - getLabels: async () => ["my-major"], - getCommitsForPR: async () => { - return [ - { - sha: "1234", - commit: { - message: "fix: normal commit", +describe("prCheck", () => { + test("should not add semver label to pr if semver label exists", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const customLabels = [ + ...defaultLabels, + { name: "my-major", releaseType: SEMVER.major } as any, + ]; + const versionLabelsCustom = getVersionMap(customLabels); + + const auto = ({ + hooks: autoHooks, + labels: customLabels, + semVerLabels: versionLabelsCustom, + logger: dummyLog(), + git: { + getLabels: async () => ["my-major"], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "fix: normal commit", + }, }, - }, - ]; + ]; + }, + addLabelToPr, }, - addLabelToPr, - }, - } as unknown) as Auto; + } as unknown) as Auto; - conventionalCommitsPlugin.apply(auto); + conventionalCommitsPlugin.apply(auto); - await auto.hooks.prCheck.promise({ - pr: { - number: 1, - } as any, - }); + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); - expect(addLabelToPr).toHaveBeenCalledTimes(0); -}); + expect(addLabelToPr).toHaveBeenCalledTimes(0); + }); -test("should add correct semver label to pr - one commit", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - const addLabelToPr = jest.fn(); - const auto = ({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - git: { - getLabels: async () => [], - getCommitsForPR: async () => { - return [ - { - sha: "1234", - commit: { - message: "fix: normal commit", + test("should add correct semver label to pr - one commit", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const auto = ({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: { + getLabels: async () => [], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "fix: normal commit", + }, }, - }, - ]; + ]; + }, + addLabelToPr, }, - addLabelToPr, - }, - } as unknown) as Auto; + } as unknown) as Auto; - conventionalCommitsPlugin.apply(auto); + conventionalCommitsPlugin.apply(auto); - await auto.hooks.prCheck.promise({ - pr: { - number: 1, - } as any, - }); + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); - expect(addLabelToPr).toHaveBeenCalledWith(1, "patch"); -}); + expect(addLabelToPr).toHaveBeenCalledWith(1, "patch"); + }); -test("should add correct semver label to pr - custom labels", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const customLabels = [ - // Only use the custom major label - ...defaultLabels.filter((l) => l.name !== "major"), - { name: "my-major", releaseType: SEMVER.major } as any, - ]; - const versionLabelsCustom = getVersionMap(customLabels); - const autoHooks = makeHooks(); - const addLabelToPr = jest.fn(); - const auto = ({ - hooks: autoHooks, - labels: customLabels, - semVerLabels: versionLabelsCustom, - logger: dummyLog(), - git: { - getLabels: async () => [], - getCommitsForPR: async () => { - return [ - { - sha: "1234", - commit: { - message: "BREAKING: normal commit", + test("should add correct semver label to pr - custom labels", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const customLabels = [ + // Only use the custom major label + ...defaultLabels.filter((l) => l.name !== "major"), + { name: "my-major", releaseType: SEMVER.major } as any, + ]; + const versionLabelsCustom = getVersionMap(customLabels); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const auto = ({ + hooks: autoHooks, + labels: customLabels, + semVerLabels: versionLabelsCustom, + logger: dummyLog(), + git: { + getLabels: async () => [], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "BREAKING: normal commit", + }, }, - }, - ]; + ]; + }, + addLabelToPr, }, - addLabelToPr, - }, - } as unknown) as Auto; + } as unknown) as Auto; - conventionalCommitsPlugin.apply(auto); + conventionalCommitsPlugin.apply(auto); - await auto.hooks.prCheck.promise({ - pr: { - number: 1, - } as any, - }); + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); - expect(addLabelToPr).toHaveBeenCalledWith(1, "my-major"); -}); + expect(addLabelToPr).toHaveBeenCalledWith(1, "my-major"); + }); -test("should add correct semver label to pr - multiple commit", async () => { - const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); - const autoHooks = makeHooks(); - const addLabelToPr = jest.fn(); - const auto = ({ - hooks: autoHooks, - labels: defaultLabels, - semVerLabels: versionLabels, - logger: dummyLog(), - git: { - getLabels: async () => [], - getCommitsForPR: async () => { - return [ - { - sha: "1234", - commit: { - message: "fix: normal commit", + test("should add correct semver label to pr - multiple commit", async () => { + const conventionalCommitsPlugin = new ConventionalCommitsPlugin(); + const autoHooks = makeHooks(); + const addLabelToPr = jest.fn(); + const auto = ({ + hooks: autoHooks, + labels: defaultLabels, + semVerLabels: versionLabels, + logger: dummyLog(), + git: { + getLabels: async () => [], + getCommitsForPR: async () => { + return [ + { + sha: "1234", + commit: { + message: "fix: normal commit", + }, }, - }, - { - sha: "12345", - commit: { - message: "feat: normal commit", + { + sha: "12345", + commit: { + message: "feat: normal commit", + }, }, - }, - { - sha: "123456", - commit: { - message: "feat: normal commit", + { + sha: "123456", + commit: { + message: "feat: normal commit", + }, }, - }, - ]; + ]; + }, + addLabelToPr, }, - addLabelToPr, - }, - } as unknown) as Auto; + } as unknown) as Auto; - conventionalCommitsPlugin.apply(auto); + conventionalCommitsPlugin.apply(auto); - await auto.hooks.prCheck.promise({ - pr: { - number: 1, - } as any, - }); + await auto.hooks.prCheck.promise({ + pr: { + number: 1, + } as any, + }); - expect(addLabelToPr).toHaveBeenCalledWith(1, "minor"); + expect(addLabelToPr).toHaveBeenCalledWith(1, "minor"); + }); });