1. 'use strict';
    
  2. 
    
  3. const fetch = require('node-fetch');
    
  4. 
    
  5. const {logPromise} = require('./utils');
    
  6. const theme = require('./theme');
    
  7. 
    
  8. const CIRCLE_TOKEN = process.env.CIRCLE_CI_API_TOKEN;
    
  9. 
    
  10. if (!CIRCLE_TOKEN) {
    
  11.   console.error(
    
  12.     theme.error(
    
  13.       'Missing required environment variable: CIRCLE_CI_API_TOKEN\n' +
    
  14.         'Grab it here: https://app.circleci.com/settings/user/tokens'
    
  15.     )
    
  16.   );
    
  17.   process.exit(1);
    
  18. }
    
  19. 
    
  20. function sleep(ms) {
    
  21.   return new Promise(resolve => {
    
  22.     setTimeout(() => resolve(), ms);
    
  23.   });
    
  24. }
    
  25. 
    
  26. async function getPublishWorkflowID(pipelineID) {
    
  27.   // Since we just created the pipeline in a POST request, the server may 404.
    
  28.   // Try a few times before giving up.
    
  29.   for (let i = 0; i < 20; i++) {
    
  30.     const pipelineWorkflowsResponse = await fetch(
    
  31.       `https://circleci.com/api/v2/pipeline/${pipelineID}/workflow`
    
  32.     );
    
  33.     if (pipelineWorkflowsResponse.ok) {
    
  34.       const pipelineWorkflowsJSON = await pipelineWorkflowsResponse.json();
    
  35.       const workflows = pipelineWorkflowsJSON.items;
    
  36.       if (workflows.length !== 0) {
    
  37.         return workflows[0].id;
    
  38.       }
    
  39.     }
    
  40.     // CircleCI server may be stale. Wait a sec and try again.
    
  41.     await sleep(1000);
    
  42.   }
    
  43.   return null;
    
  44. }
    
  45. 
    
  46. async function pollUntilWorkflowFinishes(workflowID) {
    
  47.   while (true) {
    
  48.     const workflowResponse = await fetch(
    
  49.       `https://circleci.com/api/v2/workflow/${workflowID}`
    
  50.     );
    
  51.     const workflow = await workflowResponse.json();
    
  52.     switch (workflow.status) {
    
  53.       case 'running':
    
  54.         // Workflow still running. Wait a bit then check again.
    
  55.         await sleep(2000);
    
  56.         continue;
    
  57.       case 'success':
    
  58.         // Publish succeeded! Continue.
    
  59.         return;
    
  60.       case 'not_run':
    
  61.       case 'failed':
    
  62.       case 'error':
    
  63.       case 'failing':
    
  64.       case 'on_hold':
    
  65.       case 'canceled':
    
  66.       case 'unauthorized':
    
  67.       default:
    
  68.         console.error(
    
  69.           theme.error(
    
  70.             `Failed to publish. Workflow exited with status: ${workflow.status}`
    
  71.           )
    
  72.         );
    
  73.         console.error(
    
  74.           `Visit https://app.circleci.com/pipelines/workflows/${workflowID} for details.`
    
  75.         );
    
  76.         process.exit(1);
    
  77.         break;
    
  78.     }
    
  79.   }
    
  80. }
    
  81. 
    
  82. async function main() {
    
  83.   const headCommitResponse = await fetch(
    
  84.     'https://api.github.com/repos/facebook/react/commits/main'
    
  85.   );
    
  86.   const headCommitJSON = await headCommitResponse.json();
    
  87.   const headCommitSha = headCommitJSON.sha;
    
  88. 
    
  89.   const pipelineResponse = await fetch(
    
  90.     'https://circleci.com/api/v2/project/github/facebook/react/pipeline',
    
  91.     {
    
  92.       method: 'post',
    
  93.       body: JSON.stringify({
    
  94.         parameters: {
    
  95.           prerelease_commit_sha: headCommitSha,
    
  96.         },
    
  97.       }),
    
  98.       headers: {
    
  99.         'Circle-Token': CIRCLE_TOKEN,
    
  100.         'Content-Type': 'application/json',
    
  101.       },
    
  102.     }
    
  103.   );
    
  104. 
    
  105.   if (!pipelineResponse.ok) {
    
  106.     console.error(
    
  107.       theme.error(
    
  108.         `Failed to access CircleCI. Responded with status: ${pipelineResponse.status}`
    
  109.       )
    
  110.     );
    
  111.     process.exit(1);
    
  112.   }
    
  113. 
    
  114.   const pipelineJSON = await pipelineResponse.json();
    
  115.   const pipelineID = pipelineJSON.id;
    
  116. 
    
  117.   const workflowID = await logPromise(
    
  118.     getPublishWorkflowID(pipelineID),
    
  119.     theme`{header Creating CI workflow}`,
    
  120.     2 * 1000 // Estimated time: 2 seconds,
    
  121.   );
    
  122. 
    
  123.   if (workflowID === null) {
    
  124.     console.warn(
    
  125.       theme.yellow(
    
  126.         'Created a CI pipeline to publish the packages, but the script timed ' +
    
  127.           "out when requesting the associated workflow ID. It's still " +
    
  128.           'possible the workflow was created.\n\n' +
    
  129.           'Visit ' +
    
  130.           'https://app.circleci.com/pipelines/github/facebook/react?branch=main ' +
    
  131.           'for a list of the latest workflows.'
    
  132.       )
    
  133.     );
    
  134.     process.exit(1);
    
  135.   }
    
  136. 
    
  137.   await logPromise(
    
  138.     pollUntilWorkflowFinishes(workflowID),
    
  139.     theme`{header Publishing in CI workflow}: https://app.circleci.com/pipelines/workflows/${workflowID}`,
    
  140.     2 * 60 * 1000 // Estimated time: 2 minutes,
    
  141.   );
    
  142. }
    
  143. 
    
  144. main().catch(error => {
    
  145.   console.error(theme.error('Failed to trigger publish workflow.'));
    
  146.   console.error(error.message);
    
  147.   process.exit(1);
    
  148. });