diff --git a/README.md b/README.md index 137671e..16f6b2e 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ the existing `pre-commit` file in your `.git/hooks` folder. Existing `pre-commit` will try to run your `npm test` command in the root of the git repository by default unless it's the default value that is set by the `npm -init` script. +init` script. But `pre-commit` is not limited to just running your `npm test`'s during the commit hook. It's also capable of running every other script that you've @@ -116,7 +116,7 @@ or as `"pre-commit.{key}` key properties in the `package.json`: It's all the same. Different styles so use what matches your project. To learn more about the scripts, please read the official `npm` documentation: -https://npmjs.org/doc/scripts.html +https://docs.npmjs.com/misc/scripts And to learn more about git hooks read: diff --git a/hook b/hook index 62436f3..1bf23b8 100755 --- a/hook +++ b/hook @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash HAS_NODE=`which node 2> /dev/null || which nodejs 2> /dev/null || which iojs 2> /dev/null` @@ -38,7 +38,7 @@ elif [[ -x "$LOCAL" ]]; then fi # -# Add --dry-run cli flag support so we can execute this hook without side affects +# Add --dry-run cli flag support so we can execute this hook without side effects # and see if it works in the current environment # if [[ $* == *--dry-run* ]]; then diff --git a/index.js b/index.js index a20646d..89f5a9d 100644 --- a/index.js +++ b/index.js @@ -198,7 +198,7 @@ Hook.prototype.initialize = function initialize() { // execute. // if (this.config.template) { - this.exec(this.git, ['config', 'commit.template', '"'+ this.config.template +'"']); + this.exec(this.git, ['config', 'commit.template', this.config.template]); } if (!this.config.run) return this.log(Hook.log.run, 0); diff --git a/install.js b/install.js index 2aa0dcb..e24fc0d 100644 --- a/install.js +++ b/install.js @@ -5,7 +5,7 @@ // var fs = require('fs') , path = require('path') - , spawn = require('cross-spawn') + , os = require('os') , hook = path.join(__dirname, 'hook') , root = path.resolve(__dirname, '..', '..') , exists = fs.existsSync || path.existsSync; @@ -16,15 +16,60 @@ var fs = require('fs') // `pre-commit` file. The path needs to be absolute in order for the symlinking // to work correctly. // -var git = path.resolve(root, '.git') - , hooks = path.resolve(git, 'hooks') - , precommit = path.resolve(hooks, 'pre-commit'); + +var git = getGitFolderPath(root); + +// Function to recursively finding .git folder +function getGitFolderPath(currentPath) { + var git = path.resolve(currentPath, '.git') + + if (!exists(git) || !fs.lstatSync(git).isDirectory()) { + console.log('pre-commit:'); + console.log('pre-commit: Not found .git folder in', git); + + var newPath = path.resolve(currentPath, '..'); + + // Stop if we on top folder + if (currentPath === newPath) { + return null; + } + + return getGitFolderPath(newPath); + } + + console.log('pre-commit:'); + console.log('pre-commit: Found .git folder in', git); + return git; +} + +// +// Resolve git directory for submodules +// +if (exists(git) && fs.lstatSync(git).isFile()) { + var gitinfo = fs.readFileSync(git).toString() + , gitdirmatch = /gitdir: (.+)/.exec(gitinfo) + , gitdir = gitdirmatch.length == 2 ? gitdirmatch[1] : null; + + if (gitdir !== null) { + git = path.resolve(root, gitdir); + hooks = path.resolve(git, 'hooks'); + precommit = path.resolve(hooks, 'pre-commit'); + } +} // // Bail out if we don't have an `.git` directory as the hooks will not get // triggered. If we do have directory create a hooks folder if it doesn't exist. // -if (!exists(git) || !fs.lstatSync(git).isDirectory()) return; +if (!git) { + console.log('pre-commit:'); + console.log('pre-commit: Not found any .git folder for installing pre-commit hook'); + return; +} + +var hooks = path.resolve(git, 'hooks') + , precommit = path.resolve(hooks, 'pre-commit'); + if (!exists(hooks)) fs.mkdirSync(hooks); // @@ -47,16 +92,39 @@ if (exists(precommit) && !fs.lstatSync(precommit).isSymbolicLink()) { try { fs.unlinkSync(precommit); } catch (e) {} +// Create generic precommit hook that launches this modules hook (as well +// as stashing - unstashing the unstaged changes) +// TODO: we could keep launching the old pre-commit scripts +var hookRelativeUnixPath = hook.replace(root, '.'); + +if(os.platform() === 'win32') { + hookRelativeUnixPath = hookRelativeUnixPath.replace(/[\\\/]+/g, '/'); +} + +var precommitContent = '#!/usr/bin/env bash' + os.EOL + + hookRelativeUnixPath + os.EOL + + 'RESULT=$?' + os.EOL + + '[ $RESULT -ne 0 ] && exit 1' + os.EOL + + 'exit 0' + os.EOL; + // // It could be that we do not have rights to this folder which could cause the // installation of this module to completely fail. We should just output the // error instead destroying the whole npm install process. // -try { fs.symlinkSync(path.relative(hooks, hook), precommit, 'file'); } +try { fs.writeFileSync(precommit, precommitContent); } catch (e) { console.error('pre-commit:'); - console.error('pre-commit: Failed to symlink the hook file in your .git/hooks folder because:'); + console.error('pre-commit: Failed to create the hook file in your .git/hooks folder because:'); console.error('pre-commit: '+ e.message); console.error('pre-commit: The hook was not installed.'); console.error('pre-commit:'); } + +try { fs.chmodSync(precommit, '777'); } +catch (e) { + console.error('pre-commit:'); + console.error('pre-commit: chmod 0777 the pre-commit file in your .git/hooks folder because:'); + console.error('pre-commit: '+ e.message); + console.error('pre-commit:'); +} diff --git a/package.json b/package.json index f166b47..dc1ff70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pre-commit", - "version": "1.1.3", + "version": "1.2.2", "description": "Automatically install pre-commit hooks for your npm modules.", "main": "index.js", "scripts": { @@ -30,13 +30,14 @@ "homepage": "https://github.com/observing/pre-commit", "license": "MIT", "dependencies": { - "cross-spawn": "2.0.x", + "cross-spawn": "^5.0.1", + "spawn-sync": "^1.0.15", "which": "1.2.x" }, "devDependencies": { - "assume": "1.4.x", + "assume": "~1.5.0", "istanbul": "0.4.x", - "mocha": "2.4.x", + "mocha": "~3.3.0", "pre-commit": "git://github.com/observing/pre-commit.git" } } diff --git a/uninstall.js b/uninstall.js index a0d2fd0..7c47903 100644 --- a/uninstall.js +++ b/uninstall.js @@ -3,7 +3,26 @@ var fs = require('fs') , path = require('path') , exists = fs.existsSync || path.existsSync - , precommit = path.resolve(__dirname, '../..', '.git', 'hooks', 'pre-commit'); + , root = path.resolve(__dirname, '..', '..') + , git = path.resolve(root, '.git'); + +// +// Resolve git directory for submodules +// +if (exists(git) && fs.lstatSync(git).isFile()) { + var gitinfo = fs.readFileSync(git).toString() + , gitdirmatch = /gitdir: (.+)/.exec(gitinfo) + , gitdir = gitdirmatch.length == 2 ? gitdirmatch[1] : null; + + if (gitdir !== null) { + git = path.resolve(root, gitdir); + } +} + +// +// Location of pre-commit hook, if it exists +// +var precommit = path.resolve(git, 'hooks', 'pre-commit'); // // Bail out if we don't have pre-commit file, it might be removed manually.