-
Notifications
You must be signed in to change notification settings - Fork 383
Expand file tree
/
Copy pathgh.js
More file actions
120 lines (101 loc) · 4.45 KB
/
gh.js
File metadata and controls
120 lines (101 loc) · 4.45 KB
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
const core = require('@actions/core');
const github = require('@actions/github');
const _ = require('lodash');
const config = require('./config');
// use the unique label to find the runner
// as we don't have the runner's id, it's not possible to get it in any other way
async function getRunner(label) {
const octokit = github.getOctokit(config.input.githubToken);
try {
const runners = await octokit.paginate('GET /repos/{owner}/{repo}/actions/runners', config.githubContext);
const foundRunners = _.filter(runners, { labels: [{ name: label }] });
return foundRunners.length > 0 ? foundRunners[0] : null;
} catch (error) {
return null;
}
}
// get GitHub Registration Token for registering a self-hosted runner
async function getRegistrationToken() {
const octokit = github.getOctokit(config.input.githubToken);
try {
const response = await octokit.request('POST /repos/{owner}/{repo}/actions/runners/registration-token', config.githubContext);
core.info('GitHub Registration Token is received');
return response.data.token;
} catch (error) {
core.error('GitHub Registration Token receiving error');
throw error;
}
}
function isRetryableError(error) {
if (!error.status) return false;
// Retry on server errors and rate limits
return error.status >= 500 || error.status === 429;
}
async function removeRunner() {
const runner = await getRunner(config.input.label);
const octokit = github.getOctokit(config.input.githubToken);
// skip the runner removal process if the runner is not found
if (!runner) {
core.info(`GitHub self-hosted runner with label ${config.input.label} is not found, so the removal is skipped`);
return;
}
const maxRetries = 3;
const baseDelayMs = 1000;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
await octokit.request('DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}', _.merge(config.githubContext, { runner_id: runner.id }));
core.info(`GitHub self-hosted runner ${runner.name} is removed`);
return;
} catch (error) {
const isLastAttempt = attempt === maxRetries;
// If runner not found, consider it success (already removed)
if (error.status === 404) {
core.info(`GitHub self-hosted runner ${runner.name} was already removed`);
return;
}
if (!isRetryableError(error) || isLastAttempt) {
core.error(`GitHub self-hosted runner removal error after ${attempt} attempts (HTTP ${error.status}): ${error.message || error}`);
throw error;
}
// Exponential backoff with jitter
const delayMs = baseDelayMs * Math.pow(2, attempt - 1) + Math.random() * 1000;
core.info(`GitHub runner removal attempt ${attempt} failed (${error.status}), retrying in ${Math.round(delayMs)}ms...`);
await new Promise(r => setTimeout(r, delayMs));
}
}
}
async function waitForRunnerRegistered(label) {
const timeoutMinutes = parseInt(config.input.startupTimeoutMinutes) || 5;
const retryIntervalSeconds = parseInt(config.input.startupRetryIntervalSeconds) || 10;
const quietPeriodSeconds = parseInt(config.input.startupQuietPeriodSeconds) || 30;
core.info(`Waiting ${quietPeriodSeconds}s for the AWS EC2 instance to be registered in GitHub as a new self-hosted runner`);
await new Promise((r) => setTimeout(r, quietPeriodSeconds * 1000));
core.info(`Checking every ${retryIntervalSeconds}s if the GitHub self-hosted runner is registered`);
core.info(`The maximum waiting time is ${timeoutMinutes} minutes`);
const startTime = Date.now();
const timeoutMs = timeoutMinutes * 60 * 1000;
return new Promise((resolve, reject) => {
const interval = setInterval(async () => {
const elapsedMs = Date.now() - startTime;
const runner = await getRunner(label);
if (runner && runner.status === 'online') {
core.info(`GitHub self-hosted runner ${runner.name} is registered and ready to use`);
clearInterval(interval);
resolve();
} else if (elapsedMs >= timeoutMs) {
core.error('GitHub self-hosted runner registration error');
clearInterval(interval);
reject(
`A timeout of ${timeoutMinutes} minutes is exceeded. Your AWS EC2 instance was not able to register itself in GitHub as a new self-hosted runner.`,
);
} else {
core.info('Checking...');
}
}, retryIntervalSeconds * 1000);
});
}
module.exports = {
getRegistrationToken,
removeRunner,
waitForRunnerRegistered,
};