forked from styfle/cancel-workflow-action
/
index.ts
147 lines (129 loc) · 4.87 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import * as core from '@actions/core';
import * as github from '@actions/github';
if (!github) {
throw new Error('Module not found: github');
}
if (!core) {
throw new Error('Module not found: core');
}
async function main(): Promise<void> {
const {
eventName,
sha,
ref,
repo: { owner, repo },
payload
} = github.context;
const { GITHUB_RUN_ID } = process.env;
let branch = ref.slice(11);
let headSha = sha;
if (payload.pull_request) {
branch = payload.pull_request.head.ref;
headSha = payload.pull_request.head.sha;
} else if (payload.workflow_run) {
branch = payload.workflow_run.head_branch;
headSha = payload.workflow_run.head_sha;
}
console.log({ eventName, sha, headSha, branch, owner, repo, GITHUB_RUN_ID });
const token = core.getInput('access_token', { required: true });
const workflow_id_input = 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 }) === 'true';
console.log(`Found token: ${token ? 'yes' : 'no'}`);
const workflow_ids: number[] = [];
const octokit = github.getOctokit(token);
const { data: current_run } = await octokit.actions.getWorkflowRun({
owner,
repo,
run_id: Number(GITHUB_RUN_ID)
});
if (workflow_id_input) {
// The user provided one or more workflow id
const workflow_ids_input = workflow_id_input.replace(/\s/g, '').split(',');
for (const id of workflow_ids_input) {
workflow_ids.push(parseInt(id, 10));
}
} else {
// The user did not provide workflow id so derive from current run
workflow_ids.push(current_run.workflow_id);
}
console.log(`Found workflow_id: ${JSON.stringify(workflow_ids)}`);
await Promise.all(
workflow_ids.map(async workflow_id => {
try {
const { data } = await octokit.actions.listWorkflowRuns({
per_page: 100,
owner,
repo,
workflow_id,
branch
});
let cancel_before: Date;
if (all_but_latest) {
cancel_before = new Date(
data.workflow_runs.reduce((a, b) =>
parseInt(a.created_at, 10) > parseInt(b.created_at, 10) ? a : b
).created_at
);
} else {
cancel_before = new Date(current_run.created_at);
}
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'));
// Filter for only uncompleted workflow runs
let runningWorkflows = branchWorkflows.filter(run => run.status !== 'completed');
// Filter out for only our headSha unless ignoring it
runningWorkflows = runningWorkflows.filter(run => ignore_sha || run.head_sha !== headSha);
// Filter workflow runs over time - retain either all before us, or just the latest
runningWorkflows = runningWorkflows.filter(run => {
// In all_but_latest mode, we must not cancel ourselves until we have canceled the rest
if (all_but_latest && run !== current_run) {
return new Date(run.created_at) < cancel_before;
} else {
return new Date(run.created_at) < new Date(current_run.created_at);
}
});
console.log(`with ${runningWorkflows.length} runs to cancel.`);
await cancelWorkflowRuns(runningWorkflows, owner, repo, token);
// In all_but_latest_mode, we may need to cancel ourselves as well.
// 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) {
await cancelWorkflowRuns([current_run], owner, repo, token);
}
} catch (e) {
const msg = e.message || e;
console.log(`Error while canceling workflow_id ${workflow_id}: ${msg}`);
}
console.log('');
})
);
}
async function cancelWorkflowRuns(
runningWorkflows: { id: number; head_sha: string; status: string; html_url: string }[],
owner: string,
repo: string,
token: string
): Promise<void> {
const octokit = github.getOctokit(token);
const promises = [];
for (const { id, head_sha, status, html_url } of runningWorkflows) {
console.log('Canceling run: ', { id, head_sha, status, html_url });
const current_promise = octokit.actions
.cancelWorkflowRun({
owner,
repo,
run_id: id
})
.then(res => {
console.log(`Cancel run ${id} responded with status ${res.status}`);
});
promises.push(current_promise);
}
await Promise.all(promises);
}
main()
.then(() => core.info('Cancel Complete.'))
.catch(e => core.setFailed(e.message));