Skip to content

Commit

Permalink
feat: custom 'bumpFiles' and 'packageFiles' support (#372)
Browse files Browse the repository at this point in the history
Co-Authored-By: Sébastien Règne <[email protected]>
  • Loading branch information
2 people authored and tommywo committed Dec 6, 2019
1 parent d557372 commit 564d948
Show file tree
Hide file tree
Showing 14 changed files with 397 additions and 125 deletions.
226 changes: 144 additions & 82 deletions README.md

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions command.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 <major|minor|patch>)',
Expand Down
13 changes: 13 additions & 0 deletions defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
18 changes: 11 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand All @@ -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) {
Expand Down
62 changes: 26 additions & 36 deletions lib/lifecycles/bump.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,15 @@
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')
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) {
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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)
}
Expand Down
59 changes: 59 additions & 0 deletions lib/updaters/index.js
Original file line number Diff line number Diff line change
@@ -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
}
19 changes: 19 additions & 0 deletions lib/updaters/types/json.js
Original file line number Diff line number Diff line change
@@ -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
}
7 changes: 7 additions & 0 deletions lib/updaters/types/plain-text.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module.exports.readVersion = function (contents) {
return contents
}

module.exports.writeVersion = function (_contents, version) {
return version
}
68 changes: 68 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand Down
1 change: 1 addition & 0 deletions test/mocks/VERSION-1.0.0.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0
1 change: 1 addition & 0 deletions test/mocks/VERSION-6.3.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6.3.1
28 changes: 28 additions & 0 deletions test/mocks/mix.exs
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions test/mocks/updater/customer-updater.js
Original file line number Diff line number Diff line change
@@ -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}"`
)
}
Empty file added test/mocks/version.txt
Empty file.

0 comments on commit 564d948

Please sign in to comment.