UNPKG

unleash

Version:

Unleash your code into the wild yonder

314 lines (263 loc) 9.02 kB
#!/usr/bin/env node 'use strict' // force block-scoping w/ Node < 6 /* Trick in-publish into thinking we're the NPM CLI running publish */ process.env.npm_config_argv = JSON.stringify({ cooked : [ 'publish' ] }) const ChildProcess = require('child_process'), Undertaker = require('undertaker'), colors = require('chalk'), fancyLog = require('fancy-log'), git = require('gulp-git'), merge = require('lodash.merge'), values = require('object-values'), vinylFS = require('vinyl-fs'), ghPages = require('./lib/gh-pages'), writeChangelog = require('./lib/changelog'), Deploy = require('./lib/deploy'), ls = require('./lib/ls') // Module-level CLI globals let isDryRun = false, ghp const CHANGELOG_COMMIT = 'changelog:commit', GH_PAGES_DEPLOY = 'ghpages:deploy', CHANGELOG_WRITE = 'changelog:write', DRY_RUN_SHORT_FLAG = 'd', DRY_RUN = 'dry-run', DRY_RUN_LONG_FLAG = DRY_RUN const VersionFlagMap = { m : 'minor', M : 'major', p : 'patch', P : 'prerelease' } function join (a, b) { return `${a}:${b}` } function bumperize (bumpType) { return join('bump', bumpType) } const shortVersionFlags = Object.keys(VersionFlagMap) const longVersionFlags = values(VersionFlagMap) const versionTypes = longVersionFlags const taskManager = new Undertaker const REF_SHA_CMD = 'git rev-parse --verify HEAD' const CURRENT_SHA = ChildProcess.execSync(REF_SHA_CMD) .toString().replace('\n','') const PKG_STS_CMD = 'git status package.json -s' const PKG_STATUS = ChildProcess.execSync(PKG_STS_CMD) .toString().replace('\n','').trim() const CL_STS_CMD = 'git status CHANGELOG.md -s' const CL_STATUS = ChildProcess.execSync(CL_STS_CMD) .toString().replace('\n','').trim() const unleash = shortVersionFlags.reduce((y, shortFlag) => { return y.option(shortFlag, { alias: VersionFlagMap[shortFlag], describe: `Alias for --type=${VersionFlagMap[shortFlag]}`, type: 'boolean' }) }, require('yargs')) .option('type', { describe: 'The SemVer version type such as "patch" that you want to publish to NPM with', type: 'string' }) .option('list-publishables', { alias: 'l', describe: 'Prints the files and directories that will and won\'t be published', type: 'boolean' }) .option('repo-type', { alias: 'r', describe: 'The remote repository type, "bitbucket" or "github"', default: 'github', type: 'string' }) .option('publish', { alias: 'pb', describe: 'Sets whether or not the package is published to NPM (negate with --no-publish)', default: true, type: 'boolean' }) .option('push', { alias: 'ps', describe: 'Sets whether or not the package is pushed to a git remote (negate with --no-push)', default: true, type: 'boolean' }) .option('ghpages-deploy', { alias: 'gh', describe: 'Deploy gh-pages', type: 'boolean' }) .option('ghpages-path', { alias: 'ghp', describe: 'The glob path to deploy to gh-pages', default: './docs/**/*', type: 'string' }) .option(DRY_RUN_LONG_FLAG, { alias: DRY_RUN_SHORT_FLAG, describe: 'Sets whether or not the process should be previewed rather than destructively executed', default: false, type: 'boolean' }) .alias('ls', 'l') .help('h').alias('h', 'help') .argv unleash.CURRENT_SHA = CURRENT_SHA unleash.PKG_STATUS = PKG_STATUS unleash.CL_STATUS = CL_STATUS isDryRun = !!unleash.dryRun const repoType = unleash.repoType if (unleash.gh) ghp = unleash.ghp const taskManagerInternalsSentinel = Symbol('__internals__') const taskInternals = taskManager[taskManagerInternalsSentinel] = { log : fancyLog } // Kray Kray McFadden ish to fake mutually exclusive arguments // See https://github.com/bcoe/yargs/issues/275 shortVersionFlags.forEach((key) => { if (unleash[key]) { if (unleash.type) { taskInternals.log(colors.yellow('=== UNLEASH ===')) const errorMessage = 'You\'re confusing me! Please don\'t pass more than one version type flag' taskInternals.log.error(colors.bgRed(colors.white(errorMessage))) process.exit(1) } unleash.type = VersionFlagMap[key] } }) const versionType = unleash.type taskManager.task(CHANGELOG_WRITE, (done) => { const nextVersion = Deploy.getNextVersion(versionType) if (isDryRun === true) { taskInternals.log( `* Creating a changelog entry for version ${nextVersion} with links to the commits on ${repoType}` ) return done() } else { taskInternals.log('Utilizing next version for changelog: ', colors.magenta(nextVersion)) return writeChangelog({ version : nextVersion, repoType : repoType }, done) } }) taskManager.task('ls', () => { return ls() }) taskManager.task(CHANGELOG_COMMIT, (done) => { const docsCommit = 'docs(CHANGELOG): Update changelog' if (isDryRun) { taskInternals.log(`* Adding commit "${docsCommit}"`) return done() } else // TODO - allow configuration of this src? return vinylFS.src([ '*.md' ]) .pipe(git.add()) .pipe(git.commit(docsCommit)) }) taskManager.task(GH_PAGES_DEPLOY, (done) => { if (isDryRun) { taskInternals.log(`* Pushing a gh-pages branch from the contents of "${ghp}"`) return done ? done() : true } else { taskInternals.log(`Deploying to gh-pages from ${ghp}`) return vinylFS.src([ ghp ]) .pipe(ghPages()) } }) function dryRunStartGh (done) { taskInternals.log(`Utilizing ${colors.magenta('dry run mode')}. This is a dry run of the following actions:`) return done() } taskManager.task(join(GH_PAGES_DEPLOY, DRY_RUN), taskManager.series([ dryRunStartGh, GH_PAGES_DEPLOY ])) // bump:major, bump:minor, bump:patch versionTypes.forEach((bumpType) => { const options = { bumpType: bumpType }, deployWithBump = Deploy.withBumpType.bind(Deploy, options), bumpTaskName = bumperize(bumpType) function noTrial () { return deployWithBump(merge({ dryRun : false }, unleash)) } taskManager.task(bumpTaskName, taskManager.series([ CHANGELOG_WRITE, CHANGELOG_COMMIT, noTrial ])) taskManager.task(join(CHANGELOG_WRITE, bumpType), taskManager.series([ CHANGELOG_WRITE ])) function dryRun () { return deployWithBump(merge({ dryRun : true }, unleash)) } function dryRunStart (done) { const nextVersion = Deploy.getNextVersion(versionType) taskInternals.log(`Utilizing ${colors.magenta('dry run mode')}. This is a dry run of the following actions:`) taskInternals.log(`* Incrementing to the next "${bumpType}" semantic version, "${nextVersion}"`) return done() } return taskManager.task(join(bumpTaskName, DRY_RUN), taskManager.series([ dryRunStart, CHANGELOG_WRITE, CHANGELOG_COMMIT, dryRun ])) }) // Don't automatically run tasks based on argv unless we're run via a CLI if (!module.parent) { taskInternals.log(colors.yellow('=== UNLEASH ===')) const command = process.argv.slice(1).map((a) => { return a.split('/').reverse()[0] }).join(' ') const wut = 'What did you want me to dry run?' const noType = 'Need a semantic version type homie...' const fakeBumpType = 'semantic-version-type-should-be-here' function logFlagCommand () { return taskInternals.log.error('Run "unleash --help" to discover available flags') } function logCorrectedCommand (flag) { return taskInternals.log.error(`${command} --${colors.bgGreen(colors.white(flag))}`) } if (unleash.type) { if (unleash.ls) ls() if (unleash.gh) { const task = taskManager.task(GH_PAGES_DEPLOY) task() } let taskName = bumperize(unleash.type) if (isDryRun) taskName = join(taskName, DRY_RUN) const task = taskManager.task(taskName) task(unleash) } else if (unleash.ls) { const task = taskManager.task('ls') task() } else if (unleash.gh) { ghp = unleash.ghp const task = taskManager.task(isDryRun ? join(GH_PAGES_DEPLOY, DRY_RUN) : GH_PAGES_DEPLOY) task() } else if (!unleash.publish) { const errorMessage = colors.bgRed(colors.white(isDryRun ? wut : noType)) taskInternals.log.error(errorMessage) logCorrectedCommand(fakeBumpType) logFlagCommand() } else if (!unleash.push) { const errorMessage = colors.bgRed(colors.white(isDryRun ? wut : noType)) taskInternals.log.error(errorMessage) logCorrectedCommand(fakeBumpType) logFlagCommand() } else { const noTask = 'Need a task homie...' const errorMessage = colors.bgRed(colors.white(isDryRun ? wut : noTask)) taskInternals.log.error(errorMessage) logCorrectedCommand('flag-name-should-be-here') logFlagCommand() } } module.exports = taskManager module.exports.taskManagerInternalsSentinel = taskManagerInternalsSentinel