@busy-web/cli
Advanced tools
Comparing version
{ | ||
"name": "@busy-web/cli", | ||
"version": "0.5.1", | ||
"version": "0.6.0", | ||
"description": "Command line tools to enhance web dev tasks", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -78,5 +78,16 @@ # cli | ||
Options: | ||
-l, --local prevents tag from pushing to upstream remote | ||
-u, --upstream <name> upstream remote name to push release tags, default: origin | ||
-undefined, --no-commit prevent version from committing and creating a new tag | ||
-t, --tag [name] tag the version and push to remote [name], default: origin | ||
-p, --push [name] push changes to remote [name], default: origin | ||
##### release:prune ‹version› [type] | ||
Prune a release type by version | ||
Alias: r:p | ||
Options: | ||
-undefined, --dry shows the tags that would be deleted but doesnt do anything | ||
-a, --all delete all tags matching the version and type | ||
-m, --mod <number> mod number to prune the tags with, default: 5 | ||
-r, --remote [name] flag to prune remote tags for [name], default: origin | ||
-p, --prod production tags can only be deleted with --prod option applied. | ||
##### template ‹type› ‹name› | ||
@@ -83,0 +94,0 @@ creates a new template file. (not supported yet) |
@@ -10,53 +10,199 @@ /** | ||
const logger = loader('utils/logger'); | ||
const { isEmpty } = loader('utils/types'); | ||
const { read, write } = loader('utils/file-io'); | ||
//const { get } = loader('utils/object'); | ||
/** | ||
* check to see if the version fits the valid standards | ||
* | ||
*/ | ||
function isValidTag(type) { | ||
return /^v?[0-9]+\.[0-9]+\.[0-9]+[\s\S]*$/.test(type); | ||
} | ||
function buildType(release, version) { | ||
return `npm version ${version}-${release}.1`; | ||
/** | ||
* trim any extra space and lines off the version | ||
* returned from the npm version cmd | ||
* | ||
*/ | ||
function normailzeResponse(str) { | ||
return str.replace(/[*\n]/g, '').trim(); | ||
} | ||
const buildTypes = { | ||
docker(version) { return buildType('dev', version); }, | ||
canary(version) { return buildType('canary', version); }, | ||
alpha(version) { return buildType('alpha', version); }, | ||
beta(version) { return buildType('beta', version); }, | ||
staging(version) { return buildType('staging', version); }, | ||
prod(version) { | ||
if (/-/.test(version)) { | ||
version = version.split('-')[0]; | ||
return `npm version ${version}`; | ||
/** | ||
* normalize the build type string | ||
* | ||
*/ | ||
function normalizeType(type) { | ||
if (type === 'production') { | ||
type === 'prod'; | ||
} else if (type === 'development' || type === 'docker') { | ||
type === 'dev'; | ||
} | ||
return type; | ||
} | ||
/** | ||
* save the new version to the public/version.json file | ||
* if it exists | ||
* | ||
*/ | ||
function savePackageInfo(version) { | ||
version = version.slice(1); | ||
return read('public/version.json').then(data => { | ||
let dout = JSON.parse(data); | ||
dout.version = version; | ||
return write('public/version.json', JSON.stringify(dout)); | ||
}).catch(() => RSVP.resolve()); | ||
} | ||
/** | ||
* increment the current version to get | ||
* the next version based on the build type | ||
* | ||
*/ | ||
function getNextVersion(build, version) { | ||
let [ baseVer, typeVer ] = version.split('-'); | ||
if (build === 'prerelease') { | ||
let vers = typeVer.split('.'); | ||
vers[1] = parseInt(vers[1]) + 1; | ||
typeVer = vers.join('.'); | ||
} else { | ||
let vers = baseVer.split('.'); | ||
if (build === 'major') { | ||
baseVer = [ (parseInt(vers[0]) + 1), 0, 0 ].join('.'); | ||
} else if (build === 'minor') { | ||
baseVer = [ vers[0], (parseInt(vers[1]) + 1), 0 ].join('.'); | ||
} else { | ||
return 'npm version patch'; | ||
baseVer = [ vers[0], vers[1], (parseInt(vers[2]) + 1) ].join('.'); | ||
} | ||
}, | ||
patch(version) { | ||
if (/-/.test(version)) { | ||
return 'npm version prerelease'; | ||
} | ||
return [ baseVer, typeVer ].join('-'); | ||
} | ||
/** | ||
* build a version update call npm call | ||
* | ||
* this call differs from the regular npm version call, it follows the following rules: | ||
* - major: | ||
* creates a new major version with prerelease dev --- 0.0.1 => 1.0.0-dev.0 | ||
* | ||
* - minor: | ||
* creates a new minor version with prerelease dev --- 0.0.1 => 0.1.0-dev.0 | ||
* | ||
* - patch: | ||
* creates a new patch version when version is not in prerelease --- 0.0.1 => 0.0.2 | ||
* or increments the prerelease version --- 0.0.1-dev.0 => 0.0.1-dev.1 | ||
* | ||
* - prerelease: | ||
* creates a new prerelease version --- 0.0.1-dev.0 => 0.0.1-dev.1 | ||
* | ||
* - <types>: | ||
* create a type specific prerelease --- 0.0.1 => 0.0.1.<type>.0 | ||
* change a type specific prerelease to another prerelease --- 0.0.1.dev.0 => 0.0.1.<type>.0 | ||
* increments a type specific prerelease --- 0.0.1.<type>.0 => 0.0.1.<type>.1 | ||
* | ||
* @private | ||
* @method getAction | ||
* @param type {string} the version comand action | ||
* @param version {string} the current version of the cwd project | ||
* @return {string} npm version command to run. | ||
*/ | ||
function getAction(type, version) { | ||
if (isValidTag(type)) { | ||
return type; | ||
} else { | ||
const isPreTag = /-/.test(version); | ||
if (type === 'major' || type === 'minor' || type === 'patch' || type === 'prerelease') { // block major and minor calls | ||
if (!isPreTag && type === 'patch') { | ||
// normal patch version call | ||
return 'patch'; | ||
} else if (isPreTag && type === 'prerelease') { | ||
// normal prerelease version call | ||
return 'prerelease'; | ||
} else { | ||
// prevent patch calls when in prerelease mode | ||
if (type === 'patch') { | ||
type === 'prerelease'; | ||
} | ||
let ver = getNextVersion(type, version); | ||
let [ baseVer ] = ver.split('-'); | ||
// create new version command from version | ||
return `${baseVer}-dev.0`; | ||
} | ||
} else { | ||
return 'npm version patch'; | ||
// split version into a base version and a type version | ||
let [ baseVer, typeVer ] = version.split('-'); | ||
if (type === 'prod') { | ||
// production version tags | ||
if (isEmpty(typeVer)) { | ||
// if there is no type version then the format is correct and its just a patch call | ||
return 'patch'; | ||
} else { | ||
// remove the type version info and create a tag with the base version info | ||
return baseVer; | ||
} | ||
} else { | ||
let typeRegex = new RegExp(type); | ||
if (!typeRegex.test(typeVer)) { | ||
// craete version type | ||
return `${baseVer}-${type}.0`; | ||
} else { | ||
// increment prerelease version | ||
return 'prerelease'; | ||
} | ||
} | ||
} | ||
}, | ||
minor() { return 'npm version --no-git-tag-version minor'; }, | ||
major() { return 'npm version --no-git-tag-version major'; } | ||
}; | ||
} | ||
} | ||
function getNextVersion(version) { | ||
let mode = 'patch'; | ||
if (/-/.test(version)) { | ||
mode = 'prerelease'; | ||
function getRemote() { | ||
let remote = false; | ||
if (this.program.tag) { | ||
// get tag flag | ||
remote = typeof this.program.tag === 'string' ? this.program.tag : 'origin'; | ||
} else if (this.program.push) { | ||
// get push flag | ||
remote = typeof this.program.push === 'string' ? this.program.push : 'origin'; | ||
} | ||
return cmd(`npm version --no-git-tag-version ${mode}`, { hidecmd: true }).then(newver => { | ||
newver = normailzeResponse(newver); | ||
newver = newver.split('-')[0]; | ||
return cmd(`npm version --no-git-tag-version ${version}`, { hidecmd: true }).then(oldver => { | ||
oldver = normailzeResponse(oldver); | ||
return { newver, oldver }; | ||
}); | ||
return remote; | ||
} | ||
function tagVersion(remote, version) { | ||
if (this.program.noCommit || !this.program.tag) { | ||
return RSVP.resolve(`Skipping tag`); | ||
} | ||
// create and push the new tag | ||
return cmd(`git tag -a ${version} -m "Release Version: ${version}"`, { hidecmd: true }).then(() => { | ||
return cmd(`git push ${remote} --tags`, { hidecmd: true }).then(() => `Tagged new release ${remote}/${version}`); | ||
}); | ||
} | ||
function normailzeResponse(str) { | ||
return str.replace(/[*\n]/g, '').trim(); | ||
function pushVersion(remote) { | ||
if (this.program.noCommit || (!this.program.tag && !this.program.push)) { | ||
return RSVP.resolve(`Skipping push`); | ||
} | ||
// get the branch name to push to | ||
return cmd(`git branch`, { hidecmd: true }).then(branch => { | ||
// normalize branch name | ||
branch = normailzeResponse(branch); | ||
// push the remote tag and branch data | ||
return cmd(`git push ${remote} ${branch}`, { hidecmd: true }).then(() => `Pushed to ${remote}/${branch}`); | ||
}); | ||
} | ||
function commitVersion(version) { | ||
if (this.program.noCommit) { | ||
return RSVP.resolve(`Skipping commit`); | ||
} | ||
// commit new version release | ||
return cmd(`git commit -am "Release Version: ${version}"`, { hidecmd: true }).then(() => `Created release commit`); | ||
} | ||
/** | ||
* command class | ||
* | ||
*/ | ||
module.exports = createCommand({ | ||
@@ -69,36 +215,44 @@ name: 'release', | ||
options: [ | ||
{ cmd: '--local', short: '-l', desc: 'prevents tag from pushing to upstream remote' }, | ||
{ cmd: '--upstream', short: '-u', args: ['<name>'], desc: 'upstream remote name to push release tags, default: origin' } | ||
{ cmd: '--no-commit', desc: 'prevent version from committing and creating a new tag' }, | ||
{ cmd: '--tag', short: '-t', args: [ '[name]' ], desc: 'tag the version and push to remote [name], default: origin' }, | ||
{ cmd: '--push', short: '-p', args: [ '[name]' ], desc: 'push changes to remote [name], default: origin' } | ||
], | ||
run(type) { | ||
if (!buildTypes[type]) { | ||
logger.error(`build type not found [${type}] valid types are '${Object.keys(buildTypes).join('|')}'`); | ||
return; | ||
} | ||
const cwd = process.cwd(); | ||
const remote = getRemote.call(this); | ||
let pkgInfo = require(path.join(cwd + '/package.json')); | ||
let version = pkgInfo.version; | ||
let promise = RSVP.resolve({ newver: version, oldver: version }); | ||
if (type === 'docker' || type === 'canary' || type === 'alpha' || type === 'beta') { | ||
promise = getNextVersion(version); | ||
} else if (type === 'prod' || type === 'production') { | ||
type === 'prod'; | ||
//promise = RSVP.resolve({ newver: version, oldver: version }); | ||
} | ||
let remote = 'origin'; | ||
if (this.program.upstream) { | ||
remote = this.program.upstream; | ||
} | ||
// normalize type string | ||
type = normalizeType(type); | ||
promise.then(vers => { | ||
cmd(buildTypes[type](vers.newver)).then(ver => { | ||
ver = normailzeResponse(ver); | ||
cmd(`git branch`, { hidecmd: true }).then(branch => { | ||
branch = normailzeResponse(branch); | ||
cmd(`git push ${remote} ${branch}`).then(() => { | ||
cmd(`git push ${remote} --tags`).then(() => { | ||
logger.info(`${ver} released to remote ${remote}.`); | ||
// create npm version command | ||
let vercmd = 'npm version --no-git-tag-version '; | ||
// add version action | ||
vercmd += getAction(type, version); | ||
// create new npm version string | ||
return cmd(vercmd, { hidecmd: true }).then(ver => { | ||
// normalize version info | ||
ver = normailzeResponse(ver); | ||
logger.info(`Version created: ${ver}`); | ||
// save new version in public/version.json if project has one | ||
return savePackageInfo(ver).then(() => { | ||
// commit version info. | ||
return commitVersion.call(this, ver).then(commitRes => { | ||
logger.info(commitRes); | ||
// tag promise has either pushed a tag or skipped it. | ||
return tagVersion.call(this, remote, ver).then(tagRes => { | ||
logger.info(tagRes); | ||
// push or skip pushing to remote branch | ||
return pushVersion.call(this, remote).then(pushRes => { | ||
logger.info(pushRes); | ||
return RSVP.resolve(`Release version ${ver} finished!`); | ||
}); | ||
@@ -108,4 +262,9 @@ }); | ||
}); | ||
}).catch(err => { | ||
if (process.__busyweb.debug) { | ||
logger.error(err); | ||
} | ||
return RSVP.reject(`Run cmd: ${vercmd} failed to execute.`); | ||
}); | ||
} | ||
}); |
@@ -29,7 +29,7 @@ | ||
}, | ||
debug(...args) { | ||
this.write(colors.cyan(' => Debug:'), stringify(args)); | ||
}, | ||
subinfo(...args) { | ||
@@ -36,0 +36,0 @@ this.write(colors.green(' =>'), colors.cyan(stringify(args))); |
51443
24.29%39
2.63%1291
29.62%107
11.46%