From faa8ab530007d5a333e4d1ad0cc0733995a717a3 Mon Sep 17 00:00:00 2001 From: Daniel Mason Date: Wed, 3 Mar 2021 11:48:17 +0000 Subject: [PATCH 1/6] Add ability to cancel newer runs --- dist/index.js | 3 ++- src/index.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 4f442ae6..1e9f8287 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); @@ -5894,7 +5895,7 @@ 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)); + (cancel_newer || new Date(run.created_at) < new Date(current_run.created_at))); console.log(`with ${runningWorkflows.length} runs to cancel.`); for (const { id, head_sha, status, html_url } of runningWorkflows) { console.log('Canceling run: ', { id, head_sha, status, html_url }); diff --git a/src/index.ts b/src/index.ts index 1c9b2a27..7ae7d7f1 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); @@ -66,7 +67,7 @@ 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) + (cancel_newer || new Date(run.created_at) < new Date(current_run.created_at)) ); console.log(`with ${runningWorkflows.length} runs to cancel.`); From 3f37479d87047de9854642c1d1eb37023f01896c Mon Sep 17 00:00:00 2001 From: Daniel Mason Date: Wed, 3 Mar 2021 11:56:34 +0000 Subject: [PATCH 2/6] Add documentation for cancel_newer --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) 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 From 55b35f9a16bbbb54515c7b7f1fec333659d2bd36 Mon Sep 17 00:00:00 2001 From: Daniel Mason Date: Wed, 3 Mar 2021 12:15:41 +0000 Subject: [PATCH 3/6] Fix missed workflow input, also fixed format against GHA schema default is a string and the required key is required --- action.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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' From 01c36dd7343634d33fd6731b9cc6ba7dd386a815 Mon Sep 17 00:00:00 2001 From: Daniel Mason Date: Wed, 3 Mar 2021 15:44:55 +0000 Subject: [PATCH 4/6] Fix jobs cancelling themselves when cancel_newer is used --- dist/index.js | 1 + src/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/dist/index.js b/dist/index.js index 1e9f8287..49bef774 100644 --- a/dist/index.js +++ b/dist/index.js @@ -5895,6 +5895,7 @@ 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' && + 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.`); for (const { id, head_sha, status, html_url } of runningWorkflows) { diff --git a/src/index.ts b/src/index.ts index 7ae7d7f1..bd8cf145 100644 --- a/src/index.ts +++ b/src/index.ts @@ -67,6 +67,7 @@ async function main() { const runningWorkflows = branchWorkflows.filter(run => (ignore_sha || run.head_sha !== headSha) && run.status !== 'completed' && + 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.`); From 73aeefe301814fb1ff35f18b8c0b76b6542f7c10 Mon Sep 17 00:00:00 2001 From: Daniel Mason Date: Wed, 3 Mar 2021 17:22:26 +0000 Subject: [PATCH 5/6] Pull in as many jobs as possible without major changes, default was 30 --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index bd8cf145..87a64436 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,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 From 1eb771413923b70d5378d3fcb76e87ff77cead48 Mon Sep 17 00:00:00 2001 From: Daniel Mason Date: Wed, 3 Mar 2021 17:48:39 +0000 Subject: [PATCH 6/6] =?UTF-8?q?Cancel=20jobs=20faster,=20wait=20later=20?= =?UTF-8?q?=F0=9F=9F=A6=F0=9F=A6=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/index.js | 9 +++++++-- src/index.ts | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/dist/index.js b/dist/index.js index 49bef774..19bdcd00 100644 --- a/dist/index.js +++ b/dist/index.js @@ -5885,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, @@ -5898,15 +5899,19 @@ async function main() { 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 87a64436..a93607d8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -73,15 +73,20 @@ async function main() { ); 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}`);