Skip to content

Commit 614c19b

Browse files
committed
[ci] Add polling to download-build-artifacts
Several CI workflows depend on the runtime_build_and_test.yml workflow to complete before it can successfully download the build artifact. However it is possible to encounter a race condition where the build hasn't completed when the new workflow is started. This PR adds a simple polling mechanism that waits up to 6 minutes for the build for that revision to complete. ghstack-source-id: 6e0b685 Pull Request resolved: #30515
1 parent 878ca3e commit 614c19b

File tree

1 file changed

+79
-37
lines changed

1 file changed

+79
-37
lines changed

scripts/release/shared-commands/download-build-artifacts.js

Lines changed: 79 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ const GITHUB_HEADERS = `
2121
-H "Authorization: Bearer ${process.env.GH_TOKEN}" \
2222
-H "X-GitHub-Api-Version: 2022-11-28"`.trim();
2323

24+
function sleep(ms) {
25+
return new Promise(resolve => setTimeout(resolve, ms));
26+
}
27+
2428
function getWorkflowId() {
2529
if (
2630
existsSync(join(__dirname, `../../../.github/workflows/${WORKFLOW_ID}`))
@@ -33,7 +37,7 @@ function getWorkflowId() {
3337
}
3438
}
3539

36-
async function getWorkflowRunId(commit) {
40+
async function getWorkflowRun(commit) {
3741
const res = await exec(
3842
`curl -L ${GITHUB_HEADERS} https://api.github.com/repos/${OWNER}/${REPO}/actions/workflows/${getWorkflowId()}/runs?head_sha=${commit}&branch=main&exclude_pull_requests=true`
3943
);
@@ -55,7 +59,7 @@ async function getWorkflowRunId(commit) {
5559
process.exit(1);
5660
}
5761

58-
return workflowRun.id;
62+
return workflowRun;
5963
}
6064

6165
async function getArtifact(workflowRunId, artifactName) {
@@ -84,43 +88,81 @@ async function getArtifact(workflowRunId, artifactName) {
8488
}
8589

8690
async function downloadArtifactsFromGitHub(commit, releaseChannel) {
87-
const workflowRunId = await getWorkflowRunId(commit);
88-
const artifact = await getArtifact(workflowRunId, 'artifacts_combined');
89-
90-
// Download and extract artifact
91-
const cwd = join(__dirname, '..', '..', '..');
92-
await exec(`rm -rf ./build`, {cwd});
93-
await exec(
94-
`curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} \
95-
> a.zip && unzip a.zip -d . && rm a.zip build2.tgz && tar -xvzf build.tgz && rm build.tgz`,
96-
{
97-
cwd,
91+
const workflowRun = await getWorkflowRun(commit);
92+
let retries = 0;
93+
// wait up to 6 mins for build to finish: 6 * 60 * 1_000) / 30_000 = 12
94+
while (retries < 12) {
95+
if (typeof workflowRun.status === 'string') {
96+
switch (workflowRun.status) {
97+
case 'queued':
98+
case 'in_progress':
99+
case 'waiting': {
100+
retries++;
101+
console.log(theme`Build still in progress, waiting 30s...`);
102+
await sleep(30_000);
103+
break;
104+
}
105+
case 'completed': {
106+
if (workflowRun.conclusion === 'success') {
107+
const artifact = await getArtifact(
108+
workflowRun.id,
109+
'artifacts_combined'
110+
);
111+
112+
// Download and extract artifact
113+
const cwd = join(__dirname, '..', '..', '..');
114+
await exec(`rm -rf ./build`, {cwd});
115+
await exec(
116+
`curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} \
117+
> a.zip && unzip a.zip -d . && rm a.zip build2.tgz && tar -xvzf build.tgz && rm build.tgz`,
118+
{
119+
cwd,
120+
}
121+
);
122+
123+
// Copy to staging directory
124+
// TODO: Consider staging the release in a different directory from the CI
125+
// build artifacts: `./build/node_modules` -> `./staged-releases`
126+
if (!existsSync(join(cwd, 'build'))) {
127+
await exec(`mkdir ./build`, {cwd});
128+
} else {
129+
await exec(`rm -rf ./build/node_modules`, {cwd});
130+
}
131+
let sourceDir;
132+
// TODO: Rename release channel to `next`
133+
if (releaseChannel === 'stable') {
134+
sourceDir = 'oss-stable';
135+
} else if (releaseChannel === 'experimental') {
136+
sourceDir = 'oss-experimental';
137+
} else if (releaseChannel === 'rc') {
138+
sourceDir = 'oss-stable-rc';
139+
} else if (releaseChannel === 'latest') {
140+
sourceDir = 'oss-stable-semver';
141+
} else {
142+
console.error(
143+
'Internal error: Invalid release channel: ' + releaseChannel
144+
);
145+
process.exit(releaseChannel);
146+
}
147+
await exec(`cp -r ./build/${sourceDir} ./build/node_modules`, {
148+
cwd,
149+
});
150+
return;
151+
} else {
152+
console.log(
153+
theme`{error Could not download build for ${commit} from GitHub as its conclusion was: ${workflowRun.conclusion}}`
154+
);
155+
process.exit(1);
156+
}
157+
}
158+
}
98159
}
99-
);
100-
101-
// Copy to staging directory
102-
// TODO: Consider staging the release in a different directory from the CI
103-
// build artifacts: `./build/node_modules` -> `./staged-releases`
104-
if (!existsSync(join(cwd, 'build'))) {
105-
await exec(`mkdir ./build`, {cwd});
106-
} else {
107-
await exec(`rm -rf ./build/node_modules`, {cwd});
108160
}
109-
let sourceDir;
110-
// TODO: Rename release channel to `next`
111-
if (releaseChannel === 'stable') {
112-
sourceDir = 'oss-stable';
113-
} else if (releaseChannel === 'experimental') {
114-
sourceDir = 'oss-experimental';
115-
} else if (releaseChannel === 'rc') {
116-
sourceDir = 'oss-stable-rc';
117-
} else if (releaseChannel === 'latest') {
118-
sourceDir = 'oss-stable-semver';
119-
} else {
120-
console.error('Internal error: Invalid release channel: ' + releaseChannel);
121-
process.exit(releaseChannel);
122-
}
123-
await exec(`cp -r ./build/${sourceDir} ./build/node_modules`, {cwd});
161+
162+
console.log(
163+
theme`{error Could not download build for ${commit} from GitHub as it timed out}`
164+
);
165+
process.exit(1);
124166
}
125167

126168
async function downloadBuildArtifacts(commit, releaseChannel) {

0 commit comments

Comments
 (0)