Skip to content

Commit 0cfaca7

Browse files
authored
Ignore directories and show warning for unexecutable files (#41)
1 parent 1f404e8 commit 0cfaca7

File tree

3 files changed

+58
-61
lines changed

3 files changed

+58
-61
lines changed

lib/fs-helpers.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,18 @@ var helpers = {
5353
} :
5454
function (path) {
5555
return fs.existsSync(path);
56-
}
56+
},
57+
58+
/**
59+
* @param {String} path
60+
* @returns {Boolean}
61+
*/
62+
isExecutable: function (path) {
63+
var stats = fs.lstatSync(path);
64+
return (stats.mode & 1) ||
65+
(stats.mode & 8) && process.getgid && stats.gid === process.getgid() ||
66+
(stats.mode & 64) && process.getuid && stats.uid === process.getuid();
67+
}
5768
};
5869

5970
module.exports = helpers;

lib/git-hooks.js

+16-35
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ var util = require('util');
33
var spawn = require('child_process').spawn;
44
var fs = require('fs');
55
var fsHelpers = require('./fs-helpers');
6-
var exec = require('child_process').exec;
76

87
var HOOKS_DIRNAME = 'hooks';
98
var HOOKS_OLD_DIRNAME = 'hooks.old';
@@ -26,19 +25,6 @@ var HOOKS = [
2625
'update'
2726
];
2827

29-
/**
30-
* @param {String[]} hooks List of hook's paths with possible excludes(.gitignore files)
31-
* @param {function} callback Filtered hooks will be passed in the callback
32-
*/
33-
function excludeIgnoredPaths(hooks, callback) {
34-
exec('git check-ignore ' + hooks.join(' '), function (error, output) {
35-
// intentionally ignore errors
36-
callback(hooks.filter(function (hookName) {
37-
return output.indexOf(hookName) === -1;
38-
}));
39-
});
40-
}
41-
4228
module.exports = {
4329
/**
4430
* Installs git hooks.
@@ -123,12 +109,22 @@ module.exports = {
123109

124110
if (fsHelpers.exists(hooksDirname)) {
125111
var list = fs.readdirSync(hooksDirname);
126-
var hooks = list.map(function (hookName) {
127-
return path.resolve(hooksDirname, hookName);
128-
});
129-
excludeIgnoredPaths(hooks, function (filteredHooks) {
130-
runHooks(filteredHooks, args, callback);
131-
});
112+
var hooks = list
113+
.map(function (hookName) {
114+
return path.resolve(hooksDirname, hookName);
115+
})
116+
.filter(function (hookPath) {
117+
var isFile = fs.lstatSync(hookPath).isFile();
118+
var isExecutable = fs.lstatSync(hookPath).isFile() && fsHelpers.isExecutable(hookPath);
119+
120+
if (isFile && !isExecutable) {
121+
console.warn('[GIT-HOOKS WARNING] Non-executable file ' + hookPath + ' is skipped');
122+
}
123+
124+
return isFile && isExecutable;
125+
});
126+
127+
runHooks(hooks, args, callback);
132128
} else {
133129
callback(0);
134130
}
@@ -162,16 +158,6 @@ function runHooks(hooks, args, callback) {
162158
}
163159
}
164160

165-
/**
166-
* @param {fs.Stats} stats
167-
* @returns {Boolean}
168-
*/
169-
function isExecutable(stats) {
170-
return (stats.mode & 1) ||
171-
(stats.mode & 8) && process.getgid && stats.gid === process.getgid() ||
172-
(stats.mode & 64) && process.getuid && stats.uid === process.getuid();
173-
}
174-
175161
/**
176162
* Spawns hook as a separate process.
177163
*
@@ -180,11 +166,6 @@ function isExecutable(stats) {
180166
* @returns {ChildProcess}
181167
*/
182168
function spawnHook(hookName, args) {
183-
var stats = fs.statSync(hookName);
184-
var isHookExecutable = stats && stats.isFile() && isExecutable(stats);
185-
if (!isHookExecutable) {
186-
throw new Error('Cannot execute hook: ' + hookName + '. Please check file permissions.');
187-
}
188169
args = args || [];
189170
return spawn(hookName, args, {stdio: 'inherit'});
190171
}

tests/run.test.js

+30-25
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ var GIT_ROOT = SANDBOX_PATH + '.git/';
88
var GIT_HOOKS = GIT_ROOT + 'hooks';
99
var PRECOMMIT_HOOK_PATH = GIT_HOOKS + '/pre-commit';
1010
var PROJECT_PRECOMMIT_HOOK = SANDBOX_PATH + '.githooks/pre-commit/';
11-
var GIT_IGNORE = SANDBOX_PATH + '.gitignore';
1211

1312
function createHook(path, content) {
1413
fs.writeFileSync(path, '#!/bin/bash\n' + content);
@@ -38,15 +37,39 @@ describe('git-hook runner', function () {
3837
});
3938

4039
describe('and a hook is unexecutable', function () {
40+
var oldConsoleWarn = console.warn;
41+
var consoleLogOutput = '';
42+
var logFile = SANDBOX_PATH + 'hello.log';
43+
beforeEach(function () {
44+
var hookPath = PROJECT_PRECOMMIT_HOOK + 'hello';
45+
fs.writeFileSync(hookPath, '#!/bin/bash\n' + 'echo hello > ' + logFile);
46+
console.warn = function (str) {
47+
consoleLogOutput += str;
48+
};
49+
});
50+
51+
afterEach(function () {
52+
console.warn = oldConsoleWarn;
53+
});
54+
55+
it('should skip it', function (done) {
56+
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
57+
code.should.equal(0);
58+
consoleLogOutput.should.match(/^\[GIT-HOOKS WARNING\]/);
59+
fs.existsSync(logFile).should.be.false;
60+
done();
61+
});
62+
});
63+
});
64+
65+
describe('and directory in hooks directory is found', function () {
4166
beforeEach(function () {
42-
var logFile = SANDBOX_PATH + 'hello.log';
43-
fs.writeFileSync(PROJECT_PRECOMMIT_HOOK + 'hello', '#!/bin/bash\n' + 'echo hello > ' + logFile);
67+
fsHelpers.makeDir(PROJECT_PRECOMMIT_HOOK + '/zzzz');
4468
});
4569

46-
it('should return an error', function (done) {
47-
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code, error) {
48-
code.should.equal(1);
49-
error.should.be.ok;
70+
it('should skip it', function (done) {
71+
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
72+
code.should.equal(0);
5073
done();
5174
});
5275
});
@@ -107,23 +130,5 @@ describe('git-hook runner', function () {
107130
});
108131
});
109132
});
110-
111-
describe('do not run git-ignored scripts from hooks directory', function () {
112-
var ignoreFilename = 'ignore-me';
113-
var ignoreContent = ignoreFilename + '\n*.swp';
114-
115-
beforeEach(function () {
116-
fs.writeFileSync(GIT_IGNORE, ignoreContent);
117-
fs.writeFileSync(PROJECT_PRECOMMIT_HOOK + ignoreFilename, 'exit -1');
118-
fs.writeFileSync(PROJECT_PRECOMMIT_HOOK + 'test.swp', 'exit -1');
119-
});
120-
121-
it('should ignore file with wrong permissions in hooks directory', function (done) {
122-
gitHooks.run(PRECOMMIT_HOOK_PATH, [], function (code) {
123-
code.should.equal(0);
124-
done();
125-
});
126-
});
127-
});
128133
});
129134
});

0 commit comments

Comments
 (0)