From dcb937e302afe42324a16a1cf9ca2f975fa8af52 Mon Sep 17 00:00:00 2001 From: Nicholas Lim <18374483+niclim@users.noreply.github.com> Date: Fri, 28 Apr 2023 10:42:25 -0400 Subject: [PATCH] allow pass in compare-from push and pr options --- action.yml | 6 + build/index.js | 74 ++++++++---- src/__tests__/action.test.ts | 228 +++++++++++++++++++++-------------- src/action.ts | 93 ++++++++------ src/index.ts | 12 +- 5 files changed, 257 insertions(+), 156 deletions(-) diff --git a/action.yml b/action.yml index 8e84a48..fc328ab 100644 --- a/action.yml +++ b/action.yml @@ -7,6 +7,12 @@ inputs: github_token: description: "A GitHub token that can append comments to a PR" required: true + compare_from_push: + description: "Set the ref to compare from in push events" + required: false + compare_from_pr: + description: "Set the ref to compare from in pull request events" + required: false standards_fail: description: "Fail the action if a standard fails" required: false diff --git a/build/index.js b/build/index.js index 313d80b..b38ca66 100644 --- a/build/index.js +++ b/build/index.js @@ -3999,7 +3999,8 @@ async function execCommand(command, args, options = {}, logError = true) { return false; } } -async function runAction(opticToken, githubToken, additionalArgs, standardsFail, eventName, headRef, baseRef, owner, repo, sha, refName) { +async function runAction(opticToken, githubToken, { additionalArgs, standardsFail, eventName, headRef, baseRef, owner, repo, sha, refName, compareFromPush, compareFromPr, }) { + var _a; const failOnCheckError = standardsFail === "true"; const valid = verifyInput(opticToken, eventName, owner, repo); if (!valid) { @@ -4020,25 +4021,30 @@ async function runAction(opticToken, githubToken, additionalArgs, standardsFail, } let from = ""; if (eventName === "pull_request") { - const fromBranch = baseRef || ""; - from = `origin/${fromBranch}`; - if (!(await ensureRef(fromBranch))) { + const fromBranch = (_a = compareFromPr !== null && compareFromPr !== void 0 ? compareFromPr : baseRef) !== null && _a !== void 0 ? _a : ""; + const ref = await parseAndEnsureRef(fromBranch); + if (!ref) { core.error(`Unable to fetch ${from}`); return 1; } + from = ref; } else if (eventName === "push") { - from = "HEAD~1"; - if (!(await deepen())) { - core.error("Unable to fetch HEAD~1"); + const fromBranch = compareFromPush !== null && compareFromPush !== void 0 ? compareFromPush : "HEAD~1"; + const ref = await parseAndEnsureRef(fromBranch); + if (!ref) { + core.error(`Unable to fetch ${from}`); return 1; } + from = ref; } if (from === "") { core.error("Unable to determine base for comparison."); return 1; } - const headTag = refName ? `gitbranch:${refName}` : undefined; + const headTag = refName + ? `gitbranch:${refName}` + : undefined; const comparisonRun = await diffAll(opticToken, from, additionalArgs, headTag); if (eventName === "pull_request") { const commentResult = await prComment(githubToken, owner || "", repo || "", pr || "", sha || ""); @@ -4079,23 +4085,29 @@ async function install() { "@useoptic/optic", ]); } -async function ensureRef(ref) { - if (!(await execCommand("git", [ - "fetch", - "--no-tags", - "--depth=1", - "origin", - ref, - ]))) { - return false; +async function parseAndEnsureRef(ref) { + if (/^cloud:/i.test(ref)) { + return ref; + } + else if (/^HEAD~/.test(ref)) { + const headCountRaw = ref.replace(/^HEAD~/, ""); + if (!(await execCommand("git", ["fetch", `--deepen=${headCountRaw}`]))) { + return false; + } + return ref; } - return true; -} -async function deepen() { - if (!(await execCommand("git", ["fetch", "--deepen=1"]))) { - return false; + else { + if (!(await execCommand("git", [ + "fetch", + "--no-tags", + "--depth=1", + "origin", + ref, + ]))) { + return false; + } + return `origin/${ref}`; } - return true; } async function diffAll(token, from, additionalArgs, headTag) { core.info("Running Optic diff-all"); @@ -4170,6 +4182,8 @@ const opticToken = core.getInput("optic_token"); const githubToken = core.getInput("github_token"); const standardsFail = core.getInput("standards_fail"); const additionalArgs = core.getInput("additional_args"); +const compareFromPush = core.getInput("compare_from_push"); +const compareFromPr = core.getInput("compare_from_pr"); const eventName = process.env.GITHUB_EVENT_NAME; const headRef = process.env.GITHUB_REF; const baseRef = process.env.GITHUB_BASE_REF; @@ -4177,7 +4191,19 @@ const refName = process.env.GITHUB_REF_NAME; const owner = process.env.GITHUB_REPOSITORY_OWNER; const repo = (_a = process.env.GITHUB_REPOSITORY) === null || _a === void 0 ? void 0 : _a.split("/")[1]; const sha = process.env.GITHUB_SHA; -(0, action_1.runAction)(opticToken, githubToken, additionalArgs, standardsFail, eventName, headRef, baseRef, owner, repo, sha, refName) +(0, action_1.runAction)(opticToken, githubToken, { + additionalArgs, + standardsFail, + eventName, + headRef, + baseRef, + owner, + repo, + sha, + refName, + compareFromPush, + compareFromPr, +}) .then((exitCode) => { return process.exit(exitCode); }) diff --git a/src/__tests__/action.test.ts b/src/__tests__/action.test.ts index 0471d36..7cf65ea 100644 --- a/src/__tests__/action.test.ts +++ b/src/__tests__/action.test.ts @@ -4,38 +4,38 @@ import * as exec from "@actions/exec"; jest.mock("@actions/exec"); test("invalid input", async () => { - const exitCode = await runAction( - "optic-token", - "github-token", - "", - "true", - "", - "", - undefined, - "owner", - "repo", - "abc123", - "" - ); + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "true", + eventName: "", + headRef: "", + baseRef: undefined, + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "", + compareFromPush: undefined, + compareFromPr: undefined, + }); expect(exitCode).toBe(1); }); test("failed install", async () => { const assertFailedInstall = mockFailedInstall(); - const exitCode = await runAction( - "optic-token", - "github-token", - "", - "true", - "push", - "refs/heads/main", - undefined, - "owner", - "repo", - "abc123", - "main" - ); + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "true", + eventName: "push", + headRef: "refs/heads/main", + baseRef: undefined, + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: undefined, + compareFromPr: undefined, + }); expect(exitCode).toBe(1); assertFailedInstall(); }); @@ -46,19 +46,19 @@ test("pull_request event", async () => { const assertDiffAll = mockDiffAll("token", "origin/main"); const assertGitHubComment = mockGitHubComment(); - const exitCode = await runAction( - "optic-token", - "github-token", - "", - "true", - "pull_request", - "refs/pulls/1/merge", - "main", - "owner", - "repo", - "abc123", - "main" - ); + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "true", + eventName: "pull_request", + headRef: "refs/pulls/1/merge", + baseRef: "main", + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: undefined, + compareFromPr: undefined, + }); expect(exitCode).toBe(0); assertInstall(); assertEnsureRef(); @@ -71,19 +71,19 @@ test("push event", async () => { const assertDeepen = mockDeepen(); const assertDiffAll = mockDiffAll("optic-token", "HEAD~1"); - const exitCode = await runAction( - "optic-token", - "github-token", - "", - "true", - "push", - "refs/heads/main", - undefined, - "owner", - "repo", - "abc123", - "main" - ); + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "true", + eventName: "push", + headRef: "refs/heads/main", + baseRef: "main", + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: undefined, + compareFromPr: undefined, + }); expect(exitCode).toBe(0); assertInstall(); assertDeepen(); @@ -97,19 +97,19 @@ test("push event with additional-args", async () => { "--fail-on-untracked-openapi", ]); - const exitCode = await runAction( - "optic-token", - "github-token", - "--fail-on-untracked-openapi", - "true", - "push", - "refs/heads/main", - undefined, - "owner", - "repo", - "abc123", - "main" - ); + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "--fail-on-untracked-openapi", + standardsFail: "true", + eventName: "push", + headRef: "refs/heads/main", + baseRef: undefined, + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: undefined, + compareFromPr: undefined, + }); expect(exitCode).toBe(0); assertInstall(); assertDeepen(); @@ -121,19 +121,19 @@ test("push event with standards failure and standards_fail set to true", async ( const assertDeepen = mockDeepen(); const assertDiffAll = mockDiffAll("optic-token", "HEAD~1", true); - const exitCode = await runAction( - "optic-token", - "github-token", - "", - "true", - "push", - "refs/heads/main", - undefined, - "owner", - "repo", - "abc123", - "main" - ); + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "true", + eventName: "push", + headRef: "refs/heads/main", + baseRef: undefined, + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: undefined, + compareFromPr: undefined, + }); expect(exitCode).toBe(1); assertInstall(); assertDeepen(); @@ -145,25 +145,71 @@ test("push event with standards failure but standards_fail set to false", async const assertDeepen = mockDeepen(); const assertDiffAll = mockDiffAll("optic-token", "HEAD~1", true); - const exitCode = await runAction( - "optic-token", - "github-token", - "", - "false", - "push", - "refs/heads/main", - undefined, - "owner", - "repo", - "abc123", - "main" - ); + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "false", + eventName: "push", + headRef: "refs/heads/main", + baseRef: undefined, + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: undefined, + compareFromPr: undefined, + }); expect(exitCode).toBe(0); assertInstall(); assertDeepen(); assertDiffAll(); }); +test("push event with compare-from-push override with cloud", async () => { + const assertInstall = mockInstall(); + const assertDiffAll = mockDiffAll("optic-token", "cloud:tag", true); + + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "true", + eventName: "push", + headRef: "refs/heads/main", + baseRef: undefined, + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: "cloud:tag", + compareFromPr: undefined, + }); + expect(exitCode).toBe(1); + assertInstall(); + assertDiffAll(); +}); + +test("pull request event with compare-from-push override with HEAD", async () => { + const assertInstall = mockInstall(); + const assertDeepen = mockDeepen(); + const assertDiffAll = mockDiffAll("optic-token", "HEAD~1", true); + + const exitCode = await runAction("optic-token", "github-token", { + additionalArgs: "", + standardsFail: "true", + eventName: "pull_request", + headRef: "refs/pulls/1/merge", + baseRef: "main", + owner: "owner", + repo: "repo", + sha: "abc123", + refName: "main", + compareFromPush: undefined, + compareFromPr: "HEAD~1", + }); + expect(exitCode).toBe(1); + assertInstall(); + assertDeepen(); + assertDiffAll(); +}); + function mockInstall(): () => void { jest.mocked(exec.exec).mockResolvedValueOnce(0); return () => diff --git a/src/action.ts b/src/action.ts index 5595cd4..b3e0b91 100644 --- a/src/action.ts +++ b/src/action.ts @@ -22,15 +22,31 @@ async function execCommand( export async function runAction( opticToken: string, githubToken: string, - additionalArgs: string | undefined, - standardsFail: string, - eventName: string | undefined, - headRef: string | undefined, - baseRef: string | undefined, - owner: string | undefined, - repo: string | undefined, - sha: string | undefined, - refName: string | undefined + { + additionalArgs, + standardsFail, + eventName, + headRef, + baseRef, + owner, + repo, + sha, + refName, + compareFromPush, + compareFromPr, + }: { + additionalArgs: string | undefined; + standardsFail: string; + eventName: string | undefined; + headRef: string | undefined; + baseRef: string | undefined; + owner: string | undefined; + repo: string | undefined; + sha: string | undefined; + refName: string | undefined; + compareFromPush: string | undefined; + compareFromPr: string | undefined; + } ): Promise { const failOnCheckError = standardsFail === "true"; @@ -57,19 +73,23 @@ export async function runAction( let from = ""; if (eventName === "pull_request") { - const fromBranch = baseRef || ""; - from = `origin/${fromBranch}`; + const fromBranch = compareFromPr ?? baseRef ?? ""; + const ref = await parseAndEnsureRef(fromBranch); - if (!(await ensureRef(fromBranch))) { + if (!ref) { core.error(`Unable to fetch ${from}`); return 1; } + from = ref; } else if (eventName === "push") { - from = "HEAD~1"; - if (!(await deepen())) { - core.error("Unable to fetch HEAD~1"); + const fromBranch = compareFromPush ?? "HEAD~1"; + const ref = await parseAndEnsureRef(fromBranch); + + if (!ref) { + core.error(`Unable to fetch ${from}`); return 1; } + from = ref; } if (from === "") { @@ -150,28 +170,29 @@ async function install() { ]); } -async function ensureRef(ref: string): Promise { - if ( - !(await execCommand("git", [ - "fetch", - "--no-tags", - "--depth=1", - "origin", - ref, - ])) - ) { - return false; - } - - return true; -} - -async function deepen(): Promise { - if (!(await execCommand("git", ["fetch", "--deepen=1"]))) { - return false; +async function parseAndEnsureRef(ref: string): Promise { + if (/^cloud:/i.test(ref)) { + return ref; + } else if (/^HEAD~/.test(ref)) { + const headCountRaw = ref.replace(/^HEAD~/, ""); + if (!(await execCommand("git", ["fetch", `--deepen=${headCountRaw}`]))) { + return false; + } + return ref; + } else { + if ( + !(await execCommand("git", [ + "fetch", + "--no-tags", + "--depth=1", + "origin", + ref, + ])) + ) { + return false; + } + return `origin/${ref}`; } - - return true; } async function diffAll( diff --git a/src/index.ts b/src/index.ts index a485629..0222fa7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,8 @@ const opticToken = core.getInput("optic_token"); const githubToken = core.getInput("github_token"); const standardsFail = core.getInput("standards_fail"); const additionalArgs = core.getInput("additional_args"); +const compareFromPush = core.getInput("compare_from_push"); +const compareFromPr = core.getInput("compare_from_pr"); const eventName = process.env.GITHUB_EVENT_NAME; const headRef = process.env.GITHUB_REF; @@ -14,9 +16,7 @@ const owner = process.env.GITHUB_REPOSITORY_OWNER; const repo = process.env.GITHUB_REPOSITORY?.split("/")[1]; const sha = process.env.GITHUB_SHA; -runAction( - opticToken, - githubToken, +runAction(opticToken, githubToken, { additionalArgs, standardsFail, eventName, @@ -25,8 +25,10 @@ runAction( owner, repo, sha, - refName -) + refName, + compareFromPush, + compareFromPr, +}) .then((exitCode) => { return process.exit(exitCode); })