From 4fcd4a7edabbfe4183fbaa7fcd0f55b2441449d1 Mon Sep 17 00:00:00 2001 From: "Benjamin E. Coe" Date: Sat, 4 May 2019 21:45:03 -0700 Subject: [PATCH] feat!: adds configurable conventionalcommits preset (#323) BREAKING CHANGE: we now use the conventionalcommits preset by default, which directly tracks conventionalcommits.org. --- README.md | 33 +++++++++++++++++++++++++++++++++ command.js | 28 ++++++++++++++++++++++++---- defaults.json | 4 ++-- lib/lifecycles/bump.js | 3 ++- lib/lifecycles/changelog.js | 3 ++- lib/preset-loader.js | 16 ++++++++++++++++ package.json | 10 ++++++---- test.js | 30 ++++++++++++++++++++++++++++-- 8 files changed, 113 insertions(+), 14 deletions(-) create mode 100644 lib/preset-loader.js diff --git a/README.md b/README.md index 956efb947..6d6b7d35d 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,39 @@ Now you can use `standard-version` in place of `npm version`. This has the benefit of allowing you to use `standard-version` on any repo/package without adding a dev dependency to each one. +## Configuration + +You can configure `standard-version` either by: + +1. Placing a `standard-version` stanza in your `package.json` (assuming + your project is JavaScript). +1. Creating a `.versionrc` or `.versionrc.json`. + +Any of the command line paramters accepted by `standard-version` can instead +be provided via configuration. + +### Customizing CHANGELOG Generation + +By default, `standard-version` uses the [conventionalcommits preset](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-conventionalcommits). + +This preset: + +* adheres closely to the [conventionalcommits.org](https://www.conventionalcommits.org) + specification. +* is highly configurable, following the configuration specification + [maintained here](https://github.com/conventional-changelog/conventional-changelog-config-spec). + * _we've documented these config settings as a recommendation to other tooling makers._ + +There are a variety of dials and knobs you can turn related to CHANGELOG generation. + +As an example, suppose you're using GitLab, rather than GitHub, you might modify the following variables: + +* `commitUrlFormat`: the URL format of commit SHAs detected in commit messages. +* `compareUrlFormat`: the URL format used to compare two tags. +* `issueUrlFormat`: the URL format used to link to issues. + +Making these URLs match GitLab's format, rather than GitHub's. + ## CLI Usage ### First Release diff --git a/command.js b/command.js index 23e3ab2d5..4ea6c1c3e 100755 --- a/command.js +++ b/command.js @@ -1,6 +1,12 @@ -let defaults = require('./defaults') +const findUp = require('find-up') +const defaults = require('./defaults') +const { readFileSync } = require('fs') -module.exports = require('yargs') +const configPath = findUp.sync(['.versionrc', '.version.json']) +const config = configPath ? JSON.parse(readFileSync(configPath)) : {} +const spec = require('conventional-changelog-config-spec') + +const yargs = require('yargs') .usage('Usage: $0 [options]') .option('release-as', { alias: 'r', @@ -95,11 +101,25 @@ module.exports = require('yargs') return 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') .pkgConf('standard-version') + .config(config) .wrap(97) + +Object.keys(spec.properties).forEach(propertyKey => { + const property = spec.properties[propertyKey] + yargs.option(propertyKey, { + type: property.type, + describe: property.description, + default: property.default, + group: 'Preset Configuration:' + }) +}) + +module.exports = yargs + +// TODO: yargs should be populated with keys/descriptions from +// https://github.com/conventional-changelog/conventional-changelog-config-spec diff --git a/defaults.json b/defaults.json index 17035be47..c5bc42198 100644 --- a/defaults.json +++ b/defaults.json @@ -11,5 +11,5 @@ "skip": {}, "dryRun": false, "gitTagFallback": true, - "preset": "angular" -} + "preset": "conventionalcommits" +} \ No newline at end of file diff --git a/lib/lifecycles/bump.js b/lib/lifecycles/bump.js index a62d770b5..0fd0a1ac7 100644 --- a/lib/lifecycles/bump.js +++ b/lib/lifecycles/bump.js @@ -9,6 +9,7 @@ const figures = require('figures') const fs = require('fs') const DotGitignore = require('dotgitignore') const path = require('path') +const presetLoader = require('../preset-loader') const runLifecycleScript = require('../run-lifecycle-script') const semver = require('semver') const stringifyPackage = require('stringify-package') @@ -137,7 +138,7 @@ function bumpVersion (releaseAs, args) { } else { conventionalRecommendedBump({ debug: args.verbose && console.info.bind(console, 'conventional-recommended-bump'), - preset: args.preset || 'angular', + preset: presetLoader(args), path: args.path }, function (err, release) { if (err) return reject(err) diff --git a/lib/lifecycles/changelog.js b/lib/lifecycles/changelog.js index da47f5415..f32eabdda 100644 --- a/lib/lifecycles/changelog.js +++ b/lib/lifecycles/changelog.js @@ -3,6 +3,7 @@ const chalk = require('chalk') const checkpoint = require('../checkpoint') const conventionalChangelog = require('conventional-changelog') const fs = require('fs') +const presetLoader = require('../preset-loader') const runLifecycleScript = require('../run-lifecycle-script') const writeFile = require('../write-file') @@ -32,7 +33,7 @@ function outputChangelog (args, newVersion) { if (args.dryRun) context = { version: newVersion } let changelogStream = conventionalChangelog({ debug: args.verbose && console.info.bind(console, 'conventional-changelog'), - preset: args.preset || 'angular', + preset: presetLoader(args), tagPrefix: args.tagPrefix }, context, { merges: null, path: args.path }) .on('error', function (err) { diff --git a/lib/preset-loader.js b/lib/preset-loader.js new file mode 100644 index 000000000..72cb4d3ad --- /dev/null +++ b/lib/preset-loader.js @@ -0,0 +1,16 @@ +// TODO: this should be replaced with an object we maintain and +// describe in: https://github.com/conventional-changelog/conventional-changelog-config-spec +const spec = require('conventional-changelog-config-spec') + +module.exports = (args) => { + let preset = args.preset || 'conventionalcommits' + if (preset === 'conventionalcommits') { + preset = { + name: preset + } + Object.keys(spec.properties).forEach(key => { + if (args[key] !== undefined) preset[key] = args[key] + }) + } + return preset +} diff --git a/package.json b/package.json index 8a19460fd..5bdcca035 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "replacement for `npm version` with automatic CHANGELOG generation", "bin": "bin/cli.js", "scripts": { - "pretest": "eslint .", + "posttest": "eslint .", "coverage": "nyc report --reporter=text-lcov | coveralls", "test": "nyc mocha --timeout=20000 test.js", "release": "bin/cli.js" @@ -39,12 +39,14 @@ "homepage": "https://github.com/conventional-changelog/standard-version#readme", "dependencies": { "chalk": "^2.4.1", - "conventional-changelog": "^3.0.6", - "conventional-recommended-bump": "^4.0.4", + "conventional-changelog": "^3.1.2", + "conventional-changelog-config-spec": "^1.0.0", + "conventional-recommended-bump": "^4.1.1", "detect-indent": "^5.0.0", "detect-newline": "^2.1.0", "dotgitignore": "^2.1.0", "figures": "^2.0.0", + "find-up": "^3.0.0", "fs-access": "^1.0.0", "git-semver-tags": "^2.0.2", "semver": "^5.2.0", @@ -53,7 +55,7 @@ }, "devDependencies": { "chai": "^3.5.0", - "coveralls": "^3.0.1", + "coveralls": "^3.0.3", "eslint": "^5.16.0", "eslint-config-standard": "^12.0.0", "eslint-plugin-import": "^2.16.0", diff --git a/test.js b/test.js index 363d07f89..28d73e283 100644 --- a/test.js +++ b/test.js @@ -668,13 +668,13 @@ describe('cli', function () { }) it('does not display `all staged files` without the --commit-all flag', function () { - var result = execCli() + let result = execCli() result.code.should.equal(0) result.stdout.should.not.match(/and all staged files/) }) it('does display `all staged files` if the --commit-all flag is passed', function () { - var result = execCli('--commit-all') + let result = execCli('--commit-all') result.code.should.equal(0) result.stdout.should.match(/and all staged files/) }) @@ -989,4 +989,30 @@ describe('standard-version', function () { }) }) }) + + describe('configuration', () => { + it('reads config from .versionrc', function () { + // we currently skip several replacments in CHANGELOG + // generation if repository URL isn't set. + // + // TODO: consider modifying this logic in conventional-commits + // perhaps we should only skip the replacement if we rely on + // the {{host}} field? + writePackageJson('1.0.0', { + repository: { + url: 'https://github.com/yargs/yargs.git' + } + }) + // write configuration that overrides default issue + // URL format. + fs.writeFileSync('.versionrc', JSON.stringify({ + issueUrlFormat: 'http://www.foo.com/{{id}}' + }), 'utf-8') + commit('feat: another commit addresses issue #1') + execCli() + // CHANGELOG should have the new issue URL format. + const content = fs.readFileSync('CHANGELOG.md', 'utf-8') + content.should.include('http://www.foo.com/1') + }) + }) })