unleash
Version:
Unleash your code into the wild yonder
204 lines (173 loc) • 6.48 kB
JavaScript
// force block-scoping w/ Node < 6
const
colors = require('chalk'),
log = require('fancy-log'),
paqman = require('paqman'),
vinylFS = require('vinyl-fs')
const currentVersion = paqman.packageJSON.version
function getNextVersion (type) {
const SemVer = require('semver')
return SemVer.inc(currentVersion, type)
}
function isLernaPackage () {
const Path = require('path')
const inPackageFolder = Path.dirname(process.cwd()).match(/packages$/)
if (!inPackageFolder)
return false
const FS = require('fs')
const inLernaRepo = FS.readdirSync('../../').indexOf('lerna.json') !== -1
return inLernaRepo
}
/**
* deploy module.
* @module lib/deploy
*/
module.exports = Object.create({
/**
* Obtain the next semantic version your project will use
*
* @param type - {String} the type of version increment to perform, e.g. "prerelease"
*
* @return `String`.
**/
getNextVersion: getNextVersion,
/**
* Kick-off remote side-effects of a version change such as NPM publish or git push w/ tags
*
* @param options0 - {Object} that contains some of the options for NPM &/or git deployment
* - bumpType: {String} the type of version increment to perform, e.g. "prerelease"
*
* @param options1 - {Object} that contains some of the options for NPM &/or git deployment
* - dryRun: {Boolean} a switch for turning on log-only "deployment",
* - publish: {Boolean} a switch for turning on NPM publishing (default to `true`),
* - push: {Boolean} a switch for turning on git pushing (default to `true`),
* - CL_STATUS: {String} the initial git status of the CHANGELOG, used for error recovery,
* - PKG_STATUS: {String} the initial git status of package.json, used for error recovery,
* - CURRENT_SHA: {String} the git commit hash prior to deploy, used for error recovery
*
* @return `Stream`.
**/
withBumpType: function (options0, options1) {
const
ChildProcess = require('child_process'),
merge = require('lodash.merge'),
bump = require('gulp-bump'),
filter = require('gulp-filter'),
git = require('gulp-git'),
tagVersion = require('gulp-tag-version')
// Allow options merge through arguments list.
// This is useful for bind magic
const options = merge({ }, options0, options1),
bumpType = options.bumpType,
dryRun = options.dryRun,
noPublish = !options.publish,
noPush = !options.push
const
REF_HEAD_CMD = 'git rev-parse --abbrev-ref HEAD',
CURRENT_BRANCH = ChildProcess.execSync(REF_HEAD_CMD)
.toString().replace('\n',''),
NEXT_VERSION = getNextVersion(bumpType)
if (!dryRun) {
log('Utilizing bumpType', colors.magenta(options.bumpType))
log('Retrieving latest git commit...')
log('Getting next version of type: ', colors.magenta(bumpType))
log('Incrementing from version: ', colors.magenta(currentVersion))
}
const fileStream = vinylFS.src([ './package.json' ])
const
args = ' --tags',
remote = 'origin',
releaseCommitMsg = `chore(release): release ${NEXT_VERSION}`
const wouldPublish = !noPublish
const wouldPublishButNotPush = noPush && wouldPublish
const wouldPush = !noPush
let tagVersionConfig, pkgName
const isLPkg = isLernaPackage()
if (isLPkg) {
pkgName = paqman.packageJSON.name
tagVersionConfig = {
prefix : `${pkgName}_v`,
label : `Tagging ${pkgName} release as %t`
}
}
const tagPrefix = tagVersionConfig ? tagVersionConfig.prefix : 'v'
function publish () {
log('Loading NPM....')
const NPM = require('npm')
NPM.load(() => { // must be called first
NPM.publish((pubError) => {
if (pubError) {
log.error('Publish failed')
log.error(pubError)
throw pubError
}
})
})
}
function revert () {
log(colors.bgRed(colors.black('~~~ ERROR ~~~')))
log('`git push` failed, reverting your unchanged files to their')
log(`state prior to running Unleash (${options.CURRENT_SHA})`)
ChildProcess.execSync(`git tag -d ${tagPrefix + NEXT_VERSION}`)
if (options.PKG_STATUS === '')
ChildProcess.execSync(`git checkout ${options.CURRENT_SHA} package.json`)
if (options.CL_STATUS === '')
ChildProcess.execSync(`git checkout ${options.CURRENT_SHA} CHANGELOG.md`)
ChildProcess.execSync(`git reset --mixed ${options.CURRENT_SHA}`)
}
function pushAndMaybePublish () {
return git.push(remote, CURRENT_BRANCH, { args: args }, (error) => {
if (error) {
revert()
throw error
}
if (!noPublish)
publish()
})
}
function warnLocalOnly () {
if (!options.publish && !options.push) {
const command = process.argv.slice(1).map((a) => {
return a.split('/').reverse()[0]
}).join(' ')
log(colors.bgYellow(colors.black('~~~ WARNING ~~~')))
log('You\'ve turned both publishing & pushing off. Your ' +
'changelog & package files will be updated + committed locally but ' +
'nothing outside your machine will change.')
log(command
.replace('publish',
colors.bgYellow('publish'))
.replace('push',
colors.bgYellow('push'))
)
}
}
if (isLPkg)
log(`Tagging ${pkgName} as a package in a Lerna repo`)
if (!dryRun) {
warnLocalOnly()
return fileStream
.pipe(bump({ type: bumpType }))
.pipe(vinylFS.dest('./'))
.pipe(git.commit(releaseCommitMsg))
.pipe(filter('package.json'))
.pipe(tagVersion(tagVersionConfig))
.on('end', () => {
if (wouldPublishButNotPush)
return publish()
else if (wouldPush)
return pushAndMaybePublish()
})
.on('error', console.error)
} else {
const pubLog = wouldPublish ? '* Publishing to NPM' : ''
const pushLog = wouldPush ? `* Pushing to ${remote} branch ${CURRENT_BRANCH}` : ''
log(`* Bumping to a ${bumpType} version with the commit "${releaseCommitMsg}"`)
log(`* Tagging the commit with "${tagPrefix}${NEXT_VERSION}"`)
pubLog && log(pubLog)
pushLog && log(pushLog)
warnLocalOnly()
return true
}
}
})