diff --git a/README.md b/README.md index 8ef4964..ef3dc5b 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ jobs: last-successful-event: "" # The path where your repository is. This is only required for cases where the repository code is checked out or moved to a specific path. + # The property can also contain several types separated by comma i.e. schedule,workflow_dispatch # # Default: . working-directory: "" diff --git a/action.yml b/action.yml index 1bfdd97..c17e7fb 100644 --- a/action.yml +++ b/action.yml @@ -16,7 +16,7 @@ inputs: 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" + description: "The type of event to check for the last successful commit corresponding to that workflow-id, e.g. push or pull_request or release. The property can also contain several types separated by a comma. E.g. schedule,workflow_dispatch." default: "push" working-directory: description: "The directory where your repository is located" diff --git a/dist/index.js b/dist/index.js index 7d60c65..e1e2b4f 100644 --- a/dist/index.js +++ b/dist/index.js @@ -37914,6 +37914,9 @@ 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`); if (fallbackSHA) { BASE_SHA = fallbackSHA; process.stdout.write(`Using provided fallback SHA: ${fallbackSHA}\n`); @@ -37978,21 +37981,49 @@ function findSuccessfulCommit(workflow_id, run_id, owner, repo, branch, lastSucc process.stdout.write("\n"); process.stdout.write(`Workflow Id not provided. Using workflow '${workflow_id}'\n`); } - // fetch all workflow runs on a given repo/branch/workflow with push and success - const shas = yield octokit - .request(`GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, { - owner, - repo, - // on some workflow runs we do not have branch property - branch: lastSuccessfulEvent === "push" || - lastSuccessfulEvent === "workflow_dispatch" - ? branch - : undefined, - workflow_id, - event: lastSuccessfulEvent, - status: "success", - }) - .then(({ data: { workflow_runs } }) => workflow_runs.map((run) => run.head_sha)); + let shas = []; + // if there are several events separated by comma + if (lastSuccessfulEvent.includes(",")) { + // find the greatest workflow id among the types and retrieve the sha + const events = lastSuccessfulEvent.split(","); + const workflowIdMap = new Map(); + for (const event of events) { + const workflowRun = yield octokit + .request(`GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, { + owner, + repo, + // on non-push workflow runs we do not have branch property + branch: lastSuccessfulEvent === "push" || + lastSuccessfulEvent === "workflow_dispatch" + ? branch + : undefined, + event, + workflow_id, + }) + .then(({ data }) => { + return data.workflow_runs[0]; + }); + workflowIdMap.set(workflowRun.id, workflowRun.head_sha); + } + shas.push(workflowIdMap.get(Math.max(...workflowIdMap.keys()))); + } + else { + // fetch all workflow runs on a given repo/branch/workflow with push and success + shas = yield octokit + .request(`GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, { + owner, + repo, + // on non-push workflow runs we do not have branch property + branch: lastSuccessfulEvent === "push" || + lastSuccessfulEvent === "workflow_dispatch" + ? branch + : undefined, + workflow_id, + event: lastSuccessfulEvent, + status: "success", + }) + .then(({ data: { workflow_runs } }) => workflow_runs.map((run) => run.head_sha)); + } return yield findExistingCommit(octokit, branch, shas); }); } diff --git a/find-successful-workflow.ts b/find-successful-workflow.ts index 0504758..34923fe 100644 --- a/find-successful-workflow.ts +++ b/find-successful-workflow.ts @@ -30,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` ); } } @@ -50,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) { @@ -65,7 +65,7 @@ let BASE_SHA: string; owner, repo, mainBranchName, - lastSuccessfulEvent, + lastSuccessfulEvent ); } catch (e) { core.setFailed(e.message); @@ -79,29 +79,36 @@ let BASE_SHA: string; } 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`, + `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` ); 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`, + `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`, + `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" }, + { encoding: "utf-8" } ).stdout; const commitCount = parseInt( stripNewLineEndings(commitCountOutput), - 10, + 10 ); const LAST_COMMIT_CMD = `origin/${mainBranchName}${ @@ -117,7 +124,7 @@ let BASE_SHA: string; } 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`); } @@ -154,7 +161,7 @@ async function findSuccessfulCommit( owner: string, repo: string, branch: string, - lastSuccessfulEvent: string, + lastSuccessfulEvent: string ): Promise { const octokit = new ProxifiedClient(); if (!workflow_id) { @@ -168,30 +175,61 @@ 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 - const shas = await octokit - .request( - `GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, - { - owner, - repo, - // on some workflow runs we do not have branch property - branch: - lastSuccessfulEvent === "push" || - lastSuccessfulEvent === "workflow_dispatch" - ? branch - : undefined, - workflow_id, - event: lastSuccessfulEvent, - status: "success", - }, - ) - .then(({ data: { workflow_runs } }) => - workflow_runs.map((run: { head_sha: any }) => run.head_sha), - ); + let shas = []; + // if there are several events separated by comma + if (lastSuccessfulEvent.includes(",")) { + // find the greatest workflow id among the types and retrieve the sha + const events = lastSuccessfulEvent.split(","); + const workflowIdMap = new Map(); + for (const event of events) { + const workflowRun = await octokit + .request( + `GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, + { + owner, + repo, + // on non-push workflow runs we do not have branch property + branch: + lastSuccessfulEvent === "push" || + lastSuccessfulEvent === "workflow_dispatch" + ? branch + : undefined, + event, + workflow_id, + } + ) + .then(({ data }) => { + return data.workflow_runs[0]; + }); + workflowIdMap.set(workflowRun.id, workflowRun.head_sha); + } + shas.push(workflowIdMap.get(Math.max(...workflowIdMap.keys()))); + } else { + // fetch all workflow runs on a given repo/branch/workflow with push and success + shas = await octokit + .request( + `GET /repos/${owner}/${repo}/actions/workflows/${workflow_id}/runs`, + { + owner, + repo, + // on non-push workflow runs we do not have branch property + branch: + lastSuccessfulEvent === "push" || + lastSuccessfulEvent === "workflow_dispatch" + ? branch + : undefined, + workflow_id, + event: lastSuccessfulEvent, + status: "success", + } + ) + .then(({ data: { workflow_runs } }) => + workflow_runs.map((run: { head_sha: any }) => run.head_sha) + ); + } return await findExistingCommit(octokit, branch, shas); } @@ -208,7 +246,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; } @@ -223,7 +261,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; } @@ -234,7 +272,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)) { @@ -250,7 +288,7 @@ async function findExistingCommit( async function commitExists( octokit: Octokit, branchName: string, - commitSha: string, + commitSha: string ): Promise { try { spawnSync("git", ["cat-file", "-e", commitSha], { @@ -273,7 +311,7 @@ async function commitExists( }); return commits.data.some( - (commit: { sha: string }) => commit.sha === commitSha, + (commit: { sha: string }) => commit.sha === commitSha ); } catch { return false;