Skip to content

Commit

Permalink
Optionally cancel all workflows but the latest (#35)
Browse files Browse the repository at this point in the history
* Cancel all workflows but the latest


Co-authored-by: Steven <[email protected]>
thomwiggers and styfle authored Apr 11, 2021

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 3d86a7c commit 1adde81
Showing 3 changed files with 53 additions and 12 deletions.
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@ This includes runs with a [status](https://docs.github.com/en/rest/reference/che

## How does it work?

When you `git push`, this GitHub Action will capture the current Branch and SHA. It will query GitHub's API to find previous workflow runs that match the Branch but do not match the SHA. These in-progress runs will be canceled leaving only the latest run.
When you `git push`, this GitHub Action will capture the current Branch and SHA. It will query GitHub's API to find previous workflow runs that match the Branch but do not match the SHA. These in-progress runs will be canceled leaving only this run.

Read more about the [Workflow Runs API](https://docs.github.com/en/rest/reference/actions#workflow-runs).

@@ -100,6 +100,28 @@ jobs:
workflow_id: 479426
```

## Advanced: Cancel more recent workflows

Because this action can only cancel workflows if it is actually being run, it only helps if the pipeline isn't saturated and there are still runners available to schedule the workflow.
By default, this action does not cancel any workflows older than itself. The optional flag ``all_but_latest`` switches to a mode where the action also cancels itself and all later-scheduled workflows but the last one.

```yml
name: Cancel
on: [push]
jobs:
cancel:
name: 'Cancel Previous Runs'
runs-on: ubuntu-latest
timeout-minutes: 3
steps:
- uses: styfle/[email protected]
with:
all_but_latest: true
access_token: ${{ github.token }}
```

At the time of writing `0.8.0` is the latest release but you can select any [release](https://github.com/styfle/cancel-workflow-action/releases).

## Contributing

- Clone this repo
6 changes: 5 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@ name: 'Cancel Workflow Action'
description: 'This Action will cancel any previous runs that are not `completed` for a given workflow.'
author: styfle
branding:
icon: 'stop-circle'
icon: 'stop-circle'
color: 'white'
inputs:
workflow_id:
@@ -15,6 +15,10 @@ inputs:
access_token:
description: 'Your GitHub Access Token, defaults to: {{ github.token }}'
default: '${{ github.token }}'
required: true
all_but_latest:
description: "Cancel all actions but the last one"
required: false
runs:
using: 'node12'
main: 'dist/index.js'
35 changes: 25 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -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 all_but_latest = core.getInput('all_but_latest', { required: false });
console.log(`Found token: ${token ? 'yes' : 'no'}`);
const workflow_ids: string[] = [];
const octokit = github.getOctokit(token);
@@ -58,19 +59,22 @@ async function main() {
workflow_id,
branch,
});
console.log(`Found ${data.total_count} runs total.`);

const branchWorkflows = data.workflow_runs.filter(run => run.head_branch === branch);
console.log(`Found ${branchWorkflows.length} runs for workflow ${workflow_id} on branch ${branch}`);
console.log(branchWorkflows.map(run => `- ${run.html_url}`).join('\n'));
let cancel_before;
if (all_but_latest) {
cancel_before = new Date(data.workflow_runs.reduce((a, b) => Math.max(a.created_at, b.created_at)));
} else {
cancel_before = new Date(current_run.created_at);
}

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)
const runningWorkflows = data.workflow_runs.filter(
run => run.head_branch === branch && (ignore_sha || run.head_sha !== headSha) && run.status !== 'completed' &&
run != current_run &&
new Date(run.created_at) < cancel_before
);
console.log(`with ${runningWorkflows.length} runs to cancel.`);

for (const {id, head_sha, status, html_url} of runningWorkflows) {
console.log(`Found ${runningWorkflows.length} runs to cancel.`);
for (const {id, head_sha, status} of runningWorkflows) {
console.log('Canceling run: ', {id, head_sha, status, html_url});
const res = await octokit.actions.cancelWorkflowRun({
owner,
@@ -79,6 +83,17 @@ async function main() {
});
console.log(`Cancel run ${id} responded with status ${res.status}`);
}
// Make sure we cancel this run itself if it's out-of-date.
// We postponed canceling this run because otherwise we couldn't cancel the rest.
if (all_but_latest && new Date(current_run.created_at) < cancel_before) {
const id = current_run.id;
const res = await octokit.actions.cancelWorkflowRun({
owner,
repo,
run_id: id
});
console.log(`Cancel run ${id} responded with status ${res.status}`);
}
} catch (e) {
const msg = e.message || e;
console.log(`Error while canceling workflow_id ${workflow_id}: ${msg}`);

0 comments on commit 1adde81

Please sign in to comment.