Skip to content

Add cross-platform support for pre/post version script hooks. #17

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,9 @@ sudo: false
language: node_js
matrix:
include:
- node_js: 0.10
- node_js: 0.11
- node_js: 0.12
- node_js: 4
- node_js: 5
- node_js: 6
- node_js: 7

# Mocha sometimes fails (not sure why), so when that happens, try again.
# If it fails a second time, then the CI build fails.
Expand Down
23 changes: 23 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Test against this version of Node.js
environment:
matrix:
- nodejs_version: "Stable"
- nodejs_version: "LTS"

# Install scripts. (runs after repo cloning)
install:
# Get the latest stable version of Node.js or io.js
- ps: Install-Product node $env:nodejs_version
# install modules
- npm install

# Post-install test scripts.
test_script:
# Output useful info for debugging.
- node --version
- npm --version
# run tests
- npm test

# Don't actually build.
build: off
13 changes: 11 additions & 2 deletions bin/bump.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ else {
bumpManifests(manifests, options)
.then(function() {
api.grep(manifests, options);
manifests.forEach(function (manifest) {
api.runNpmScriptIfExists(manifest, 'version');
});
})
.then(function() {
api.git(manifests, options);
Expand Down Expand Up @@ -98,7 +101,7 @@ function bumpManifests(manifests, options) {
* @returns {Promise}
*/
function bumpManifest(manifest, defaultBumpType, options) {
return new Promise(function(resolve) {
return new Promise(function(resolve, reject) {
if (options.prompt) {
// Prompt the user for the type of bump to perform
var version = api.versionInfo(manifest, options);
Expand All @@ -120,6 +123,7 @@ function bumpManifest(manifest, defaultBumpType, options) {
]
})
.then(function(answer) {
api.runNpmScriptIfExists(manifest, 'preversion');
bump(answer.bump);
});
}
Expand All @@ -133,11 +137,16 @@ function bumpManifest(manifest, defaultBumpType, options) {
options.prepatch ? 'prepatch' :
options.prerelease ? 'prerelease' :
defaultBumpType;
api.runNpmScriptIfExists(manifest, 'preversion');
bump(bumpType);
}

function bump(bumpType) {
api.bump(manifest, bumpType, options);
try {
api.bump(manifest, bumpType, options);
} catch(ex) {
reject(ex);
}
resolve(bumpType);
}
});
Expand Down
56 changes: 47 additions & 9 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ module.exports = {
manifests: manifests,
versionInfo: versionInfo,
bump: bump,
runNpmScriptIfExists: runNpmScriptIfExists,
grep: grep,
git: git
};
Expand Down Expand Up @@ -84,11 +85,31 @@ function bump(manifest, type, options) {

// Save the file
var usedIndent = indent(fs.readFileSync(pkgPath, 'utf8')).indent || ' ';

fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, usedIndent));

console.log('%s Updated %s to %s', logSymbols.success, manifest, current.version);
}

/**
* Takes npm scripts object and if a script exists, runs the script.
*
* @param {string} manifest - The name of the manifest file (e.g. "package.json")
* @param {string} script - Name of the script ("preversion", "postversion", etc.)
*/
function runNpmScriptIfExists(manifest, script) {
if (manifest !== 'package.json') {
return;
}
var pkgPath = path.join(cwd, manifest);
var pkg = require(pkgPath);
var pkgScripts = pkg.scripts;

if (pkgScripts && pkgScripts[script]) {
exec('npm', ['run', script]);
}
}

/**
* Performs text replacements of the old version string with the new version string
* in the {@link options.grep} file list.
Expand Down Expand Up @@ -160,6 +181,10 @@ function git(manifests, options) {
console.log(logSymbols.success, 'Git tag');
}

manifests.forEach(function (manifest) {
runNpmScriptIfExists(manifest, 'postversion');
});

// Git Push
if (options.push) {
exec('git', ['push']);
Expand All @@ -175,18 +200,31 @@ function git(manifests, options) {
*
* @param {string} command - The command to run
* @param {string[]} args - An array of arguments to pass
* @param {object} [opts] - Options to pass to spawnSync
* @param {boolean} [opts.skipCustomErrorLog] - Use to skip logging the error when using try-catch
*/
function exec(command, args) {
var result = spawnSync(command, args);
var output = result.stdout.toString() || result.stderr.toString();

function exec(command, args, opts) {
opts = opts || {};
var skipCustomErrorLog = opts.skipCustomErrorLog;
delete opts.skipCustomErrorLog;
//use which to make sure that node spawn can find correct executable, at the moment this is used as
//a windows fallback, until https://github.com/libuv/libuv/pull/358 is fixed
var which = require('npm-which')(process.cwd());
var pathToCommand = which.sync(command);
var result = spawnSync(pathToCommand, args, opts);
if (result.status || result.error) {
console.error(
chalk.bold.red('Error running command:'),
chalk.reset.magenta(command, args.join(' '))
);
if (!skipCustomErrorLog) {
console.error(
chalk.bold.red('Error running command:'),
chalk.reset.magenta(command, args.join(' '))
);
}

var err = new Error(output);
var err = result.error;
if (!result.error) {
var output = result.stdout.toString() || result.stderr.toString();
err = new Error(output);
}
err.status = result.status;
throw err;
}
Expand Down
8 changes: 6 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"scripts": {
"lint": "jshint . --verbose && jscs . --verbose",
"build": "npm run lint",
"test": "mocha --bail --recursive tests/fixtures tests/specs",
"test": "mocha --bail --recursive --timeout 13000 tests/fixtures tests/specs",
"upgrade": "npm-check -u",
"bump": "node bin/bump.js --prompt --tag --push --all",
"release": "npm run upgrade && npm run build && npm test && npm run bump && npm publish",
Expand All @@ -42,6 +42,7 @@
"lodash": "^4.16.4",
"mocha": "^3.1.1",
"npm-check": "^5.4.0",
"npm-which": "^3.0.1",
"sinon": "^1.17.6"
},
"dependencies": {
Expand All @@ -61,5 +62,8 @@
],
"bin": {
"bump": "bin/bump.js"
},
"engines": {
"node": ">=4"
}
}
}
7 changes: 7 additions & 0 deletions tests/fixtures/.tmp/git.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\bin\git" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\bin\git" %*
)
7 changes: 7 additions & 0 deletions tests/fixtures/bin/git.cmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\git" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\git" %*
)
16 changes: 14 additions & 2 deletions tests/fixtures/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ beforeEach(function() {
fs.mkdirSync(tmpPath)
}
else {
// Make sure the .tmp directory is empty
// Make sure the .tmp directory is empty, except for git.cmd,
// which is a hack to make appveyor build work (has some issues with PATH modification)
fs.readdirSync(tmpPath).forEach(function(file) {
if (file === 'git.cmd') {
return;
}
fs.unlinkSync(path.join(tmpPath, file));
});
}
Expand Down Expand Up @@ -58,7 +62,15 @@ function bump(args, initialJSON, finalJSON) {
// Modify the PATH environment variable so bump will execute our fake `git`
var binPath = path.join(__dirname, 'bin');
fs.chmodSync(path.join(binPath, 'git'), '0777');
var env = _.clone(process.env);
var env = _.reduce(process.env, function (memo, value, key) {
//in windows, environment variables are case insensitive and can be defined as Path, path, ...
//since in code below we're relying on PATH to be uppercase, make sure it is cloned as uppercase
if (/^path$/i.test(key)) {
memo.PATH = value;
}
memo[key] = value;
return memo;
}, {});
env.PATH = binPath + path.delimiter + env.PATH;

// Run bump
Expand Down
30 changes: 30 additions & 0 deletions tests/specs/version-hooks.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use strict';

var fs = require('fs');
var path = require('path');
var helper = require('../fixtures/helper');

describe('npm version hooks', function() {
it('should commit temp file created by version hooks', function() {
helper.bump('--major --commit', {
version: '1.0.0',
scripts: {
preversion: 'echo Hi > preversion.txt',
version: 'echo Hi > version.txt',
postversion: 'echo Hi > postversion.txt'
}
}, {
version: '2.0.0',
scripts: {
preversion: 'echo Hi > preversion.txt',
version: 'echo Hi > version.txt',
postversion: 'echo Hi > postversion.txt'
}
});
['preversion.txt', 'version.txt', 'postversion.txt'].forEach(function (file) {
require('chai').expect(
fs.existsSync(path.join(__dirname, '../fixtures/.tmp/' + file)) || file + ' to exist'
).to.be.true;
});
});
});