#!/usr/bin/env node const execa = require('execa') const fetch = require('isomorphic-fetch') const fse = require('fs-extra') const registryUrl = require('registry-url') const semver = require('semver') const bin = 'node_modules/.bin/' const lernaBin = `${bin}lerna` const getPackages = require('./get-packages') const getReleaseVersion = require('./get-release-version') const revertPackages = require('./revert-packages') const PRERELEASE = 'prerelease' const DIST_TAG = 'rc' const PRIMER_CSS = 'primer-css' const RELEASE_VERSION = getReleaseVersion( PRIMER_CSS, process.env.TRAVIS_BRANCH ) const depFields = [ 'dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies', ] const getUpdated = (args) => { return execa(lernaBin, ['updated', '--json'], { reject: false }) .then(res => { if (res.stdout == "") { console.warn("No packages need updating") return [] } else { return JSON.parse(res.stdout) } }) .then(updated => updated.map(pkg => pkg.name)) } const notify = status => { return execa('script/notify', [status], {env: process.env}) .catch(error => { console.error('notify error:', error) }) } const writePackage = (pkg) => { const {dir} = pkg delete pkg.dir const json = JSON.stringify(pkg, null, ' ') + '\n' pkg.dir = dir return fse.writeFile(`${pkg.dir}/package.json`, json, 'utf8') .then(() => pkg) } const bump = (pkg, by, preid) => { if (pkg.name === PRIMER_CSS) { pkg.version = RELEASE_VERSION } const original = pkg.version let version = increment(pkg.version, by, preid) return getPackageInfo(pkg.name) .then(info => { while (version in info.versions) { version = increment(version, by, preid) } console.warn('%s %s -> %s', pkg.name, original, version) pkg.version = version return writePackage(pkg) }) } const getPackageInfo = (name) => { const url = registryUrl() + name return fetch(url).then(res => res.json()) } const increment = (version, by, preid) => { const {major, minor, patch} = semver(version) const prev = [major, minor, patch].join('.') const next = semver.inc(version, by, preid) // if this is a prerelease, "revert" major.minor.patch // so that only the prerelease id is incremented return by === PRERELEASE ? next.replace(/^\d+\.\d+\.\d+/, prev) : next } const updateDependants = (pkg, pkgs) => { return pkgs.filter(other => { return depFields.some(field => { if (other[field] && (pkg.name in other[field])) { other[field][pkg.name] = pkg.version return true } }) }) } revertPackages() .then(() => getPackages()) .then(dirs => { return dirs.map(dir => { const pkg = require(`../${dir}/package.json`) pkg.dir = dir return pkg }) }) .then(pkgs => { const by = PRERELEASE const preid = DIST_TAG return getUpdated() .then(updated => { console.warn('%d packages updated...', updated.length) return pkgs.filter(pkg => updated.includes(pkg.name)) }) .then(updated => { const changed = new Set(updated) return Promise.all(updated.map(pkg => { return bump(pkg, by, preid) .then(pkg => updateDependants(pkg, pkgs)) .then(dependants => { dependants.forEach(dep => changed.add(dep)) }) })) .then(() => { const tasks = Array.from(changed) .map(writePackage) return Promise.all(tasks) }) .then(updated => { const tasks = updated.map(pkg => { return execa('npm', ['publish', '--tag', DIST_TAG], { cwd: pkg.dir, stdio: 'inherit', }) }) return Promise.all(tasks) }) .then(() => notify('success')) }) }) .catch(error => { console.error('Error:', error) process.exitCode = 1 return notify('error') }) .then(() => process.exit())