This repository was archived by the owner on Aug 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcircle-wait.js
More file actions
125 lines (110 loc) · 3.63 KB
/
circle-wait.js
File metadata and controls
125 lines (110 loc) · 3.63 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
121
122
123
124
125
#!/usr/bin/env node
const fetch = require('node-fetch');
main();
function usage() {
console.warn('Usage: (ENVVARS)', process.argv0);
console.warn();
console.warn('* Required envvars (configure in project settings):');
console.warn(' CIRCLE_API_KEY=asdf...');
console.warn('* CircleCI envvars (required, but already in Circle CI 2.0 build envs):');
console.warn(' CIRCLE_PROJECT_USERNAME=org-user');
console.warn(' CIRCLE_PROJECT_REPONAME=my-repo');
console.warn(' CIRCLE_BUILD_NUM=123');
console.warn('* Optional envvars:');
console.warn(' CIRCLE_PROJECT_VCS=github');
}
function main() {
const waitOpts = {
// Repo identification.
apiKey: process.env.CIRCLE_API_KEY,
vcs: process.env.CIRCLE_PROJECT_VCS,
user: process.env.CIRCLE_PROJECT_USERNAME,
repo: process.env.CIRCLE_PROJECT_REPONAME,
// Job identification.
job: process.env.CIRCLE_BUILD_NUM,
// Scheduling. TODO: Make options.
maxParallel: 1,
checkInterval: 1000, // 1 second.
timeout: 30 * 60 * 1000, // 30 minutes.
};
if (!waitOpts.apiKey || !waitOpts.user || !waitOpts.repo || !waitOpts.job) {
usage();
process.exit(1);
}
waitOpts.job = parseInt(waitOpts.job, 10);
if (!(waitOpts.job > 0)) {
console.warn('CIRCLE_BUILD_NUM should be an integer greater than zero.');
usage();
process.exit(0);
}
if (!waitOpts.vcs) {
waitOpts.vcs = 'github';
console.warn('Envvar CIRCLE_PROJECT_VCS not set. Default into github.');
}
waitForCI(waitOpts)
.then(function() {
console.log('Our turn! 🎉');
process.exit(0);
})
.catch(function(e) {
console.warn(e);
process.exit(1);
});
}
/**
* waitForCI returns a promise which resolves when the given job is at the head
* of the build queue.
*/
function waitForCI(opts) {
return Promise.race([
// Time out waiting for build opportunity.
new Promise(function(_, reject) {
setTimeout(reject.bind(this, 'Timed out after ' + opts.timeout + ' milliseconds.'), opts.timeout);
}),
// Resolve once this is the latest build.
new Promise(function(resolve, reject) {
const tick = ticker();
const interval = setInterval(check, opts.checkInterval);
check();
function check() {
jobNumbers(opts)
.then(function(jobs) {
// Check whether we need to wait for longer.
if (jobs.length >= opts.maxParallel) {
const queueTail = jobs[opts.maxParallel - 1];
if (queueTail < opts.job) {
console.log(tick() + ' ' + opts.maxParallel + '/' + opts.maxParallel + ' jobs are running, up to job ' + queueTail + '. ' + (opts.job - queueTail) + ' to go. Waiting...');
return;
}
}
// Start the build.
clearInterval(interval);
resolve();
})
.catch(function(e) {
reject(e);
});
}
})
]);
}
/**
* jobNumbers returns the numbers of all running jobs.
*/
function jobNumbers(opts) {
const url = 'https://circleci.com/api/v1.1/project/' + encodeURIComponent(opts.vcs) + '/' + encodeURIComponent(opts.user) + '/' + encodeURIComponent(opts.repo) + '?filter=running';
return fetch(url + '&circle-token=' + encodeURIComponent(opts.apiKey))
.then(res => res.json())
.then(function(jobs) {
jobs = jobs.map(j => j.build_num);
jobs.sort();
return jobs;
});
}
function ticker() {
let ticks = ['🕛', '🕐', '🕑', '🕒', '🕓', '🕔', '🕕', '🕖', '🕗', '🕘', '🕙', '🕚'];
return function() {
ticks = ticks.slice(1).concat(ticks[0]);
return ticks[0];
}
}