diff --git a/README.md b/README.md index e614ff20e..e0c9f2a43 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,48 @@ # Standard Version +A utility for versioning using [semver](https://semver.org/) and CHANGELOG generation powered by [Conventional Commits](https://conventionalcommits.org). + [![Build Status](https://travis-ci.org/conventional-changelog/standard-version.svg?branch=master)](https://travis-ci.org/conventional-changelog/standard-version) [![NPM version](https://img.shields.io/npm/v/standard-version.svg)](https://www.npmjs.com/package/standard-version) [![Coverage Status](https://coveralls.io/repos/conventional-changelog/standard-version/badge.svg?branch=)](https://coveralls.io/r/conventional-changelog/standard-version?branch=master) [![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) -[![community slack](http://devtoolscommunity.herokuapp.com/badge.svg)](http://devtoolscommunity.herokuapp.com) +[![Community slack](http://devtoolscommunity.herokuapp.com/badge.svg)](http://devtoolscommunity.herokuapp.com) + +_Having problems? Want to contribute? Join us on the [node-tooling community Slack](http://devtoolscommunity.herokuapp.com)_. + + +_How It Works:_ -_Having problems? want to contribute? join our [community slack](http://devtoolscommunity.herokuapp.com)_. +1. Follow the [Conventional Commits Specification](https://conventionalcommits.org) in your repository. +2. When you're ready to release, run `standard-version`. +`standard-version` will then do the following: -Automate versioning and CHANGELOG generation, with [semver](https://semver.org/) and -[conventional commit messages](https://conventionalcommits.org). +1. Retreive the current version of your repository by looking at `bumpFiles`[1](), falling back to the last `git tag`. +2. `bump` the version in `bumpFiles`[1]() based on your commits. +4. Generates a `changelog` based on your commints (uses [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog) under the hood). +5. Creates a new `commit` including your `bumpFiles`[1]() and updated CHANGELOG. +6. Creates a new `tag` with the new version number. -_how it works:_ -1. when you land commits on your `master` branch, select the _Squash and Merge_ option. -2. add a title and body that follows the [Conventional Commits Specification](https://conventionalcommits.org). -3. when you're ready to release: - 1. `git checkout master; git pull origin master` - 2. run `standard-version` - 3. `git push --follow-tags origin master && npm publish` - _(or, `docker push`, `gem push`, etc.)_ +### `bumpFiles`, `packageFiles` and `updaters` -`standard-version` does the following: +`standard-version` uses a few key concepts for handling version bumping in your project. -1. bumps the version in metadata files (package.json, composer.json, etc). -2. uses [conventional-changelog](https://github.com/conventional-changelog/conventional-changelog) to update _CHANGELOG.md_ -3. commits _package.json (et al.)_ and _CHANGELOG.md_ -4. tags a new release +- **`packageFiles`** – User-defined files where versions can be read from _and_ "bumped". + - Examples: `package.json`, `manifest.json` + - In most cases (including the default), `packageFiles` are a subset of `bumpFiles`. +- **`bumpFiles`** – User-defined files where versions should be "bumped", but not explicitly read from. + - Examples: `package-lock.json`, `npm-shrinkwrap.json` +- **`updaters`** – Simple modules used for reading `packageFiles` and writing to `bumpFiles`. -## Installation +By default, `standard-version` assumes you're working in a NodeJS based project... because of this, for the majority of projects you might never need to interact with these options. -### As `npm run` script +That said, if you find your self asking ["How can I use `standard-version` for additional metadata files, languages or version files?"](#how-can-I-use-standard-version-for-additional metadata-files-languages-or-version-files) – these configuration options will help! + +## Installing `standard-version` + +### As a local `npm run` script Install and add to `devDependencies`: @@ -39,7 +50,7 @@ Install and add to `devDependencies`: npm i --save-dev standard-version ``` -Add an [`npm run` script](https://docs.npmjs.com/cli/run-script) to your _package.json_: +Add an [`npm run` script](https://docs.npmjs.com/cli/run-script) to your `package.json`: ```json { @@ -53,7 +64,7 @@ Now you can use `npm run release` in place of `npm version`. This has the benefit of making your repo/package more portable, so that other developers can cut releases without having to globally install `standard-version` on their machine. -### As global bin +### As global `bin` Install globally (add to your `PATH`): @@ -65,6 +76,12 @@ 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. +### Using `npx` + +As of `npm@5.2.0`, `npx` is installed alongside `npm`. Using `npx` you can use `standard-version` without having to keep a `package.json` file by running: `npx standard-version`. + +This method is especially useful when using `standard-version` in non-JavaScript projects. + ## Configuration You can configure `standard-version` either by: @@ -80,15 +97,15 @@ be provided via configuration. Please refer to the [conventional-changelog-confi ### Customizing CHANGELOG Generation -By default, `standard-version` uses the [conventionalcommits preset](https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-changelog-conventionalcommits). +By default (as of `6.0.0`), `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) +* Adheres closely to the [conventionalcommits.org](https://www.conventionalcommits.org) specification. -* is highly configurable, following the configuration 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._ + * _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. @@ -111,15 +128,17 @@ To generate your changelog for your first release, simply do: ```sh # npm run script npm run release -- --first-release -# or global bin +# global bin standard-version --first-release +# npx +npx standard-version --first-release ``` -This will tag a release **without bumping the version in package.json (_et al._)**. +This will tag a release **without bumping the version `bumpFiles`[1]()**. -When ready, push the git tag and `npm publish` your first release. \o/ +When you are ready, push the git tag and `npm publish` your first release. \o/ -### Cut a Release +### Cutting Releases If you typically use `npm version` to cut a new release, do this instead: @@ -134,7 +153,7 @@ 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 +### Release as a Pre-Release Use the flag `--prerelease` to generate pre-releases: @@ -144,7 +163,7 @@ Suppose the last version of your code is `1.0.0`, and your code to be committed # npm run script npm run release -- --prerelease ``` -you will get version `1.0.1-0`. +This will tag your version as: `1.0.1-0`. If you want to name the pre-release, you specify the name via `--prerelease `. @@ -155,14 +174,14 @@ For example, suppose your pre-release should contain the `alpha` prefix: npm run release -- --prerelease alpha ``` -this will tag the version `1.0.1-alpha.0` +This will tag the version as: `1.0.1-alpha.0` -### Release as a target type imperatively like `npm version` +### Release as a Target Type Imperatively (`npm version`-like) -To forgo the automated version bump use `--release-as` with the argument `major`, `minor` or `patch`: +To forgo the automated version bump use `--release-as` with the argument `major`, `minor` or `patch`. Suppose the last version of your code is `1.0.0`, you've only landed `fix:` commits, but -you would like your next release to be a `minor`. Simply do: +you would like your next release to be a `minor`. Simply run the following: ```bash # npm run script @@ -171,7 +190,7 @@ npm run release -- --release-as minor npm run release -- --release-as 1.1.0 ``` -you will get version `1.1.0` rather than the auto generated version `1.0.1`. +you will get version `1.1.0` rather than what would be the auto-generated version `1.0.1`. > **NOTE:** you can combine `--release-as` and `--prerelease` to generate a release. This is useful when publishing experimental feature(s). @@ -186,11 +205,11 @@ npm run release -- --no-verify standard-version --no-verify ``` -### Signing commits and tags +### Signing Commits and Tags If you have your GPG key set up, add the `--sign` or `-s` flag to your `standard-version` command. -### Lifecycle scripts +### Lifecycle Scripts `standard-version` supports lifecycle scripts. These allow you to execute your own supplementary commands during the release. The following @@ -231,7 +250,7 @@ with a link to your Jira - assuming you have already installed [replace](https:/ } ``` -### Skipping lifecycle steps +### Skipping Lifecycle Steps You can skip any of the lifecycle steps (`bump`, `changelog`, `commit`, `tag`), by adding the following to your package.json: @@ -246,7 +265,7 @@ by adding the following to your package.json: } ``` -### Committing generated artifacts in the release commit +### Committing Generated Artifacts in the Release Commit If you want to commit generated artifacts in the release commit (e.g. [#96](https://github.com/conventional-changelog/standard-version/issues/96)), you can use the `--commit-all` or `-a` flag. You will need to stage the artifacts you want to commit, so your `release` command could look like this: @@ -255,7 +274,7 @@ If you want to commit generated artifacts in the release commit (e.g. [#96](http "release": "git add && standard-version -a" ``` -### Dry run mode +### Dry Run Mode running `standard-version` with the flag `--dry-run` allows you to see what commands would be run, without committing to git or updating files. @@ -288,10 +307,7 @@ npm run release -- --help standard-version --help ``` -## Code usage - -Use the `silent` option to stop `standard-version` from printing anything -to the console. +## Code Usage ```js const standardVersion = require('standard-version') @@ -309,69 +325,115 @@ standardVersion({ }) ``` -## Commit Message Convention, at a Glance +_TIP: Use the `silent` option to prevent `standard-version` from printing to the `console`._ + +## FAQ -_patches:_ +### How is `standard-version` different from `semantic-release`? -```sh -git commit -a -m "fix(parsing): fixed a bug in our parser" -``` +[`semantic-release`](https://github.com/semantic-release/semantic-release) is described as: -_features:_ +> semantic-release automates the whole package release workflow including: determining the next version number, generating the release notes and publishing the package. -```sh -git commit -a -m "feat(parser): we now have a parser \o/" -``` +While both are based on the same foundation of structured commit messages, `standard-version` takes a different approach by handling versioning, changelog generation, and git tagging for you **without** automatic pushing (to GitHub) or publishing (to an npm registry). Use of `standard-version` only affects your local git repo - it doesn't affect remote resources at all. After you run `standard-version`, you can review your release state, correct mistakes and follow the release strategy that makes the most sense for your codebase. -_breaking changes:_ +We think they are both fantastic tools, and we encourage folks to use `semantic-release` instead of `standard-version` if it makes sense for their use-case. -```sh -git commit -a -m "feat(new-parser): introduces a new parsing library -BREAKING CHANGE: new library does not support foo-construct" -``` +### Should I always squash commits when merging PRs? -_other changes:_ +The instructions to squash commits when merging pull requests assumes that **one PR equals, at most, one feature or fix**. -You decide, e.g., docs, chore, etc. +If you have multiple features or fixes landing in a single PR and each commit uses a structured message, then you can do a standard merge when accepting the PR. This will preserve the commit history from your branch after the merge. -```sh -git commit -a -m "docs: fixed up the docs a bit" -``` +Although this will allow each commit to be included as separate entries in your CHANGELOG, the entries will **not** be able to reference the PR that pulled the changes in because the preserved commit messages do not include the PR number. -_but wait, there's more!_ +For this reason, we recommend keeping the scope of each PR to one general feature or fix. In practice, this allows you to use unstructured commit messages when committing each little change and then squash them into a single commit with a structured message (referencing the PR number) once they have been reviewed and accepted. -Github usernames (`@bcoe`) and issue references (#133) will be swapped out for the -appropriate URLs in your CHANGELOG. +### Can I use `standard-version` for additional metadata files, languages or version files? -## Badges! +YES! Using `bumpFiles` (and `packageFiles`) configurations you should be able to configure `standard-version` to work for you. -Tell your users that you adhere to the Conventional Commits specification: +1. Specify a custom `bumpFile` "`file`", this is the path to the file you want to "bump" +2. Specify the `bumpFile` "`updater`", this is _how_ the file will be bumped. + + a. If your using a common type, you can use one of `standard-version`'s built-in `updaters` by specifying a `type`. -```markdown -[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org) + b. If your using an less-common version file, you can create your own `updater`. + +```json +// .versionrc +{ + "bumpFiles": [ + { + "file": "MY_VERSION_TRACKER.txt", + // The `plain-text` updater assumes the file contents represents the version. + "type": "plain-text" + }, + { + "file": "a/deep/package/dot/json/file/package.json", + // The `json` updater assumes the version is available under a `version` key in the provided JSON document. + "type": "json" + } + { + "file": "VERSION_TRACKER.json", + // See "Custom `updater`s" for more details. + "updater": "standard-version-updater.js" + } + ] +} ``` -## FAQ +#### Custom `updater`s -### How is `standard-version` different from `semantic-release`? +An `updater` is expected to be a Javascript module with _atleast_ two methods exposed: `readVersion` and `writeVersion`. -[`semantic-release`](https://github.com/semantic-release/semantic-release) is described as: +##### `readVersion(contents = string): string` -> semantic-release automates the whole package release workflow including: determining the next version number, generating the release notes and publishing the package. +This method is used to read the version from the provided file contents. -While both are based on the same foundation of structured commit messages, `standard-version` takes a different approach by handling versioning, changelog generation, and git tagging for you **without** automatic pushing (to GitHub) or publishing (to an npm registry). Use of `standard-version` only affects your local git repo - it doesn't affect remote resources at all. After you run `standard-version`, you can review your release state, correct mistakes and follow the release strategy that makes the most sense for your codebase. +The return value is expected to be a semantic version string. -We think they are both fantastic tools, and we encourage folks to use `semantic-release` instead of `standard-version` if it makes sense for their use-case. +##### `writeVersion(contents = string, version: string): string` -### Should I always squash commits when merging PRs? +This method is used to write the version to the provided contents. -The instructions to squash commits when merging pull requests assumes that **one PR equals, at most, one feature or fix**. +The return value will be written directly (overwrite) to the provided file. -If you have multiple features or fixes landing in a single PR and each commit uses a structured message, then you can do a standard merge when accepting the PR. This will preserve the commit history from your branch after the merge. +--- -Although this will allow each commit to be included as separate entries in your CHANGELOG, the entries will **not** be able to reference the PR that pulled the changes in because the preserved commit messages do not include the PR number. +Let's assume our `VERSION_TRACKER.json` has the following contents: -For this reason, we recommend keeping the scope of each PR to one general feature or fix. In practice, this allows you to use unstructured commit messages when committing each little change and then squash them into a single commit with a structured message (referencing the PR number) once they have been reviewed and accepted. +```json +{ + "tracker": { + "package": { + "version": "1.0.0" + } + } +} + +``` + +An acceptable `standard-version-updater.js` would be: + +```js +// standard-version-updater.js +const stringifyPackage = require('stringify-package') +const detectIndent = require('detect-indent') +const detectNewline = require('detect-newline') + +module.exports.readVersion = function (contents) { + return JSON.parse(contents).tracker.package.version; +} + +module.exports.writeVersion = function (contents, version) { + const json = JSON.parse(contents) + let indent = detectIndent(contents).indent + let newline = detectNewline(contents) + json.tracker.package.version = version + return stringifyPackage(json, indent, newline) +} +``` ## License diff --git a/command.js b/command.js index 1674aec74..95d15825a 100755 --- a/command.js +++ b/command.js @@ -5,6 +5,14 @@ const { START_OF_LAST_RELEASE_PATTERN } = require('./lib/lifecycles/changelog') const yargs = require('yargs') .usage('Usage: $0 [options]') + .option('packageFiles', { + default: defaults.packageFiles, + array: true + }) + .option('bumpFiles', { + default: defaults.bumpFiles, + array: true + }) .option('release-as', { alias: 'r', describe: 'Specify the release type manually (like npm version )', diff --git a/defaults.js b/defaults.js index fa5cb4287..fc88a5f61 100644 --- a/defaults.js +++ b/defaults.js @@ -23,4 +23,17 @@ Object.keys(spec.properties).forEach(propertyKey => { defaults[propertyKey] = property.default }) +defaults.packageFiles = [ + 'package.json', + 'bower.json', + 'manifest.json', + 'composer.json' +] + +defaults.bumpFiles = defaults.packageFiles.concat([ + 'package-lock.json', + 'npm-shrinkwrap.json', + 'composer.lock' +]) + module.exports = defaults diff --git a/index.js b/index.js index 00dbadd8b..9ee458584 100755 --- a/index.js +++ b/index.js @@ -6,8 +6,10 @@ const latestSemverTag = require('./lib/latest-semver-tag') const path = require('path') const printError = require('./lib/print-error') const tag = require('./lib/lifecycles/tag') +const { resolveUpdaterObjectFromArgument } = require('./lib/updaters') module.exports = function standardVersion (argv) { + const defaults = require('./defaults') /** * `--message` (`-m`) support will be removed in the next major version. */ @@ -24,19 +26,21 @@ module.exports = function standardVersion (argv) { } } + const args = Object.assign({}, defaults, argv) let pkg - bump.pkgFiles.forEach((filename) => { + args.packageFiles.forEach((packageFile) => { if (pkg) return - const pkgPath = path.resolve(process.cwd(), filename) + const updater = resolveUpdaterObjectFromArgument(packageFile) + const pkgPath = path.resolve(process.cwd(), updater.filename) try { - const data = fs.readFileSync(pkgPath, 'utf8') - pkg = JSON.parse(data) + const contents = fs.readFileSync(pkgPath, 'utf8') + pkg = { + version: updater.updater.readVersion(contents), + private: typeof updater.updater.isPrivate === 'function' ? updater.updater.isPrivate(contents) : false + } } catch (err) {} }) let newVersion - const defaults = require('./defaults') - const args = Object.assign({}, defaults, argv) - return Promise.resolve() .then(() => { if (!pkg && args.gitTagFallback) { diff --git a/lib/lifecycles/bump.js b/lib/lifecycles/bump.js index 86bb77455..b59c46cb5 100644 --- a/lib/lifecycles/bump.js +++ b/lib/lifecycles/bump.js @@ -3,8 +3,6 @@ const chalk = require('chalk') const checkpoint = require('../checkpoint') const conventionalRecommendedBump = require('conventional-recommended-bump') -const detectIndent = require('detect-indent') -const detectNewline = require('detect-newline') const figures = require('figures') const fs = require('fs') const DotGitignore = require('dotgitignore') @@ -12,9 +10,8 @@ const path = require('path') const presetLoader = require('../preset-loader') const runLifecycleScript = require('../run-lifecycle-script') const semver = require('semver') -const stringifyPackage = require('stringify-package') const writeFile = require('../write-file') - +const { resolveUpdaterObjectFromArgument } = require('../updaters') let configsToUpdate = {} function Bump (args, version) { @@ -51,19 +48,6 @@ Bump.getUpdatedConfigs = function () { return configsToUpdate } -Bump.pkgFiles = [ - 'package.json', - 'bower.json', - 'manifest.json', - 'composer.json' -] - -Bump.lockFiles = [ - 'package-lock.json', - 'npm-shrinkwrap.json', - 'composer.lock' -] - function getReleaseType (prerelease, expectedReleaseType, currentVersion) { if (isString(prerelease)) { if (isInPrerelease(currentVersion)) { @@ -154,32 +138,38 @@ function bumpVersion (releaseAs, currentVersion, args) { } /** - * attempt to update the version # in a collection of common config - * files, e.g., package.json, bower.json. - * + * attempt to update the version number in provided `bumpFiles` * @param args config object - * @param newVersion version # to update to. - * @return {string} + * @param newVersion version number to update to. + * @return void */ function updateConfigs (args, newVersion) { const dotgit = DotGitignore() - Bump.pkgFiles.concat(Bump.lockFiles).forEach(function (filename) { - const configPath = path.resolve(process.cwd(), filename) + args.bumpFiles.forEach(function (bumpFile) { + const updater = resolveUpdaterObjectFromArgument(bumpFile) + if (!updater) { + return + } + const configPath = path.resolve(process.cwd(), updater.filename) try { if (dotgit.ignore(configPath)) return const stat = fs.lstatSync(configPath) - if (stat.isFile()) { - const data = fs.readFileSync(configPath, 'utf8') - const indent = detectIndent(data).indent - const newline = detectNewline(data) - const config = JSON.parse(data) - checkpoint(args, 'bumping version in ' + filename + ' from %s to %s', [config.version, newVersion]) - config.version = newVersion - writeFile(args, configPath, stringifyPackage(config, indent, newline)) - // flag any config files that we modify the version # for - // as having been updated. - configsToUpdate[filename] = true - } + + if (!stat.isFile()) return + const contents = fs.readFileSync(configPath, 'utf8') + checkpoint( + args, + 'bumping version in ' + updater.filename + ' from %s to %s', + [updater.updater.readVersion(contents), newVersion] + ) + writeFile( + args, + configPath, + updater.updater.writeVersion(contents, newVersion) + ) + // flag any config files that we modify the version # for + // as having been updated. + configsToUpdate[updater.filename] = true } catch (err) { if (err.code !== 'ENOENT') console.warn(err.message) } diff --git a/lib/updaters/index.js b/lib/updaters/index.js new file mode 100644 index 000000000..84ed1a0fa --- /dev/null +++ b/lib/updaters/index.js @@ -0,0 +1,59 @@ +const path = require('path') +const JSON_BUMP_FILES = require('../../defaults').bumpFiles +const PLAIN_TEXT_BUMP_FILES = ['VERSION.txt', 'version.txt'] + +function getUpdaterByType (type) { + try { + return require(`./types/${type}`) + } catch (e) { + throw Error(`Unable to locate updated for provided type (${type}).`) + } +} + +function getUpdaterByFilename (filename) { + if (JSON_BUMP_FILES.includes(path.basename(filename))) { + return getUpdaterByType('json') + } + if (PLAIN_TEXT_BUMP_FILES.includes(filename)) { + return getUpdaterByType('plain-text') + } + throw Error( + `Unsupported file (${filename}) provided for bumping.\n Please specifcy the updater \`type\` or use a custom \`updater\`.` + ) +} + +function getCustomUpdater (updater) { + return require(path.resolve(process.cwd(), updater)) +} + +module.exports.resolveUpdaterObjectFromArgument = function (arg) { + /** + * If an Object was not provided, we assume it's the path/filename + * of the updater. + */ + let updater = arg + if (typeof updater !== 'object') { + updater = { + filename: arg + } + } + try { + if (updater.updater) { + updater.updater = getCustomUpdater(updater.updater) + } else if (updater.type) { + updater.updater = getUpdaterByType(updater.type) + } else { + updater.updater = getUpdaterByFilename(updater.filename) + } + } catch (err) { + if (err.code !== 'ENOENT') console.warn(err.message) + } + /** + * We weren't able to resolve an updater for the argument. + */ + if (!updater.updater) { + return false + } + + return updater +} diff --git a/lib/updaters/types/json.js b/lib/updaters/types/json.js new file mode 100644 index 000000000..4494aab76 --- /dev/null +++ b/lib/updaters/types/json.js @@ -0,0 +1,19 @@ +const stringifyPackage = require('stringify-package') +const detectIndent = require('detect-indent') +const detectNewline = require('detect-newline') + +module.exports.readVersion = function (contents) { + return JSON.parse(contents).version +} + +module.exports.writeVersion = function (contents, version) { + const json = JSON.parse(contents) + const indent = detectIndent(contents).indent + const newline = detectNewline(contents) + json.version = version + return stringifyPackage(json, indent, newline) +} + +module.exports.isPrivate = function (contents) { + return JSON.parse(contents).private +} diff --git a/lib/updaters/types/plain-text.js b/lib/updaters/types/plain-text.js new file mode 100644 index 000000000..18bcabed2 --- /dev/null +++ b/lib/updaters/types/plain-text.js @@ -0,0 +1,7 @@ +module.exports.readVersion = function (contents) { + return contents +} + +module.exports.writeVersion = function (_contents, version) { + return version +} diff --git a/test.js b/test.js index f0710ffaa..19120bccf 100644 --- a/test.js +++ b/test.js @@ -922,6 +922,74 @@ describe('standard-version', function () { }) }) + describe('custom `bumpFiles` support', function () { + it('mix.exs + version.txt', function () { + // @todo This file path is relative to the `tmp` directory, which is a little confusing + fs.copyFileSync('../test/mocks/mix.exs', 'mix.exs') + fs.copyFileSync('../test/mocks/version.txt', 'version.txt') + fs.copyFileSync('../test/mocks/updater/customer-updater.js', 'custom-updater.js') + commit('feat: first commit') + shell.exec('git tag -a v1.0.0 -m "my awesome first release"') + commit('feat: new feature!') + return require('./index')({ + silent: true, + bumpFiles: [ + 'version.txt', + { + filename: 'mix.exs', + updater: 'custom-updater.js' + } + ] + }) + .then(() => { + fs.readFileSync('mix.exs', 'utf-8').should.contain('version: "1.1.0"') + fs.readFileSync('version.txt', 'utf-8').should.equal('1.1.0') + }) + }) + + it('bumps a custom `plain-text` file', function () { + fs.copyFileSync('../test/mocks/VERSION-1.0.0.txt', 'VERSION_TRACKER.txt') + commit('feat: first commit') + return require('./index')({ + silent: true, + bumpFiles: [ + { + filename: 'VERSION_TRACKER.txt', + type: 'plain-text' + } + ] + }) + .then(() => { + fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('1.1.0') + }) + }) + }) + + describe('custom `packageFiles` support', function () { + it('reads and writes to a custom `plain-text` file', function () { + fs.copyFileSync('../test/mocks/VERSION-6.3.1.txt', 'VERSION_TRACKER.txt') + commit('feat: yet another commit') + return require('./index')({ + silent: true, + packageFiles: [ + { + filename: 'VERSION_TRACKER.txt', + type: 'plain-text' + } + ], + bumpFiles: [ + { + filename: 'VERSION_TRACKER.txt', + type: 'plain-text' + } + ] + }) + .then(() => { + fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0') + }) + }) + }) + describe('npm-shrinkwrap.json support', function () { beforeEach(function () { writeNpmShrinkwrapJson('1.0.0') diff --git a/test/mocks/VERSION-1.0.0.txt b/test/mocks/VERSION-1.0.0.txt new file mode 100644 index 000000000..afaf360d3 --- /dev/null +++ b/test/mocks/VERSION-1.0.0.txt @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/test/mocks/VERSION-6.3.1.txt b/test/mocks/VERSION-6.3.1.txt new file mode 100644 index 000000000..39ee137ba --- /dev/null +++ b/test/mocks/VERSION-6.3.1.txt @@ -0,0 +1 @@ +6.3.1 \ No newline at end of file diff --git a/test/mocks/mix.exs b/test/mocks/mix.exs new file mode 100644 index 000000000..bc539135e --- /dev/null +++ b/test/mocks/mix.exs @@ -0,0 +1,28 @@ +defmodule StandardVersion.MixProject do + use Mix.Project + + def project do + [ + app: :standard_version, + version: "0.1.0", + elixir: "~> 1.9", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/test/mocks/updater/customer-updater.js b/test/mocks/updater/customer-updater.js new file mode 100644 index 000000000..989aba3c0 --- /dev/null +++ b/test/mocks/updater/customer-updater.js @@ -0,0 +1,12 @@ +const REPLACER = /version: "(.*)"/ + +module.exports.readVersion = function (contents) { + return REPLACER.exec(contents)[1] +} + +module.exports.writeVersion = function (contents, version) { + return contents.replace( + REPLACER.exec(contents)[0], + `version: "${version}"` + ) +} diff --git a/test/mocks/version.txt b/test/mocks/version.txt new file mode 100644 index 000000000..e69de29bb