diff --git a/packages/core/src/__tests__/get-current-branch.test.ts b/packages/core/src/__tests__/get-current-branch.test.ts new file mode 100644 index 000000000..e697b9d9c --- /dev/null +++ b/packages/core/src/__tests__/get-current-branch.test.ts @@ -0,0 +1,65 @@ +import { execSync } from "child_process"; +import { CiEnv } from "env-ci"; +import { getCurrentBranch } from "../auto"; + +jest.mock('child_process') + +describe('getCurrentBranch', () => { + + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('when isPr', () => { + it('returns pr branch from env ci when valid', () => { + const env: Partial = { + isPr: true, + prBranch: 'my-pr-branch' + } + expect(getCurrentBranch(env)).toBe('my-pr-branch') + }); + + it('tries git command when PR is invalid', () => { + const env: Partial = { + isPr: true, + prBranch: 'undefined' + } + + getCurrentBranch(env); + + expect(execSync).toHaveBeenCalledWith("git symbolic-ref --short HEAD", { + encoding: "utf8", + stdio: "ignore", + }) + }); + }) + + describe('when not isPr', () => { + + it('returns pr branch from env ci when valid', () => { + const env: Partial = { + isPr: false, + prBranch: 'my-pr-branch', + branch: 'my-release-branch' + } + + expect(getCurrentBranch(env)).toBe('my-release-branch'); + + expect(execSync).not.toHaveBeenCalled() + }); + + it('tries git command when branch name is invalid', () => { + const env: Partial = { + isPr: false, + prBranch: 'my-pr-branch', + branch: undefined + } + getCurrentBranch(env); + + expect(execSync).toHaveBeenCalledWith("git symbolic-ref --short HEAD", { + encoding: "utf8", + stdio: "ignore", + }) + }); + }) +}) \ No newline at end of file diff --git a/packages/core/src/auto.ts b/packages/core/src/auto.ts index bc193fd2f..8775faf1f 100644 --- a/packages/core/src/auto.ts +++ b/packages/core/src/auto.ts @@ -1684,7 +1684,9 @@ export default class Auto { private async oldRelease( options: IShipItOptions ): Promise { - const latestTag = await this.git?.getLatestTagInBranch(); + const latestTag = await this.git?.getLatestTagInBranch( + getCurrentBranch() || "" + ); const result = await this.publishFullRelease({ ...options, from: latestTag, diff --git a/packages/core/src/utils/get-current-branch.ts b/packages/core/src/utils/get-current-branch.ts index cbc6e07d9..75f23c890 100644 --- a/packages/core/src/utils/get-current-branch.ts +++ b/packages/core/src/utils/get-current-branch.ts @@ -1,18 +1,23 @@ -import envCi from "env-ci"; +import envCi, { CiEnv } from "env-ci"; import { execSync } from "child_process"; -const env = envCi(); +const defaultCiEnvironment = envCi(); + +/** + * Validates that the given branch name should be returned by environment context + */ +const isValidBranch = (branch: string | undefined) => typeof branch === "string" && branch !== "undefined" /** Get the current branch the git repo is set to */ -export function getCurrentBranch() { +export function getCurrentBranch(env: Partial = defaultCiEnvironment) { const isPR = "isPr" in env && env.isPr; let branch: string | undefined; // env-ci sets branch to target branch (ex: main) in some CI services. // so we should make sure we aren't in a PR just to be safe - if (isPR && "prBranch" in env) { + if (isPR && "prBranch" in env && isValidBranch(env.prBranch)) { branch = env.prBranch; - } else { + } else if(isValidBranch(env.branch)) { branch = env.branch; } diff --git a/packages/core/src/utils/load-plugins.ts b/packages/core/src/utils/load-plugins.ts index ffade5262..3b27daaba 100644 --- a/packages/core/src/utils/load-plugins.ts +++ b/packages/core/src/utils/load-plugins.ts @@ -166,18 +166,18 @@ export function findPlugin( return userPlugin; } - const officialPlugin = path.join("@auto-it", pluginPath); + const canaryPlugin = path.join("@auto-canary", pluginPath); // Try importing official plugin - if (exists(officialPlugin)) { - return officialPlugin; + if (exists(canaryPlugin)) { + return canaryPlugin; } - const canaryPlugin = path.join("@auto-canary", pluginPath); + const officialPlugin = path.join("@auto-it", pluginPath); // Try importing official plugin - if (exists(canaryPlugin)) { - return canaryPlugin; + if (exists(officialPlugin)) { + return officialPlugin; } // Try requiring a package diff --git a/plugins/npm/__tests__/npm.test.ts b/plugins/npm/__tests__/npm.test.ts index a42b3b013..58c3908eb 100644 --- a/plugins/npm/__tests__/npm.test.ts +++ b/plugins/npm/__tests__/npm.test.ts @@ -398,6 +398,42 @@ describe("getPreviousVersion", () => { expect(await hooks.getPreviousVersion.promise()).toBe("0.1.2"); }); + + test("should ignore greatest published monorepo package in maintenance mode", async () => { + execPromise.mockClear() + mockFs({ + "lerna.json": ` + { + "name": "test", + "version": "1.5.0" + } + `, + ...monorepoPackagesOnFs, + }); + const plugin = new NPMPlugin(); + const hooks = makeHooks(); + + // isMonorepo + monorepoPackages.mockReturnValueOnce(monorepoPackagesResult); + // published version of test package + execPromise.mockReturnValueOnce("2.1.0"); + + jest.spyOn(Auto, 'getCurrentBranch').mockReturnValueOnce('major-2') + + + plugin.apply({ + config: { prereleaseBranches: ["next"], versionBranches: 'major-' }, + hooks, + remote: "origin", + baseBranch: "main", + logger: dummyLog(), + prefixRelease: (str) => str, + } as Auto.Auto); + + + expect(await hooks.getPreviousVersion.promise()).toBe("1.5.0"); + expect(execPromise).not.toHaveBeenCalled() + }); }); test("should use string semver if no published package", async () => { diff --git a/plugins/npm/src/index.ts b/plugins/npm/src/index.ts index 793920f34..e95a4a379 100644 --- a/plugins/npm/src/index.ts +++ b/plugins/npm/src/index.ts @@ -264,7 +264,7 @@ const markdownList = (lines: string[]) => lines.map((line) => `- \`${line}\``).join("\n"); /** Get the previous version. Typically from a package distribution description file. */ -async function getPreviousVersion(auto: Auto, prereleaseBranch: string) { +async function getPreviousVersion(auto: Auto, prereleaseBranch: string, isMaintenanceBranch: boolean) { let previousVersion = ""; if (isMonorepo()) { @@ -281,7 +281,7 @@ async function getPreviousVersion(auto: Auto, prereleaseBranch: string) { } else { const releasedPackage = getMonorepoPackage(); - if (!releasedPackage.name && !releasedPackage.version) { + if (isMaintenanceBranch || (!releasedPackage.name && !releasedPackage.version)) { previousVersion = auto.prefixRelease(monorepoVersion); } else { previousVersion = await greaterRelease( @@ -297,15 +297,18 @@ async function getPreviousVersion(auto: Auto, prereleaseBranch: string) { "Using package.json to calculate previous version" ); const { version, name } = await loadPackageJson(); - - previousVersion = version - ? await greaterRelease( - auto.prefixRelease, - name, - auto.prefixRelease(version), - prereleaseBranch - ) - : "0.0.0"; + if(isMaintenanceBranch && version) { + previousVersion = version + } else { + previousVersion = version + ? await greaterRelease( + auto.prefixRelease, + name, + auto.prefixRelease(version), + prereleaseBranch + ) + : "0.0.0"; + } } auto.logger.verbose.info( @@ -668,6 +671,12 @@ export default class NPMPlugin implements IPlugin { ? branch : prereleaseBranches[0]; + let isMaintenanceBranch = false; + + if(auto.config?.versionBranches && branch) { + isMaintenanceBranch = branch.includes(typeof auto.config.versionBranches === "boolean" ? "version-" : auto.config.versionBranches) + } + auto.hooks.validateConfig.tapPromise(this.name, async (name, options) => { if (name === this.name || name === `@auto-it/${this.name}`) { return validatePluginConfiguration(this.name, pluginOptions, options); @@ -736,7 +745,7 @@ export default class NPMPlugin implements IPlugin { }); auto.hooks.getPreviousVersion.tapPromise(this.name, () => - getPreviousVersion(auto, prereleaseBranch) + getPreviousVersion(auto, prereleaseBranch, isMaintenanceBranch) ); auto.hooks.getRepository.tapPromise(this.name, async () => { @@ -771,6 +780,14 @@ export default class NPMPlugin implements IPlugin { return line; } + // Allows us to see the commit being assessed + auto.logger.veryVerbose.info(`Rendering changelog line for commit:`, commit) + + // adds commits to changelog only if hash is resolvable + if(!commit || !commit.hash) { + return line; + } + const lernaPackages = await this.getLernaPackages(); const changedPackages = await getChangedPackages({ sha: commit.hash, @@ -1199,7 +1216,7 @@ export default class NPMPlugin implements IPlugin { const lastRelease = await auto.git!.getLatestRelease(); const latestTag = (await auto.git?.getLastTagNotInBaseBranch(prereleaseBranch)) || - (await getPreviousVersion(auto, prereleaseBranch)); + (await getPreviousVersion(auto, prereleaseBranch, isMaintenanceBranch)); if (isMonorepo()) { auto.logger.verbose.info("Detected monorepo, using lerna");