diff --git a/README.md b/README.md index 4ece3ae..8ef4964 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,11 @@ jobs: # Default: false error-on-no-successful-workflow: "" + # Fallback SHA to use if no successful workflow run is found. This can be useful in scenarios where you need a specific commit as a reference for comparison, especially in newly set up repositories or those with sparse workflow runs. + # + # Default: "" + fallback-sha: "" + # The type of event to check for the last successful commit corresponding to that workflow-id, e.g. push, pull_request, release etc. # # Default: push diff --git a/action.yml b/action.yml index b6690d4..1bfdd97 100644 --- a/action.yml +++ b/action.yml @@ -11,6 +11,10 @@ inputs: error-on-no-successful-workflow: description: "By default, if no successful workflow is found on the main branch to determine the SHA, we will log a warning and use HEAD~1. Enable this option to error and exit instead." default: "false" + fallback-sha: + description: "Fallback SHA to use if no successful workflow run is found." + required: false + default: "" last-successful-event: description: "The type of event to check for the last successful commit corresponding to that workflow-id, e.g. push, pull_request, release etc" default: "push" diff --git a/dist/index.js b/dist/index.js index 55d2d3b..2f46a59 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37867,6 +37867,7 @@ const errorOnNoSuccessfulWorkflow = process.argv[4]; const lastSuccessfulEvent = process.argv[5]; const workingDirectory = process.argv[6]; const workflowId = process.argv[7]; +const fallbackSHA = process.argv[8]; const defaultWorkingDirectory = "."; const ProxifiedClient = action_1.Octokit.plugin(proxyPlugin); let BASE_SHA; @@ -37913,18 +37914,24 @@ let BASE_SHA; else { process.stdout.write("\n"); process.stdout.write(`WARNING: Unable to find a successful workflow run on 'origin/${mainBranchName}', or the latest successful workflow was connected to a commit which no longer exists on that branch (e.g. if that branch was rebased)\n`); - process.stdout.write(`We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'\n`); - process.stdout.write("\n"); - process.stdout.write(`NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the action in your workflow.\n`); - process.stdout.write("\n"); - const commitCountOutput = (0, child_process_1.spawnSync)("git", ["rev-list", "--count", `origin/${mainBranchName}`], { encoding: "utf-8" }).stdout; - const commitCount = parseInt(stripNewLineEndings(commitCountOutput), 10); - const LAST_COMMIT_CMD = `origin/${mainBranchName}${commitCount > 1 ? "~1" : ""}`; - const baseRes = (0, child_process_1.spawnSync)("git", ["rev-parse", LAST_COMMIT_CMD], { - encoding: "utf-8", - }); - BASE_SHA = baseRes.stdout; - core.setOutput("noPreviousBuild", "true"); + if (fallbackSHA) { + BASE_SHA = fallbackSHA; + process.stdout.write(`Using provided fallback SHA: ${fallbackSHA}\n`); + } + else { + process.stdout.write(`We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'\n`); + process.stdout.write("\n"); + process.stdout.write(`NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the action in your workflow.\n`); + process.stdout.write("\n"); + const commitCountOutput = (0, child_process_1.spawnSync)("git", ["rev-list", "--count", `origin/${mainBranchName}`], { encoding: "utf-8" }).stdout; + const commitCount = parseInt(stripNewLineEndings(commitCountOutput), 10); + const LAST_COMMIT_CMD = `origin/${mainBranchName}${commitCount > 1 ? "~1" : ""}`; + const baseRes = (0, child_process_1.spawnSync)("git", ["rev-parse", LAST_COMMIT_CMD], { + encoding: "utf-8", + }); + BASE_SHA = baseRes.stdout; + core.setOutput("noPreviousBuild", "true"); + } } } else { diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index a0e1ee1..fd95d2f 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -17,6 +17,7 @@ const errorOnNoSuccessfulWorkflow = process.argv[4]; const lastSuccessfulEvent = process.argv[5]; const workingDirectory = process.argv[6]; const workflowId = process.argv[7]; +const fallbackSHA = process.argv[8]; const defaultWorkingDirectory = "."; const ProxifiedClient = Octokit.plugin(proxyPlugin); @@ -29,7 +30,7 @@ let BASE_SHA: string; } else { process.stdout.write("\n"); process.stdout.write( - `WARNING: Working directory '${workingDirectory}' doesn't exist.\n` + `WARNING: Working directory '${workingDirectory}' doesn't exist.\n`, ); } } @@ -49,7 +50,7 @@ let BASE_SHA: string; const baseResult = spawnSync( "git", ["merge-base", `origin/${mainBranchName}`, mergeBaseRef], - { encoding: "utf-8" } + { encoding: "utf-8" }, ); BASE_SHA = baseResult.stdout; } catch (e) { @@ -64,7 +65,7 @@ let BASE_SHA: string; owner, repo, mainBranchName, - lastSuccessfulEvent + lastSuccessfulEvent, ); } catch (e) { core.setFailed(e.message); @@ -76,42 +77,47 @@ let BASE_SHA: string; reportFailure(mainBranchName); return; } else { - process.stdout.write( "\n"); - process.stdout.write( - `WARNING: Unable to find a successful workflow run on 'origin/${mainBranchName}', or the latest successful workflow was connected to a commit which no longer exists on that branch (e.g. if that branch was rebased)\n` - ); - process.stdout.write( - `We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'\n` - ); process.stdout.write("\n"); process.stdout.write( - `NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the action in your workflow.\n` + `WARNING: Unable to find a successful workflow run on 'origin/${mainBranchName}', or the latest successful workflow was connected to a commit which no longer exists on that branch (e.g. if that branch was rebased)\n`, ); - process.stdout.write("\n"); + if (fallbackSHA) { + BASE_SHA = fallbackSHA; + process.stdout.write(`Using provided fallback SHA: ${fallbackSHA}\n`); + } else { + process.stdout.write( + `We are therefore defaulting to use HEAD~1 on 'origin/${mainBranchName}'\n`, + ); + process.stdout.write("\n"); + process.stdout.write( + `NOTE: You can instead make this a hard error by setting 'error-on-no-successful-workflow' on the action in your workflow.\n`, + ); + process.stdout.write("\n"); - const commitCountOutput = spawnSync( - "git", - ["rev-list", "--count", `origin/${mainBranchName}`], - { encoding: "utf-8" } - ).stdout; - const commitCount = parseInt( - stripNewLineEndings(commitCountOutput), - 10 - ); + const commitCountOutput = spawnSync( + "git", + ["rev-list", "--count", `origin/${mainBranchName}`], + { encoding: "utf-8" }, + ).stdout; + const commitCount = parseInt( + stripNewLineEndings(commitCountOutput), + 10, + ); - const LAST_COMMIT_CMD = `origin/${mainBranchName}${ - commitCount > 1 ? "~1" : "" - }`; - const baseRes = spawnSync("git", ["rev-parse", LAST_COMMIT_CMD], { - encoding: "utf-8", - }); - BASE_SHA = baseRes.stdout; - core.setOutput("noPreviousBuild", "true"); + const LAST_COMMIT_CMD = `origin/${mainBranchName}${ + commitCount > 1 ? "~1" : "" + }`; + const baseRes = spawnSync("git", ["rev-parse", LAST_COMMIT_CMD], { + encoding: "utf-8", + }); + BASE_SHA = baseRes.stdout; + core.setOutput("noPreviousBuild", "true"); + } } } else { process.stdout.write("\n"); process.stdout.write( - `Found the last successful workflow run on 'origin/${mainBranchName}'\n` + `Found the last successful workflow run on 'origin/${mainBranchName}'\n`, ); process.stdout.write(`Commit: ${BASE_SHA}\n`); } @@ -148,7 +154,7 @@ async function findSuccessfulCommit( owner: string, repo: string, branch: string, - lastSuccessfulEvent: string + lastSuccessfulEvent: string, ): Promise { const octokit = new ProxifiedClient(); if (!workflow_id) { @@ -162,7 +168,7 @@ async function findSuccessfulCommit( .then(({ data: { workflow_id } }) => workflow_id); process.stdout.write("\n"); process.stdout.write( - `Workflow Id not provided. Using workflow '${workflow_id}'\n` + `Workflow Id not provided. Using workflow '${workflow_id}'\n`, ); } // fetch all workflow runs on a given repo/branch/workflow with push and success @@ -177,10 +183,10 @@ async function findSuccessfulCommit( workflow_id, event: lastSuccessfulEvent, status: "success", - } + }, ) .then(({ data: { workflow_runs } }) => - workflow_runs.map((run: { head_sha: any }) => run.head_sha) + workflow_runs.map((run: { head_sha: any }) => run.head_sha), ); return await findExistingCommit(octokit, branch, shas); @@ -198,7 +204,7 @@ async function findMergeBaseRef(): Promise { function findMergeQueuePr(): string { const { head_ref, base_sha } = github.context.payload.merge_group; const result = new RegExp( - `^refs/heads/gh-readonly-queue/${mainBranchName}/pr-(\\d+)-${base_sha}$` + `^refs/heads/gh-readonly-queue/${mainBranchName}/pr-(\\d+)-${base_sha}$`, ).exec(head_ref); return result ? result.at(1) : undefined; } @@ -213,7 +219,7 @@ async function findMergeQueueBranch(): Promise { const octokit = new ProxifiedClient(); const result = await octokit.request( `GET /repos/${owner}/${repo}/pulls/${pull_number}`, - { owner, repo, pull_number: +pull_number } + { owner, repo, pull_number: +pull_number }, ); return result.data.head.ref; } @@ -224,7 +230,7 @@ async function findMergeQueueBranch(): Promise { async function findExistingCommit( octokit: Octokit, branchName: string, - shas: string[] + shas: string[], ): Promise { for (const commitSha of shas) { if (await commitExists(octokit, branchName, commitSha)) { @@ -240,7 +246,7 @@ async function findExistingCommit( async function commitExists( octokit: Octokit, branchName: string, - commitSha: string + commitSha: string, ): Promise { try { spawnSync("git", ["cat-file", "-e", commitSha], { @@ -263,7 +269,7 @@ async function commitExists( }); return commits.data.some( - (commit: { sha: string }) => commit.sha === commitSha + (commit: { sha: string }) => commit.sha === commitSha, ); } catch { return false;