require("shelljs/global");

var readline = require("readline-sync");
var semver   = require("semver");
var chalk    = require("chalk");
var child    = require("child_process");
var async    = require("async");
var fs       = require("fs");

//

var PACKAGE_LOC = __dirname + "/../packages";
var VERSION_LOC = __dirname + "/../VERSION";

var NPM_OWNERS = fs.readFileSync(__dirname + "/../NPM_OWNERS", "utf8").trim().split("\n");
var changedPackages = [];
var changedFiles = [VERSION_LOC];

var CURRENT_VERSION = fs.readFileSync(VERSION_LOC, "utf8").trim();
console.log("Current version:", CURRENT_VERSION);

var FORCE_VERSION = process.env.FORCE_VERSION;
FORCE_VERSION = FORCE_VERSION ? FORCE_VERSION.split(",") : [];

var NEW_VERSION = getVersion();
fs.writeFileSync(VERSION_LOC, NEW_VERSION, "utf8");

//

try {
  checkUpdatedPackages();
  updateChangedPackages();
  updateTag();
  build();
  publish();
} catch (err) {
  onError(err);
}

//

function updateTag() {
  var NEW_TAG_NAME = "v" + NEW_VERSION;
  execSync("git commit -m " + NEW_TAG_NAME, true);
  execSync("git tag " + NEW_TAG_NAME, true);
}

function build() {
  execSync("make build-dist");
}

function getVersion() {
  var input = readline.question("New version (Leave blank for new patch): ");

  var ver = semver.valid(input);
  if (!ver) {
    ver = semver.inc(CURRENT_VERSION, input || "patch");
  }

  if (ver) {
    return ver;
  } else {
    console.log("Version provided is not valid semver.");
    return getVersion();
  }
}

function execSync(cmd, log) {
  var out = child.execSync(cmd, {
    encoding: "utf8"
  }).trim();

  console.log("$ " + cmd);

  if (log) {
    if (out) console.log(out);
  } else {
    return out;
  }
}

function getPackageLocation(name) {
  return PACKAGE_LOC + "/" + name;
}

function getPackageConfig(name) {
  return require(getPackageLocation(name) + "/package.json");
}

function updateDepsObject(deps) {
  for (var depName in deps) {
    // ensure this was generated and we're on the same major
    if (deps[depName][0] !== "^" || deps[depName][1] !== NEW_VERSION[0]) continue;

    if (changedPackages.indexOf(depName) >= 0) {
      deps[depName] = "^" + NEW_VERSION;
    }
  }
}

function checkUpdatedPackages() {
  var packageNames = fs.readdirSync(PACKAGE_LOC).filter(function (name) {
    return name[0] !== "." && fs.statSync(PACKAGE_LOC + "/" + name).isDirectory();
  });

  var lastTagCommit = execSync("git rev-list --tags --max-count=1");
  var lastTag       = execSync("git describe " + lastTagCommit);

  packageNames.forEach(function (name) {
    var config = getPackageConfig(name);
    if (config.private) return;

    // check if package has changed since last release
    var diff = execSync("git diff " + lastTag + " -- " + getPackageLocation(name));
    if (diff || FORCE_VERSION.indexOf("*") >= 0 || FORCE_VERSION.indexOf(name) >= 0) {
      console.log(chalk.green("Changes detected to package", name));
      changedPackages.push(name);
    }
  });

  if (!changedPackages.length && !FORCE_VERSION.length) {
    throw new Error(chalk.red("No packages changed."));
  }

  if (changedPackages.indexOf("babel-browser") < 0) {
    changedPackages.push("babel-browser");
  }
}

function updateChangedPackages() {
  changedPackages.forEach(function (name) {
    var pkgLoc = getPackageLocation(name) + "/package.json";
    var pkg = require(pkgLoc);

    // set new version
    pkg.version = NEW_VERSION;

    // updated dependencies
    updateDepsObject(pkg.dependencies);
    updateDepsObject(pkg.devDependencies);

    // write new package
    fs.writeFileSync(pkgLoc, JSON.stringify(pkg, null, "  ") + "\n");

    // push to be git committed
    changedFiles.push(pkgLoc);
  });

  changedFiles.forEach(function (loc) {
    execSync("git add " + loc, true);
  });
}

function publish() {
  changedPackages.forEach(function (name) {
    // prepublish script
    var prePub = getPackageLocation(name) + "/scripts/prepublish.js";
    if (fs.existsSync(prePub)) require(prePub);
  });

  async.parallelLimit(changedPackages.map(function (name) {
    return function (done) {
      var loc = getPackageLocation(name);

      child.exec("cd " + loc + " && npm publish --tag prerelease", function (err, stdout, stderr) {
        if (err || stderr) return done(err || stderr);

        console.log(stdout);

        // postpublish script
        var postPub = loc + "/scripts/postpublish.js";
        if (fs.existsSync(postPub)) require(postPub);

        done();
      });
    };
  }), 4, function (err) {
    onError(err);
    ship();
  });
}

function onError(err) {
  if (!err) return;
  console.log(chalk.red("There was a problem publishing."));
  console.log(err.stack || err);
  process.exit(1);
}

function ship() {
  async.parallelLimit(changedPackages.map(function (name) {
    return function (done) {
      var loc = getPackageLocation(name);
      execSync("npm dist-tag rm " + name + " prerelease", true);
      execSync("npm dist-tag add " + name + "@" + NEW_VERSION + " stable");
    };
  }), 4, function (err) {
    onError(err);
    execSync("git push", true);
    execSync("git push --tags", true);
    console.log(chalk.green("Successfully shipped " + NEW_VERSION));
    process.exit();
  });
}

@@ -937,16 +937,41 @@
 %0A%7D%0A%0A//%0A%0A
+var createdTag = false;%0A%0A
 function
@@ -1121,16 +1121,317 @@
  true);%0A
+  createdTag = true;%0A%7D%0A%0Afunction removeTag() %7B%0A  console.error(chalk.red(%22Rolling back version file.%22));%0A  fs.writeFileSync(VERSION_LOC, NEW_VERSION, %22utf8%22);%0A%0A  if (createdTag) %7B%0A    console.error(chalk.red(%22Attempting to roll back tag creation.%22));%0A    exec(%22git tag -d v%22 + NEW_VERSION, true);%0A  %7D%0A
 %7D%0A%0Afunct
@@ -4644,16 +4644,23 @@
 g(stdout
+.trim()
 );%0A%0A    
@@ -4998,16 +4998,31 @@
 ng.%22));%0A
+  removeTag();%0A
   consol

clean up tag creation and removal