Skip to content
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
19 changes: 12 additions & 7 deletions lib/git-hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,10 @@ module.exports = {
*
* @param {String} filename Path to git hook.
* @param {String[]} [args] Git hook arguments.
* @param {String} input Input string to be passed into each hook's standard input.
* @param {Function} callback
*/
run: function (filename, args, callback) {
run: function (filename, args, input, callback) {
var hookName = path.basename(filename);
var hooksDirname = path.resolve(path.dirname(filename), '../../.githooks', hookName);

Expand All @@ -127,7 +128,7 @@ module.exports = {
return path.resolve(hooksDirname, hookName);
});
excludeIgnoredPaths(hooks, function (filteredHooks) {
runHooks(filteredHooks, args, callback);
runHooks(filteredHooks, args, input, callback);
});
} else {
callback(0);
Expand All @@ -140,19 +141,20 @@ module.exports = {
*
* @param {String[]} hooks List of hook names to execute.
* @param {String[]} args
* @param {String} input Input string to be passed into each hook's standard input.
* @param {Function} callback
*/
function runHooks(hooks, args, callback) {
function runHooks(hooks, args, input, callback) {
if (!hooks.length) {
callback(0);
return;
}

try {
var hook = spawnHook(hooks.shift(), args);
var hook = spawnHook(hooks.shift(), args, input);
hook.on('close', function (code) {
if (code === 0) {
runHooks(hooks, args, callback);
runHooks(hooks, args, input, callback);
} else {
callback(code);
}
Expand All @@ -177,15 +179,18 @@ function isExecutable(stats) {
*
* @param {String} hookName
* @param {String[]} args
* @param {String} input Input string to be passed into the hook's standard input.
* @returns {ChildProcess}
*/
function spawnHook(hookName, args) {
function spawnHook(hookName, args, input) {
var stats = fs.statSync(hookName);
var isHookExecutable = stats && stats.isFile() && isExecutable(stats);
if (!isHookExecutable) {
throw new Error('Cannot execute hook: ' + hookName + '. Please check file permissions.');
}
return spawn(hookName, args, {stdio: 'inherit'});
var childProcess = spawn(hookName, args, {stdio: ['pipe', process.stdout, process.stderr]});
childProcess.stdin.end(input, 'utf8');
return childProcess;
}

/**
Expand Down
51 changes: 32 additions & 19 deletions lib/hook-template.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,36 @@
#!/usr/bin/env node

try {
/**
* require('git-hooks') isn't used to support case when node_modules is put in subdirectory.
* .git
* .githooks
* www
* node_modules
*/
require('%s/git-hooks').run(__filename, process.argv.slice(2), function (code, error) {
if (error) {
console.error('[GIT-HOOKS ERROR] ' + error.message);
}
process.exit(code);
});
} catch (e) {
console.error('[GIT-HOOKS ERROR] ' + e.message);
var input = '';

process.stdin.setEncoding('utf8');

process.stdin.on('readable', function () {
var chunk;
if ((chunk = process.stdin.read()) !== null) {
input += chunk;
}
});

process.stdin.on('end', function () {
try {
/**
* require('git-hooks') isn't used to support case when node_modules is put in subdirectory.
* .git
* .githooks
* www
* node_modules
*/
require('%s/git-hooks').run(__filename, process.argv.slice(2), input, function (code, error) {
if (error) {
console.error('[GIT-HOOKS ERROR] ' + error.message);
}
process.exit(code);
});
} catch (e) {
console.error('[GIT-HOOKS ERROR] ' + e.message);

if (e.code === 'MODULE_NOT_FOUND') {
console.error('[GIT-HOOKS ERROR] Please reinstall git-hooks to fix this error');
if (e.code === 'MODULE_NOT_FOUND') {
console.error('[GIT-HOOKS ERROR] Please reinstall git-hooks to fix this error');
}
}
}
});
43 changes: 33 additions & 10 deletions tests/run.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require('chai').should();
var fs = require('fs');
var exec = require('child_process').exec;
var gitHooks = require('../lib/git-hooks');
var fsHelpers = require('../lib/fs-helpers');

Expand All @@ -26,7 +27,7 @@ describe('git-hook runner', function () {
});

it('should works without hooks', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], '', function (code) {
code.should.equal(0);
done();
});
Expand All @@ -44,7 +45,7 @@ describe('git-hook runner', function () {
});

it('should return an error', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code, error) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], '', function (code, error) {
code.should.equal(1);
error.should.be.ok;
done();
Expand All @@ -62,7 +63,7 @@ describe('git-hook runner', function () {
});

it('should run it one by one', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], '', function (code) {
code.should.equal(0);
hooks.forEach(function (name) {
var logFile = SANDBOX_PATH + name + '.log';
Expand All @@ -76,20 +77,42 @@ describe('git-hook runner', function () {
describe('and work without errors', function () {
var logFile = SANDBOX_PATH + 'hello.log';
beforeEach(function () {
createHook(PROJECT_PRECOMMIT_HOOK + 'hello', 'echo "Hello, world! ${@:1}" > ' + logFile);
createHook(
PROJECT_PRECOMMIT_HOOK + 'hello',
'input=`cat`; echo -e "Hello, world!\n${@:1}\n$input" > ' + logFile
);
});

it('should pass all arguments to them', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, ['I', 'am', 'working', 'properly!'], function () {
fs.readFileSync(logFile).toString().should.equal('Hello, world! I am working properly!\n');
gitHooks.run(PRECOMMIT_HOOK_PATH, ['I', 'am', 'working', 'properly!'], '', function () {
fs.readFileSync(logFile).toString().trim().should.equal('Hello, world!\nI am working properly!');
done();
});
});

describe('if standard input is passed in', function () {
it('should read it properly', function (done) {
exec('echo "I am working properly!" | ' + PRECOMMIT_HOOK_PATH, function () {
fs.readFileSync(logFile).toString().trim()
.should.equal('Hello, world!\n\nI am working properly!');
done();
});
});

it('should pass it to them', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], 'I am working properly!', function () {
fs.readFileSync(logFile).toString().trim()
.should.equal('Hello, world!\n\nI am working properly!');
done();
});
});

});

it('should run a hook with success status', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], '', function (code) {
code.should.equal(0);
fs.readFileSync(logFile).toString().should.equal('Hello, world! \n');
fs.readFileSync(logFile).toString().trim().should.equal('Hello, world!');
done();
});
});
Expand All @@ -101,7 +124,7 @@ describe('git-hook runner', function () {
});

it('should run a hook and return error', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], '', function (code) {
code.should.equal(255);
done();
});
Expand All @@ -119,7 +142,7 @@ describe('git-hook runner', function () {
});

it('should ignore file with wrong permissions in hooks directory', function (done) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
gitHooks.run(PRECOMMIT_HOOK_PATH, [], '', function (code) {
code.should.equal(0);
done();
});
Expand Down