-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
116 lines (106 loc) · 4.38 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
import * as core from '@actions/core';
import * as github from '@actions/github';
import { Octokit } from '@octokit/rest';
import {
extract as extractOlderThanInMs,
filterWorkflowRuns as filterWorkflowRunsOlderThan, WorkflowRun
} from './process_older_than.js';
type Extractor<T> = (data: string | boolean) => T
const getInput = <T, F extends T | T[] = T>(name: string, fallback?: F, extractor?: Extractor<T>): F extends T ? T : T[] => {
if (!extractor) {
extractor = (data) => data as T
}
if (Array.isArray(fallback)) {
const ml = core.getMultilineInput(name).map(extractor).filter(Boolean)
return (ml.length ? ml : fallback) as any
}
let input: string | boolean
try {
input = core.getBooleanInput(name)
} catch {
input = core.getInput(name)
}
if (!input) {
return fallback as any
}
return extractor(input) as any
}
const run2Id = (run: WorkflowRun): number => run.id
const run = async () => {
try {
const token = core.getInput('token') || process.env.GITHUB_TOKEN;
const deleteObsolete = getInput<boolean>('remove-obsolete', true)
const deleteByConclusion = {
// property names must match workflow run conclusions:
// see: https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#list-workflow-runs-for-a-repository
cancelled: getInput<boolean | string[], boolean>('remove-cancelled', false),
failure: getInput<boolean | string[], boolean>('remove-failed', false),
skipped: getInput<boolean | string[], boolean>('remove-skipped', false),
}
const deleteOlderThan = getInput('remove-older-than', [], extractOlderThanInMs)
const {owner, repo} = github.context.repo
const octokit = new Octokit({auth: token});
const workflowIds = await (octokit.paginate(
'GET /repos/{owner}/{repo}/actions/workflows',
{
repo: repo,
owner: owner,
per_page: 100,
},
page => page.data.map(workflow => workflow.id)
));
const workflowRuns = await octokit.paginate(
"GET /repos/{owner}/{repo}/actions/runs",
{
repo: repo,
owner: owner,
per_page: 100,
},
page => page.data.map(run => ({
conclusion: run.conclusion,
created_at: run.created_at,
id: run.id,
name: run.name,
workflow_id: run.workflow_id,
}))
)
const idsToDelete: number[] = []
if (deleteObsolete) {
const workflowRunIdsWithoutWorkflow = workflowRuns
.filter(run => !workflowIds.includes(run.workflow_id))
.map(run2Id)
core.info(`Found ${workflowRunIdsWithoutWorkflow.length} obsolete workflow runs.`)
idsToDelete.push(...workflowRunIdsWithoutWorkflow)
}
for (const conclusion in deleteByConclusion) {
const conclusionValue = deleteByConclusion[conclusion as keyof typeof deleteByConclusion]
if (conclusionValue) {
const idsToDeleteByStatus = workflowRuns
.filter(run => {
if (run.conclusion !== conclusion) { return false }
return conclusionValue === true || (run.name && conclusionValue.includes(run.name))
})
.map(run2Id)
core.info(`Found ${idsToDeleteByStatus.length} workflow runs with status [${conclusion}].`);
idsToDelete.push(...idsToDeleteByStatus)
}
}
if (deleteOlderThan.length) {
idsToDelete.push(...filterWorkflowRunsOlderThan(workflowRuns, deleteOlderThan).map(run2Id))
}
const uniqueRunIdsToDelete = Array.from(new Set(idsToDelete))
await Promise.all(uniqueRunIdsToDelete.map(run_id => octokit.request(
"DELETE /repos/{owner}/{repo}/actions/runs/{run_id}",
{
repo: repo,
owner: owner,
run_id: run_id,
})
.then(() => core.info("Removed run with id: " + run_id))
));
} catch (error) {
core.info('Error occurred: ' + JSON.stringify({error}))
core.error(JSON.stringify({error}))
}
}
void run()