cyclejs/cycle-core

View on GitHub
.scripts/check-release.js

Summary

Maintainability
A
2 hrs
Test Coverage
#!/usr/bin/env node

/** This script checks whether each package should be released with
 * a new version according to ComVer https://github.com/staltz/comver.
 * It has two modes: REPORT and ORACLE.
 *
 * It runs in REPORT mode if no additional command line argument was given.
 * For instance, `node check-release.js`. It will display a human readable
 * report on which packages should have new releases.
 *
 * It runs in ORACLE mode is an argument was provided, e.g.,
 * `node check-release.js xstream-run`,
 * it will exit with a status code answering whether the `xstream-run`
 * package should be released with a new version.
 * 0 means no new release is necessary
 * 2 means it should have a new minor version _.x release
 * 3 means it should have a new major version x._ release
 */

var conventionalChangelog = require('conventional-changelog');
var fs = require('fs');

var theCommitThatStartedTheMonorepo = fs
  .readFileSync(__dirname + '/SEED_COMMIT', 'utf8')
  .trim();

var packagesWithChangelog = fs
  .readFileSync(__dirname + '/RELEASABLE_PACKAGES', 'utf8')
  .trim()
  .split('\n');

var status = {};
packagesWithChangelog.forEach(function(package) {
  status[package] = {
    increment: 0, // 0 = nothing, 1 = patch, 2 = minor, 3 = major
    commits: [],
  };
});

function incrementName(code) {
  if (code === 1) {
    return 'patch';
  } else if (code === 2) {
    return 'minor';
  } else if (code === 3) {
    return 'major';
  } else {
    return '';
  }
}

function isCommitBreakingChange(commit) {
  return (
    typeof commit.footer === 'string' &&
    commit.footer.indexOf('BREAKING CHANGE') !== -1
  );
}

function showReportHeaderPositive() {
  console.log(
    'RELEASES TO DO\n\n' +
      'We checked all packages and recent commits, and discovered that\n' +
      'according to ComVer https://github.com/staltz/comver you should\n' +
      'release new versions for the following packages.\n'
  );
}

function showReportHeaderNegative() {
  console.log(
    'Nothing to release.\n\n' +
      'We checked all packages and recent commits, and discovered that\n' +
      'you do not need to release any new version, according to ComVer.'
  );
}

function showReport(status) {
  var headerShown = false;
  for (var package in status) {
    if (status.hasOwnProperty(package) && status[package].increment > 0) {
      if (!headerShown) {
        showReportHeaderPositive();
        headerShown = true;
      }

      console.log(
        '`' +
          package +
          '` needs a new ' +
          incrementName(status[package].increment).toUpperCase() +
          ' version released because:'
      );
      status[package].commits.forEach(function(commit) {
        console.log('  . ' + commit.header);
        if (isCommitBreakingChange(commit)) {
          console.log('    BREAKING CHANGE');
        }
      });
      console.log('');
    }
  }
  if (!headerShown) {
    showReportHeaderNegative();
  }
}

conventionalChangelog(
  {
    preset: 'angular',
    append: true,
    transform: function(commit, cb) {
      if (packagesWithChangelog.indexOf(commit.scope) === -1) {
        cb();
        return;
      }

      var package = commit.scope;
      var toPush = null;
      if (commit.type === 'fix' || commit.type === 'feat') {
        status[package].increment = Math.max(status[package].increment, 2);
        toPush = commit;
      }
      if (isCommitBreakingChange(commit)) {
        status[package].increment = Math.max(status[package].increment, 3);
        toPush = commit;
      }
      if (toPush) {
        status[package].commits.push(commit);
      }
      if (commit.type === 'release') {
        status[package].increment = 0;
        status[package].commits = [];
      }
      cb();
    },
  },
  {},
  {from: theCommitThatStartedTheMonorepo, reverse: true}
)
  .on('end', function() {
    // ORACLE mode
    var argPackage = process.argv[2];
    if (typeof argPackage === 'string' && argPackage.length > 0) {
      return process.exit(status[argPackage].increment);
    }
    // REPORT mode
    else {
      showReport(status);
    }
  })
  .resume();