react/scripts/release/publish-using-ci-workflow.js

149 lines
4.0 KiB
JavaScript

'use strict';
const fetch = require('node-fetch');
const {logPromise} = require('./utils');
const theme = require('./theme');
const CIRCLE_TOKEN = process.env.CIRCLE_CI_API_TOKEN;
if (!CIRCLE_TOKEN) {
console.error(
theme.error(
'Missing required environment variable: CIRCLE_CI_API_TOKEN\n' +
'Grab it here: https://app.circleci.com/settings/user/tokens'
)
);
process.exit(1);
}
function sleep(ms) {
return new Promise(resolve => {
setTimeout(() => resolve(), ms);
});
}
async function getPublishWorkflowID(pipelineID) {
// Since we just created the pipeline in a POST request, the server may 404.
// Try a few times before giving up.
for (let i = 0; i < 20; i++) {
const pipelineWorkflowsResponse = await fetch(
`https://circleci.com/api/v2/pipeline/${pipelineID}/workflow`
);
if (pipelineWorkflowsResponse.ok) {
const pipelineWorkflowsJSON = await pipelineWorkflowsResponse.json();
const workflows = pipelineWorkflowsJSON.items;
if (workflows.length !== 0) {
return workflows[0].id;
}
}
// CircleCI server may be stale. Wait a sec and try again.
await sleep(1000);
}
return null;
}
async function pollUntilWorkflowFinishes(workflowID) {
while (true) {
const workflowResponse = await fetch(
`https://circleci.com/api/v2/workflow/${workflowID}`
);
const workflow = await workflowResponse.json();
switch (workflow.status) {
case 'running':
// Workflow still running. Wait a bit then check again.
await sleep(2000);
continue;
case 'success':
// Publish succeeded! Continue.
return;
case 'not_run':
case 'failed':
case 'error':
case 'failing':
case 'on_hold':
case 'canceled':
case 'unauthorized':
default:
console.error(
theme.error(
`Failed to publish. Workflow exited with status: ${workflow.status}`
)
);
console.error(
`Visit https://app.circleci.com/pipelines/workflows/${workflowID} for details.`
);
process.exit(1);
break;
}
}
}
async function main() {
const headCommitResponse = await fetch(
'https://api.github.com/repos/facebook/react/commits/main'
);
const headCommitJSON = await headCommitResponse.json();
const headCommitSha = headCommitJSON.sha;
const pipelineResponse = await fetch(
'https://circleci.com/api/v2/project/github/facebook/react/pipeline',
{
method: 'post',
body: JSON.stringify({
parameters: {
prerelease_commit_sha: headCommitSha,
},
}),
headers: {
'Circle-Token': CIRCLE_TOKEN,
'Content-Type': 'application/json',
},
}
);
if (!pipelineResponse.ok) {
console.error(
theme.error(
`Failed to access CircleCI. Responded with status: ${pipelineResponse.status}`
)
);
process.exit(1);
}
const pipelineJSON = await pipelineResponse.json();
const pipelineID = pipelineJSON.id;
const workflowID = await logPromise(
getPublishWorkflowID(pipelineID),
theme`{header Creating CI workflow}`,
2 * 1000 // Estimated time: 2 seconds,
);
if (workflowID === null) {
console.warn(
theme.yellow(
'Created a CI pipeline to publish the packages, but the script timed ' +
"out when requesting the associated workflow ID. It's still " +
'possible the workflow was created.\n\n' +
'Visit ' +
'https://app.circleci.com/pipelines/github/facebook/react?branch=main ' +
'for a list of the latest workflows.'
)
);
process.exit(1);
}
await logPromise(
pollUntilWorkflowFinishes(workflowID),
theme`{header Publishing in CI workflow}: https://app.circleci.com/pipelines/workflows/${workflowID}`,
2 * 60 * 1000 // Estimated time: 2 minutes,
);
}
main().catch(error => {
console.error(theme.error('Failed to trigger publish workflow.'));
console.error(error.message);
process.exit(1);
});