1. #!/usr/bin/env node
    
  2. 
    
  3. 'use strict';
    
  4. 
    
  5. const chalk = require('chalk');
    
  6. const {exec} = require('child-process-promise');
    
  7. const {readJsonSync} = require('fs-extra');
    
  8. const inquirer = require('inquirer');
    
  9. const {join, relative} = require('path');
    
  10. const {DRY_RUN, NPM_PACKAGES, ROOT_PATH} = require('./configuration');
    
  11. const {
    
  12.   checkNPMPermissions,
    
  13.   clear,
    
  14.   confirm,
    
  15.   execRead,
    
  16.   logger,
    
  17.   readSavedBuildMetadata,
    
  18. } = require('./utils');
    
  19. 
    
  20. // This is the primary control function for this script.
    
  21. async function main() {
    
  22.   clear();
    
  23. 
    
  24.   await confirm('Have you run the build-and-test script?', () => {
    
  25.     const buildAndTestScriptPath = join(__dirname, 'build-and-test.js');
    
  26.     const pathToPrint = relative(process.cwd(), buildAndTestScriptPath);
    
  27. 
    
  28.     console.log('Begin by running the build-and-test script:');
    
  29.     console.log(chalk.bold.green('  ' + pathToPrint));
    
  30.   });
    
  31. 
    
  32.   const {archivePath, buildID} = readSavedBuildMetadata();
    
  33. 
    
  34.   await checkNPMPermissions();
    
  35. 
    
  36.   await publishToNPM();
    
  37. 
    
  38.   await printFinalInstructions(buildID, archivePath);
    
  39. }
    
  40. 
    
  41. async function printFinalInstructions(buildID, archivePath) {
    
  42.   console.log('');
    
  43.   console.log(
    
  44.     'You are now ready to publish the extension to Chrome, Edge, and Firefox:'
    
  45.   );
    
  46.   console.log(
    
  47.     `  ${chalk.blue.underline(
    
  48.       'https://fburl.com/publish-react-devtools-extensions'
    
  49.     )}`
    
  50.   );
    
  51.   console.log('');
    
  52.   console.log('When publishing to Firefox, remember the following:');
    
  53.   console.log(`  Build id: ${chalk.bold(buildID)}`);
    
  54.   console.log(`  Git archive: ${chalk.bold(archivePath)}`);
    
  55.   console.log('');
    
  56.   console.log('Also consider syncing this release to Facebook:');
    
  57.   console.log(`  ${chalk.bold.green('js1 upgrade react-devtools')}`);
    
  58. }
    
  59. 
    
  60. async function publishToNPM() {
    
  61.   const {otp} = await inquirer.prompt([
    
  62.     {
    
  63.       type: 'input',
    
  64.       name: 'otp',
    
  65.       message: 'Please provide an NPM two-factor auth token:',
    
  66.     },
    
  67.   ]);
    
  68. 
    
  69.   console.log('');
    
  70. 
    
  71.   if (!otp) {
    
  72.     console.error(chalk.red(`Invalid OTP provided: "${chalk.bold(otp)}"`));
    
  73.     process.exit(0);
    
  74.   }
    
  75. 
    
  76.   for (let index = 0; index < NPM_PACKAGES.length; index++) {
    
  77.     const npmPackage = NPM_PACKAGES[index];
    
  78.     const packagePath = join(ROOT_PATH, 'packages', npmPackage);
    
  79.     const {version} = readJsonSync(join(packagePath, 'package.json'));
    
  80. 
    
  81.     // Check if this package version has already been published.
    
  82.     // If so we might be resuming from a previous run.
    
  83.     // We could infer this by comparing the build-info.json,
    
  84.     // But for now the easiest way is just to ask if this is expected.
    
  85.     const info = await execRead(`npm view ${npmPackage}@${version}`)
    
  86.       // Early versions of npm view gives empty response, but newer versions give 404 error.
    
  87.       // Catch the error to keep it consistent.
    
  88.       .catch(childProcessError => {
    
  89.         if (childProcessError.stderr.startsWith('npm ERR! code E404')) {
    
  90.           return null;
    
  91.         }
    
  92. 
    
  93.         throw childProcessError;
    
  94.       });
    
  95. 
    
  96.     if (info) {
    
  97.       console.log('');
    
  98.       console.log(
    
  99.         `${npmPackage} version ${chalk.bold(
    
  100.           version
    
  101.         )} has already been published.`
    
  102.       );
    
  103. 
    
  104.       await confirm(`Is this expected (will skip ${npmPackage}@${version})?`);
    
  105.       continue;
    
  106.     }
    
  107. 
    
  108.     if (DRY_RUN) {
    
  109.       console.log(`Publishing package ${chalk.bold(npmPackage)}`);
    
  110.       console.log(chalk.dim(`  npm publish --otp=${otp}`));
    
  111.     } else {
    
  112.       const publishPromise = exec(`npm publish --otp=${otp}`, {
    
  113.         cwd: packagePath,
    
  114.       });
    
  115. 
    
  116.       await logger(
    
  117.         publishPromise,
    
  118.         `Publishing package ${chalk.bold(npmPackage)}`,
    
  119.         {
    
  120.           estimate: 2500,
    
  121.         }
    
  122.       );
    
  123.     }
    
  124.   }
    
  125. }
    
  126. 
    
  127. main();