diff --git a/lib/git-hooks.js b/lib/git-hooks.js index 9191ff5..c7c46e7 100644 --- a/lib/git-hooks.js +++ b/lib/git-hooks.js @@ -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); @@ -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); @@ -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); } @@ -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; } /** diff --git a/lib/hook-template.js b/lib/hook-template.js index 6105f0f..7dad813 100644 --- a/lib/hook-template.js +++ b/lib/hook-template.js @@ -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'); + } } -} +}); diff --git a/tests/run.test.js b/tests/run.test.js index 2588be2..6ff8eef 100644 --- a/tests/run.test.js +++ b/tests/run.test.js @@ -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'); @@ -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(); }); @@ -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(); @@ -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'; @@ -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(); }); }); @@ -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(); }); @@ -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(); });