'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); });