diff --git a/README.md b/README.md index dc1de619..2f984bc8 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,29 @@ jobs: workflow_id: 479426 ``` +### Advanced: Cancel newer builds + +Under certain circumstances it could be beneficial to cancel _newer_ workflows. For example, if there are no code +changes, but you are pulling data from another source, there may be no need for subsequent builds that are currently +queued. Using the `cancel_newer` option will cancel _ALL_ other builds for the same workflow. + +```yml +on: + pull_request: + types: [closed] +jobs: + cleanup: + name: 'Clean up ALL other builds' + runs-on: ubuntu-latest + timeout-minutes: 3 + steps: + - name: Cancel ALL build runs + uses: styfle/cancel-workflow-action@0.9.0 + with: + ignore_sha: true + cancel_newer: true +``` + ## Contributing - Clone this repo diff --git a/action.yml b/action.yml index f3c505bd..846fb8c9 100644 --- a/action.yml +++ b/action.yml @@ -11,10 +11,15 @@ inputs: ignore_sha: description: 'Optional - Allow canceling other workflows with the same SHA. Useful for the `pull_request.closed` event.' required: false - default: false + default: 'false' + cancel_newer: + description: 'Cancel all queued and in progress workflows, even if they are newer' + required: false + default: 'false' access_token: description: 'Your GitHub Access Token, defaults to: {{ github.token }}' default: '${{ github.token }}' + required: true runs: using: 'node12' main: 'dist/index.js' diff --git a/dist/index.js b/dist/index.js index 4f442ae6..19bdcd00 100644 --- a/dist/index.js +++ b/dist/index.js @@ -5864,6 +5864,7 @@ async function main() { const token = core.getInput('access_token', { required: true }); const workflow_id = core.getInput('workflow_id', { required: false }); const ignore_sha = core.getInput('ignore_sha', { required: false }) === 'true'; + const cancel_newer = core.getInput('cancel_newer', { required: false }) === 'true'; console.log(`Found token: ${token ? 'yes' : 'no'}`); const workflow_ids = []; const octokit = github.getOctokit(token); @@ -5884,6 +5885,7 @@ async function main() { await Promise.all(workflow_ids.map(async (workflow_id) => { try { const { data } = await octokit.actions.listWorkflowRuns({ + per_page: 100, owner, repo, workflow_id, @@ -5894,17 +5896,22 @@ async function main() { console.log(branchWorkflows.map(run => `- ${run.html_url}`).join('\n')); const runningWorkflows = branchWorkflows.filter(run => (ignore_sha || run.head_sha !== headSha) && run.status !== 'completed' && - new Date(run.created_at) < new Date(current_run.created_at)); + run.id !== current_run.id && + (cancel_newer || new Date(run.created_at) < new Date(current_run.created_at))); console.log(`with ${runningWorkflows.length} runs to cancel.`); + const promises = []; for (const { id, head_sha, status, html_url } of runningWorkflows) { console.log('Canceling run: ', { id, head_sha, status, html_url }); - const res = await octokit.actions.cancelWorkflowRun({ + const current_promise = octokit.actions.cancelWorkflowRun({ owner, repo, run_id: id + }).then((res) => { + console.log(`Cancel run ${id} responded with status ${res.status}`); }); - console.log(`Cancel run ${id} responded with status ${res.status}`); + promises.push(current_promise); } + await Promise.all(promises); } catch (e) { const msg = e.message || e; diff --git a/src/index.ts b/src/index.ts index 1c9b2a27..a93607d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -27,6 +27,7 @@ async function main() { const token = core.getInput('access_token', { required: true }); const workflow_id = core.getInput('workflow_id', { required: false }); const ignore_sha = core.getInput('ignore_sha', { required: false }) === 'true'; + const cancel_newer = core.getInput('cancel_newer', { required: false }) === 'true'; console.log(`Found token: ${token ? 'yes' : 'no'}`); const workflow_ids: string[] = []; const octokit = github.getOctokit(token); @@ -52,6 +53,7 @@ async function main() { await Promise.all(workflow_ids.map(async (workflow_id) => { try { const { data } = await octokit.actions.listWorkflowRuns({ + per_page: 100, owner, repo, // @ts-ignore @@ -66,19 +68,25 @@ async function main() { const runningWorkflows = branchWorkflows.filter(run => (ignore_sha || run.head_sha !== headSha) && run.status !== 'completed' && - new Date(run.created_at) < new Date(current_run.created_at) + run.id !== current_run.id && // Do not cancel yourself + (cancel_newer || new Date(run.created_at) < new Date(current_run.created_at)) ); console.log(`with ${runningWorkflows.length} runs to cancel.`); + const promises = []; for (const {id, head_sha, status, html_url} of runningWorkflows) { console.log('Canceling run: ', {id, head_sha, status, html_url}); - const res = await octokit.actions.cancelWorkflowRun({ + const current_promise = octokit.actions.cancelWorkflowRun({ owner, repo, run_id: id + }).then((res) => { + console.log(`Cancel run ${id} responded with status ${res.status}`); }); - console.log(`Cancel run ${id} responded with status ${res.status}`); + promises.push(current_promise); } + await Promise.all(promises); + } catch (e) { const msg = e.message || e; console.log(`Error while canceling workflow_id ${workflow_id}: ${msg}`);