Skip to content

Commit

Permalink
[ci] Add polling to download-build-artifacts
Browse files Browse the repository at this point in the history
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 10 minutes for
the build for that revision to complete.

ghstack-source-id: 6a954638a800fbea8081e6fba35ee4b4437731c5
Pull Request resolved: #30515
  • Loading branch information
poteto committed Jul 29, 2024
1 parent 076f16e commit e92a7c4
Showing 1 changed file with 86 additions and 37 deletions.
123 changes: 86 additions & 37 deletions scripts/release/shared-commands/download-build-artifacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const GITHUB_HEADERS = `
-H "Authorization: Bearer ${process.env.GH_TOKEN}" \
-H "X-GitHub-Api-Version: 2022-11-28"`.trim();

function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}

function getWorkflowId() {
if (
existsSync(join(__dirname, `../../../.github/workflows/${WORKFLOW_ID}`))
Expand All @@ -33,7 +37,7 @@ function getWorkflowId() {
}
}

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

return workflowRun.id;
return workflowRun;
}

async function getArtifact(workflowRunId, artifactName) {
Expand Down Expand Up @@ -84,43 +88,88 @@ async function getArtifact(workflowRunId, artifactName) {
}

async function downloadArtifactsFromGitHub(commit, releaseChannel) {
const workflowRunId = await getWorkflowRunId(commit);
const artifact = await getArtifact(workflowRunId, 'artifacts_combined');

// Download and extract artifact
const cwd = join(__dirname, '..', '..', '..');
await exec(`rm -rf ./build`, {cwd});
await exec(
`curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} \
> a.zip && unzip a.zip -d . && rm a.zip build2.tgz && tar -xvzf build.tgz && rm build.tgz`,
{
cwd,
const workflowRun = await getWorkflowRun(commit);
let retries = 0;
// wait up to 10 mins for build to finish: 10 * 60 * 1_000) / 30_000 = 20
while (retries < 20) {
if (typeof workflowRun.status === 'string') {
switch (workflowRun.status) {
case 'queued':
case 'in_progress':
case 'waiting': {
retries++;
console.log(theme`Build still in progress, waiting 30s...`);
await sleep(30_000);
break;
}
case 'completed': {
if (workflowRun.conclusion === 'success') {
const artifact = await getArtifact(
workflowRun.id,
'artifacts_combined'
);

// Download and extract artifact
const cwd = join(__dirname, '..', '..', '..');
await exec(`rm -rf ./build`, {cwd});
await exec(
`curl -L ${GITHUB_HEADERS} ${artifact.archive_download_url} \
> a.zip && unzip a.zip -d . && rm a.zip build2.tgz && tar -xvzf build.tgz && rm build.tgz`,
{
cwd,
}
);

// Copy to staging directory
// TODO: Consider staging the release in a different directory from the CI
// build artifacts: `./build/node_modules` -> `./staged-releases`
if (!existsSync(join(cwd, 'build'))) {
await exec(`mkdir ./build`, {cwd});
} else {
await exec(`rm -rf ./build/node_modules`, {cwd});
}
let sourceDir;
// TODO: Rename release channel to `next`
if (releaseChannel === 'stable') {
sourceDir = 'oss-stable';
} else if (releaseChannel === 'experimental') {
sourceDir = 'oss-experimental';
} else if (releaseChannel === 'rc') {
sourceDir = 'oss-stable-rc';
} else if (releaseChannel === 'latest') {
sourceDir = 'oss-stable-semver';
} else {
console.error(
'Internal error: Invalid release channel: ' + releaseChannel
);
process.exit(releaseChannel);
}
await exec(`cp -r ./build/${sourceDir} ./build/node_modules`, {
cwd,
});
return;
} else {
console.log(
theme`{error Could not download build for ${commit} from GitHub as its conclusion was: ${workflowRun.conclusion}}`
);
process.exit(1);
}
break;
}
default: {
console.log(
theme`{error Unhandled workflow run status: ${workflowRun.status}}`
);
process.exit(1);
}
}
}
);

// Copy to staging directory
// TODO: Consider staging the release in a different directory from the CI
// build artifacts: `./build/node_modules` -> `./staged-releases`
if (!existsSync(join(cwd, 'build'))) {
await exec(`mkdir ./build`, {cwd});
} else {
await exec(`rm -rf ./build/node_modules`, {cwd});
}
let sourceDir;
// TODO: Rename release channel to `next`
if (releaseChannel === 'stable') {
sourceDir = 'oss-stable';
} else if (releaseChannel === 'experimental') {
sourceDir = 'oss-experimental';
} else if (releaseChannel === 'rc') {
sourceDir = 'oss-stable-rc';
} else if (releaseChannel === 'latest') {
sourceDir = 'oss-stable-semver';
} else {
console.error('Internal error: Invalid release channel: ' + releaseChannel);
process.exit(releaseChannel);
}
await exec(`cp -r ./build/${sourceDir} ./build/node_modules`, {cwd});

console.log(
theme`{error Could not download build for ${commit} from GitHub as it timed out}`
);
process.exit(1);
}

async function downloadBuildArtifacts(commit, releaseChannel) {
Expand Down

0 comments on commit e92a7c4

Please sign in to comment.