release.js 5.28 KB
const { utils } = require('umi');
const { join, relative, resolve, sep } = require('path');
const exec = require('./utils/exec');
const getPackages = require('./utils/getPackages');
const isNextVersion = require('./utils/isNextVersion');

const { yParser, execa, chalk } = utils;
const cwd = process.cwd();
const args = yParser(process.argv);
const lernaCli = require.resolve('lerna/cli');

function printErrorAndExit(message) {
  console.error(chalk.red(message));
  process.exit(1);
}

function logStep(name) {
  console.log(`${chalk.gray('>> Release:')} ${chalk.magenta.bold(name)}`);
}

function packageExists({ name, version }) {
  const { stdout } = execa.sync('npm', ['info', `${name}@${version}`]);
  return stdout.length > 0;
}

async function release() {
  let pkgList = execa.sync(lernaCli, ['list', '--json']).stdout;
  pkgList = JSON.parse(pkgList);
  const locMap = {};
  pkgList.forEach((item) => {
    const location = item.location.split(sep);
    const componentName = item.name.split('/')[1];
    locMap[componentName] = `${location[location.length - 2]}/${location[location.length - 1]}`;
  });
  // Check git status
  if (!args.skipGitStatusCheck) {
    const gitStatus = execa.sync('git', ['status', '--porcelain']).stdout;
    if (gitStatus.length) {
      printErrorAndExit(`Your git status is not clean. Aborting.`);
    }
  } else {
    logStep('git status check is skipped, since --skip-git-status-check is supplied');
  }

  // Check npm registry
  logStep('check npm registry');
  const userRegistry = execa.sync('npm', ['config', 'get', 'registry']).stdout;
  if (userRegistry.includes('https://registry.yarnpkg.com/')) {
    printErrorAndExit(`Release failed, please use ${chalk.blue('npm run release')}.`);
  }
  // https://g.civnet.cn:4873
  // if (!userRegistry.includes('https://registry.npmjs.org/')) {
  //   const registry = chalk.blue('https://registry.npmjs.org/');
  //   printErrorAndExit(`Release failed, npm registry must be ${registry}.`);
  // }

  if (!userRegistry.includes('https://g.civnet.cn:4873/')) {
    const registry = chalk.blue('https://g.civnet.cn:4873/');
    printErrorAndExit(`Release failed, npm registry must be ${registry}.`);
  }
  let updated = null;

  if (!args.publishOnly) {
    // Get updated packages
    logStep('check updated packages');
    const updatedStdout = execa.sync(lernaCli, ['changed']).stdout;
    console.log('updatedStdout', updatedStdout);
    updated = updatedStdout
      .split('\n')
      .map((pkg) => {
        return pkg.split('/')[1];
      })
      .filter(Boolean);
    if (!updated.length) {
      printErrorAndExit('Release failed, no updated package is updated.');
    }

    // Clean
    logStep('clean');

    // Build
    if (!args.skipBuild) {
      logStep('build');
      try {
        await exec('npm', ['run', 'build']);
      } catch (error) {
        console.log(error);
      }
    } else {
      logStep('build is skipped, since args.skipBuild is supplied');
    }

    // Bump version
    // Commit
    // Git Tag
    // Push
    logStep('bump version with lerna version');
    const conventionalGraduate = args.conventionalGraduate
      ? ['--conventional-graduate'].concat(
          Array.isArray(args.conventionalGraduate) ? args.conventionalGraduate.join(',') : [],
        )
      : [];
    const conventionalPrerelease = args.conventionalPrerelease
      ? ['--conventional-prerelease'].concat(
          Array.isArray(args.conventionalPrerelease) ? args.conventionalPrerelease.join(',') : [],
        )
      : [];
    await exec(
      lernaCli,
      [
        'version',
        '--exact',
        // '--no-commit-hooks',
        // '--no-git-tag-version',
        // '--no-push',
        '--message',
        '🎨 chore(release): Publish',
        '--conventional-commits',
      ]
        .concat(conventionalGraduate)
        .concat(conventionalPrerelease),
    );
  }

  // Publish
  // Umi must be the latest.
  const pkgs = args.publishOnly ? getPackages() : updated;
  logStep(`publish packages: ${chalk.blue(pkgs.join(', '))}`);

  // eslint-disable-next-line consistent-return,array-callback-return
  const publishList = pkgs.map((pkg, index) => {
    const pkgMap = locMap[pkg];
    const [p, shortName] = pkgMap.split('/');
    const pkgPath = join(cwd, 'packages', p, shortName.replace('pro-', ''));
    // eslint-disable-next-line global-require,import/no-dynamic-require
    const { name, version } = require(join(pkgPath, 'package.json'));
    if (name === 'parseform') return false;
    const isNext = isNextVersion(version);
    let isPackageExist = null;
    if (args.publishOnly) {
      isPackageExist = packageExists({ name, version });
      if (isPackageExist) {
        console.log(`package ${name}@${version} is already exists on npm, skip.`);
      }
    }
    if (!args.publishOnly || !isPackageExist) {
      console.log(
        `[${index + 1}/${pkgs.length}] Publish package ${name} ${isNext ? 'with next tag' : ''}`,
      );
      const cliArgs = isNext ? ['publish', '--tag', 'next'] : ['publish', '--tag', 'beta'];
      return execa('npm', cliArgs, {
        cwd: pkgPath,
      });
    }
  });

  console.log(`发布中${pkgs.join('/')}`);
  await Promise.all(publishList);
  console.log('发布成功! ');
  await exec('npm', ['run', 'prettier']);

  logStep('done');
}

release().catch((err) => {
  console.error(err);
  process.exit(1);
});