Skip to content

Commit 564d948

Browse files
jbottiglieroregseb
authored andcommittedDec 6, 2019
feat: custom 'bumpFiles' and 'packageFiles' support (#372)
Co-Authored-By: Sébastien Règne <regseb@users.noreply.github.com>
1 parent d557372 commit 564d948

14 files changed

+397
-125
lines changed
 

‎README.md

+144-82
Large diffs are not rendered by default.

‎command.js

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ const { START_OF_LAST_RELEASE_PATTERN } = require('./lib/lifecycles/changelog')
55

66
const yargs = require('yargs')
77
.usage('Usage: $0 [options]')
8+
.option('packageFiles', {
9+
default: defaults.packageFiles,
10+
array: true
11+
})
12+
.option('bumpFiles', {
13+
default: defaults.bumpFiles,
14+
array: true
15+
})
816
.option('release-as', {
917
alias: 'r',
1018
describe: 'Specify the release type manually (like npm version <major|minor|patch>)',

‎defaults.js

+13
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,17 @@ Object.keys(spec.properties).forEach(propertyKey => {
2323
defaults[propertyKey] = property.default
2424
})
2525

26+
defaults.packageFiles = [
27+
'package.json',
28+
'bower.json',
29+
'manifest.json',
30+
'composer.json'
31+
]
32+
33+
defaults.bumpFiles = defaults.packageFiles.concat([
34+
'package-lock.json',
35+
'npm-shrinkwrap.json',
36+
'composer.lock'
37+
])
38+
2639
module.exports = defaults

‎index.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ const latestSemverTag = require('./lib/latest-semver-tag')
66
const path = require('path')
77
const printError = require('./lib/print-error')
88
const tag = require('./lib/lifecycles/tag')
9+
const { resolveUpdaterObjectFromArgument } = require('./lib/updaters')
910

1011
module.exports = function standardVersion (argv) {
12+
const defaults = require('./defaults')
1113
/**
1214
* `--message` (`-m`) support will be removed in the next major version.
1315
*/
@@ -24,19 +26,21 @@ module.exports = function standardVersion (argv) {
2426
}
2527
}
2628

29+
const args = Object.assign({}, defaults, argv)
2730
let pkg
28-
bump.pkgFiles.forEach((filename) => {
31+
args.packageFiles.forEach((packageFile) => {
2932
if (pkg) return
30-
const pkgPath = path.resolve(process.cwd(), filename)
33+
const updater = resolveUpdaterObjectFromArgument(packageFile)
34+
const pkgPath = path.resolve(process.cwd(), updater.filename)
3135
try {
32-
const data = fs.readFileSync(pkgPath, 'utf8')
33-
pkg = JSON.parse(data)
36+
const contents = fs.readFileSync(pkgPath, 'utf8')
37+
pkg = {
38+
version: updater.updater.readVersion(contents),
39+
private: typeof updater.updater.isPrivate === 'function' ? updater.updater.isPrivate(contents) : false
40+
}
3441
} catch (err) {}
3542
})
3643
let newVersion
37-
const defaults = require('./defaults')
38-
const args = Object.assign({}, defaults, argv)
39-
4044
return Promise.resolve()
4145
.then(() => {
4246
if (!pkg && args.gitTagFallback) {

‎lib/lifecycles/bump.js

+26-36
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,15 @@
33
const chalk = require('chalk')
44
const checkpoint = require('../checkpoint')
55
const conventionalRecommendedBump = require('conventional-recommended-bump')
6-
const detectIndent = require('detect-indent')
7-
const detectNewline = require('detect-newline')
86
const figures = require('figures')
97
const fs = require('fs')
108
const DotGitignore = require('dotgitignore')
119
const path = require('path')
1210
const presetLoader = require('../preset-loader')
1311
const runLifecycleScript = require('../run-lifecycle-script')
1412
const semver = require('semver')
15-
const stringifyPackage = require('stringify-package')
1613
const writeFile = require('../write-file')
17-
14+
const { resolveUpdaterObjectFromArgument } = require('../updaters')
1815
let configsToUpdate = {}
1916

2017
function Bump (args, version) {
@@ -51,19 +48,6 @@ Bump.getUpdatedConfigs = function () {
5148
return configsToUpdate
5249
}
5350

54-
Bump.pkgFiles = [
55-
'package.json',
56-
'bower.json',
57-
'manifest.json',
58-
'composer.json'
59-
]
60-
61-
Bump.lockFiles = [
62-
'package-lock.json',
63-
'npm-shrinkwrap.json',
64-
'composer.lock'
65-
]
66-
6751
function getReleaseType (prerelease, expectedReleaseType, currentVersion) {
6852
if (isString(prerelease)) {
6953
if (isInPrerelease(currentVersion)) {
@@ -154,32 +138,38 @@ function bumpVersion (releaseAs, currentVersion, args) {
154138
}
155139

156140
/**
157-
* attempt to update the version # in a collection of common config
158-
* files, e.g., package.json, bower.json.
159-
*
141+
* attempt to update the version number in provided `bumpFiles`
160142
* @param args config object
161-
* @param newVersion version # to update to.
162-
* @return {string}
143+
* @param newVersion version number to update to.
144+
* @return void
163145
*/
164146
function updateConfigs (args, newVersion) {
165147
const dotgit = DotGitignore()
166-
Bump.pkgFiles.concat(Bump.lockFiles).forEach(function (filename) {
167-
const configPath = path.resolve(process.cwd(), filename)
148+
args.bumpFiles.forEach(function (bumpFile) {
149+
const updater = resolveUpdaterObjectFromArgument(bumpFile)
150+
if (!updater) {
151+
return
152+
}
153+
const configPath = path.resolve(process.cwd(), updater.filename)
168154
try {
169155
if (dotgit.ignore(configPath)) return
170156
const stat = fs.lstatSync(configPath)
171-
if (stat.isFile()) {
172-
const data = fs.readFileSync(configPath, 'utf8')
173-
const indent = detectIndent(data).indent
174-
const newline = detectNewline(data)
175-
const config = JSON.parse(data)
176-
checkpoint(args, 'bumping version in ' + filename + ' from %s to %s', [config.version, newVersion])
177-
config.version = newVersion
178-
writeFile(args, configPath, stringifyPackage(config, indent, newline))
179-
// flag any config files that we modify the version # for
180-
// as having been updated.
181-
configsToUpdate[filename] = true
182-
}
157+
158+
if (!stat.isFile()) return
159+
const contents = fs.readFileSync(configPath, 'utf8')
160+
checkpoint(
161+
args,
162+
'bumping version in ' + updater.filename + ' from %s to %s',
163+
[updater.updater.readVersion(contents), newVersion]
164+
)
165+
writeFile(
166+
args,
167+
configPath,
168+
updater.updater.writeVersion(contents, newVersion)
169+
)
170+
// flag any config files that we modify the version # for
171+
// as having been updated.
172+
configsToUpdate[updater.filename] = true
183173
} catch (err) {
184174
if (err.code !== 'ENOENT') console.warn(err.message)
185175
}

‎lib/updaters/index.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const path = require('path')
2+
const JSON_BUMP_FILES = require('../../defaults').bumpFiles
3+
const PLAIN_TEXT_BUMP_FILES = ['VERSION.txt', 'version.txt']
4+
5+
function getUpdaterByType (type) {
6+
try {
7+
return require(`./types/${type}`)
8+
} catch (e) {
9+
throw Error(`Unable to locate updated for provided type (${type}).`)
10+
}
11+
}
12+
13+
function getUpdaterByFilename (filename) {
14+
if (JSON_BUMP_FILES.includes(path.basename(filename))) {
15+
return getUpdaterByType('json')
16+
}
17+
if (PLAIN_TEXT_BUMP_FILES.includes(filename)) {
18+
return getUpdaterByType('plain-text')
19+
}
20+
throw Error(
21+
`Unsupported file (${filename}) provided for bumping.\n Please specifcy the updater \`type\` or use a custom \`updater\`.`
22+
)
23+
}
24+
25+
function getCustomUpdater (updater) {
26+
return require(path.resolve(process.cwd(), updater))
27+
}
28+
29+
module.exports.resolveUpdaterObjectFromArgument = function (arg) {
30+
/**
31+
* If an Object was not provided, we assume it's the path/filename
32+
* of the updater.
33+
*/
34+
let updater = arg
35+
if (typeof updater !== 'object') {
36+
updater = {
37+
filename: arg
38+
}
39+
}
40+
try {
41+
if (updater.updater) {
42+
updater.updater = getCustomUpdater(updater.updater)
43+
} else if (updater.type) {
44+
updater.updater = getUpdaterByType(updater.type)
45+
} else {
46+
updater.updater = getUpdaterByFilename(updater.filename)
47+
}
48+
} catch (err) {
49+
if (err.code !== 'ENOENT') console.warn(err.message)
50+
}
51+
/**
52+
* We weren't able to resolve an updater for the argument.
53+
*/
54+
if (!updater.updater) {
55+
return false
56+
}
57+
58+
return updater
59+
}

‎lib/updaters/types/json.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const stringifyPackage = require('stringify-package')
2+
const detectIndent = require('detect-indent')
3+
const detectNewline = require('detect-newline')
4+
5+
module.exports.readVersion = function (contents) {
6+
return JSON.parse(contents).version
7+
}
8+
9+
module.exports.writeVersion = function (contents, version) {
10+
const json = JSON.parse(contents)
11+
const indent = detectIndent(contents).indent
12+
const newline = detectNewline(contents)
13+
json.version = version
14+
return stringifyPackage(json, indent, newline)
15+
}
16+
17+
module.exports.isPrivate = function (contents) {
18+
return JSON.parse(contents).private
19+
}

‎lib/updaters/types/plain-text.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
module.exports.readVersion = function (contents) {
2+
return contents
3+
}
4+
5+
module.exports.writeVersion = function (_contents, version) {
6+
return version
7+
}

‎test.js

+68
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,74 @@ describe('standard-version', function () {
922922
})
923923
})
924924

925+
describe('custom `bumpFiles` support', function () {
926+
it('mix.exs + version.txt', function () {
927+
// @todo This file path is relative to the `tmp` directory, which is a little confusing
928+
fs.copyFileSync('../test/mocks/mix.exs', 'mix.exs')
929+
fs.copyFileSync('../test/mocks/version.txt', 'version.txt')
930+
fs.copyFileSync('../test/mocks/updater/customer-updater.js', 'custom-updater.js')
931+
commit('feat: first commit')
932+
shell.exec('git tag -a v1.0.0 -m "my awesome first release"')
933+
commit('feat: new feature!')
934+
return require('./index')({
935+
silent: true,
936+
bumpFiles: [
937+
'version.txt',
938+
{
939+
filename: 'mix.exs',
940+
updater: 'custom-updater.js'
941+
}
942+
]
943+
})
944+
.then(() => {
945+
fs.readFileSync('mix.exs', 'utf-8').should.contain('version: "1.1.0"')
946+
fs.readFileSync('version.txt', 'utf-8').should.equal('1.1.0')
947+
})
948+
})
949+
950+
it('bumps a custom `plain-text` file', function () {
951+
fs.copyFileSync('../test/mocks/VERSION-1.0.0.txt', 'VERSION_TRACKER.txt')
952+
commit('feat: first commit')
953+
return require('./index')({
954+
silent: true,
955+
bumpFiles: [
956+
{
957+
filename: 'VERSION_TRACKER.txt',
958+
type: 'plain-text'
959+
}
960+
]
961+
})
962+
.then(() => {
963+
fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('1.1.0')
964+
})
965+
})
966+
})
967+
968+
describe('custom `packageFiles` support', function () {
969+
it('reads and writes to a custom `plain-text` file', function () {
970+
fs.copyFileSync('../test/mocks/VERSION-6.3.1.txt', 'VERSION_TRACKER.txt')
971+
commit('feat: yet another commit')
972+
return require('./index')({
973+
silent: true,
974+
packageFiles: [
975+
{
976+
filename: 'VERSION_TRACKER.txt',
977+
type: 'plain-text'
978+
}
979+
],
980+
bumpFiles: [
981+
{
982+
filename: 'VERSION_TRACKER.txt',
983+
type: 'plain-text'
984+
}
985+
]
986+
})
987+
.then(() => {
988+
fs.readFileSync('VERSION_TRACKER.txt', 'utf-8').should.equal('6.4.0')
989+
})
990+
})
991+
})
992+
925993
describe('npm-shrinkwrap.json support', function () {
926994
beforeEach(function () {
927995
writeNpmShrinkwrapJson('1.0.0')

‎test/mocks/VERSION-1.0.0.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.0

‎test/mocks/VERSION-6.3.1.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6.3.1

‎test/mocks/mix.exs

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
defmodule StandardVersion.MixProject do
2+
use Mix.Project
3+
4+
def project do
5+
[
6+
app: :standard_version,
7+
version: "0.1.0",
8+
elixir: "~> 1.9",
9+
start_permanent: Mix.env() == :prod,
10+
deps: deps()
11+
]
12+
end
13+
14+
# Run "mix help compile.app" to learn about applications.
15+
def application do
16+
[
17+
extra_applications: [:logger]
18+
]
19+
end
20+
21+
# Run "mix help deps" to learn about dependencies.
22+
defp deps do
23+
[
24+
# {:dep_from_hexpm, "~> 0.3.0"},
25+
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
26+
]
27+
end
28+
end
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
const REPLACER = /version: "(.*)"/
2+
3+
module.exports.readVersion = function (contents) {
4+
return REPLACER.exec(contents)[1]
5+
}
6+
7+
module.exports.writeVersion = function (contents, version) {
8+
return contents.replace(
9+
REPLACER.exec(contents)[0],
10+
`version: "${version}"`
11+
)
12+
}

‎test/mocks/version.txt

Whitespace-only changes.

0 commit comments

Comments
 (0)
Please sign in to comment.