From 5ea577a062f9c2d7a542543457ac2e88b45eb4e5 Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 3 Feb 2021 15:37:31 -0600 Subject: [PATCH] Add command to publish preleases via CI ``` yarn publish-prereleases ``` Script to trigger a CircleCI workflow that publishes preleases. **The CI workflow doesn't actually publish yet; it just runs and logs its inputs.** --- package.json | 3 +- scripts/release/publish-using-ci-workflow.js | 117 +++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 scripts/release/publish-using-ci-workflow.js diff --git a/package.json b/package.json index bca998508f1d..ab63cd182a61 100644 --- a/package.json +++ b/package.json @@ -139,6 +139,7 @@ "prettier-all": "node ./scripts/prettier/index.js write", "version-check": "node ./scripts/tasks/version-check.js", "merge-fork": "node ./scripts/merge-fork/merge-fork.js", - "replace-fork": "node ./scripts/merge-fork/replace-fork.js" + "replace-fork": "node ./scripts/merge-fork/replace-fork.js", + "publish-prereleases": "node ./scripts/release/publish-using-ci-workflow.js" } } diff --git a/scripts/release/publish-using-ci-workflow.js b/scripts/release/publish-using-ci-workflow.js new file mode 100644 index 000000000000..c9202baa2d7b --- /dev/null +++ b/scripts/release/publish-using-ci-workflow.js @@ -0,0 +1,117 @@ +'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') + ); + 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 up to three times before giving up. + for (let i = 0; i < 3; 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); + } + throw new Error('Failed to create CircleCI workflow.'); +} + +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 pipelineResponse = await fetch( + 'https://circleci.com/api/v2/project/github/facebook/react/pipeline', + { + method: 'post', + body: JSON.stringify({ + parameters: { + // TODO: Make commit SHA configurable + prerelease_commit_sha: 'master', + }, + }), + headers: { + 'Circle-Token': CIRCLE_TOKEN, + 'Content-Type': 'application/json', + }, + } + ); + + 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, + ); + + 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); +});