Skip to content

Commit

Permalink
feat: support releasing a custom version, including pre-releases (#129)
Browse files Browse the repository at this point in the history
  • Loading branch information
e-cloud authored and bcoe committed Nov 26, 2016
1 parent b6e1562 commit 068008d
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 27 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ npm-debug.log
/.settings
/.idea
/.vscode

# coverage
coverage
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,40 @@ As long as your git commit messages are conventional and accurate, you no longer

After you cut a release, you can push the new git tag and `npm publish` (or `npm publish --tag next`) when you're ready.

### Release as a pre-release

Use `--prerelease`, you can generate a pre-release.

Suppose the last version of your code is `1.0.0`, and your code to be committed has patched changes. Run
```bash
# npm run script
npm run release -- --prerelease
```
you will get version `1.0.1-0`.

If you want to name the pre-release, you specify the name via `--prerelease <name>`.

For example, the wanted name is `alpha`
. Use the example above:
```bash
# npm run script
npm run release -- --prerelease alpha
```
you will get version `1.0.1-alpha.0`

### Release as a target type imperatively like `npm version`

You can use `--release-as` to generate a `major`, `minor` or `patch` release imperatively.

Suppose the last version of your code is `1.0.0`, and your code to be committed has patched changes. Run
```bash
# npm run script
npm run release -- --release-as minor
```
you will get version `1.1.0` rather than the smartly generated version `1.0.1`.

**NOTE:** you can combine `--release-as` and `--prerelease` to generate a release. That's useful when publishing experimental feature(s).

### Prevent Git Hooks

If you use git hooks, like pre-commit, to test your code before committing, you can prevent hooks from being verified during the commit step by passing the `--no-verify` option:
Expand Down
9 changes: 9 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env node
var standardVersion = require('../index')
var cmdParser = require('../command')

standardVersion(cmdParser.argv, function (err) {
if (err) {
process.exit(1)
}
})
27 changes: 17 additions & 10 deletions cli.js → command.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
#!/usr/bin/env node
var standardVersion = require('./index')
var defaults = require('./defaults')

var argv = require('yargs')
module.exports = require('yargs')
.usage('Usage: $0 [options]')
.option('release-as', {
alias: 'r',
describe: 'Specify the release type manually. like npm version xxx with limited choices',
requiresArg: true,
string: true,
choices: ['major', 'minor', 'patch'],
global: true
})
.option('prerelease', {
alias: 'p',
describe: 'make a pre-release with optional option value to specify a tag id',
string: true,
global: true
})
.option('infile', {
alias: 'i',
describe: 'Read the CHANGELOG from this file',
Expand Down Expand Up @@ -51,15 +63,10 @@ var argv = require('yargs')
default: defaults.silent,
global: true
})
.version()
.alias('version', 'v')
.help()
.alias('help', 'h')
.example('$0', 'Update changelog and tag release')
.example('$0 -m "%s: see changelog for details"', 'Update changelog and tag release with custom commit message')
.wrap(97)
.argv

standardVersion(argv, function (err) {
if (err) {
process.exit(1)
}
})
105 changes: 93 additions & 12 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,40 +16,121 @@ module.exports = function standardVersion (argv, done) {
var pkg = require(pkgPath)
var defaults = require('./defaults')

argv = objectAssign(defaults, argv)
var args = objectAssign({}, defaults, argv)

conventionalRecommendedBump({
preset: 'angular'
}, function (err, release) {
bumpVersion(args.releaseAs, function (err, release) {
if (err) {
printError(argv, err.message)
printError(args, err.message)
return done(err)
}

var newVersion = pkg.version
if (!argv.firstRelease) {
newVersion = semver.inc(pkg.version, release.releaseType)
checkpoint(argv, 'bumping version in package.json from %s to %s', [pkg.version, newVersion])

if (!args.firstRelease) {
var releaseType = getReleaseType(args.prerelease, release.releaseType, pkg.version)
newVersion = semver.inc(pkg.version, releaseType, args.prerelease)

checkpoint(args, 'bumping version in package.json from %s to %s', [pkg.version, newVersion])

pkg.version = newVersion
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8')
} else {
checkpoint(argv, 'skip version bump on first release', [], chalk.red(figures.cross))
checkpoint(args, 'skip version bump on first release', [], chalk.red(figures.cross))
}

outputChangelog(argv, function (err) {
outputChangelog(args, function (err) {
if (err) {
return done(err)
}
commit(argv, newVersion, function (err) {
commit(args, newVersion, function (err) {
if (err) {
return done(err)
}
return tag(newVersion, pkg.private, argv, done)
return tag(newVersion, pkg.private, args, done)
})
})
})
}

function getReleaseType (prerelease, expectedReleaseType, currentVersion) {
if (isString(prerelease)) {
if (isInPrerelease(currentVersion)) {
if (shouldContinuePrerelease(currentVersion, expectedReleaseType) ||
getTypePriority(getCurrentActiveType(currentVersion)) > getTypePriority(expectedReleaseType)
) {
return 'prerelease'
}
}

return 'pre' + expectedReleaseType
} else {
return expectedReleaseType
}
}

function isString (val) {
return typeof val === 'string'
}

/**
* if a version is currently in pre-release state,
* and if it current in-pre-release type is same as expect type,
* it should continue the pre-release with the same type
*
* @param version
* @param expectType
* @return {boolean}
*/
function shouldContinuePrerelease (version, expectType) {
return getCurrentActiveType(version) === expectType
}

function isInPrerelease (version) {
return Array.isArray(semver.prerelease(version))
}

var TypeList = ['major', 'minor', 'patch'].reverse()

/**
* extract the in-pre-release type in target version
*
* @param version
* @return {string}
*/
function getCurrentActiveType (version) {
var typelist = TypeList
for (var i = 0; i < typelist.length; i++) {
if (semver[typelist[i]](version)) {
return typelist[i]
}
}
}

/**
* calculate the priority of release type,
* major - 2, minor - 1, patch - 0
*
* @param type
* @return {number}
*/
function getTypePriority (type) {
return TypeList.indexOf(type)
}

function bumpVersion (releaseAs, callback) {
if (releaseAs) {
callback(null, {
releaseType: releaseAs
})
} else {
conventionalRecommendedBump({
preset: 'angular'
}, function (err, release) {
callback(err, release)
})
}
}

function outputChangelog (argv, cb) {
createIfMissing(argv)
var header = '# Change Log\n\nAll notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n'
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
"name": "standard-version",
"version": "3.0.0",
"description": "replacement for `npm version` with automatic CHANGELOG generation",
"bin": "cli.js",
"bin": "bin/cli.js",
"scripts": {
"pretest": "standard",
"coverage": "nyc report --reporter=text-lcov | coveralls",
"test": "nyc mocha --timeout=20000 test.js",
"release": "./cli.js"
"release": "bin/cli.js"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -43,6 +43,7 @@
"yargs": "^6.0.0"
},
"devDependencies": {
"bluebird": "^3.4.6",
"chai": "^3.5.0",
"coveralls": "^2.11.9",
"mocha": "^3.1.0",
Expand Down
Loading

0 comments on commit 068008d

Please sign in to comment.