Skip to content

Commit

Permalink
Merge pull request #6 from laysent/master
Browse files Browse the repository at this point in the history
Fix Windows Issues
  • Loading branch information
dflourusso authored Oct 7, 2017
2 parents 0396b93 + 85809c8 commit ec4e2e5
Show file tree
Hide file tree
Showing 8 changed files with 798 additions and 55 deletions.
10 changes: 6 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
sudo: false
language: node_js
node_js:
- "0.8"
- "0.12"
- "0.10"
- "0.11"
before_install:
- "npm install -g npm@1.4.x"
- "4"
- "iojs"
script:
- "npm run test-travis"
after_script:
- "npm install coveralls@2.11.x && cat coverage/lcov.info | coveralls"
matrix:
fast_finish: true
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

**pre-push** is a pre-push hook installer for `git`. It will ensure that
your `npm test` (or other specified scripts) passes before you can push your
changes. This all conveniently configured in your package.json.
changes. This all conveniently configured in your `package.json`.

But don't worry, you can still force a push by telling `git` to skip the
`pre-push` hooks by simply pushing using `--no-verify`.
Expand All @@ -32,7 +32,7 @@ the existing `pre-push` file in your `.git/hooks` folder. Existing

`pre-push` 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-push` is not limited to just running your `npm test`'s during the
push hook. It's also capable of running every other script that you've
Expand All @@ -45,7 +45,7 @@ could ensure that:
- Contribution licenses signed etc.

The only thing you need to do is add a `pre-push` array to your `package.json`
that specifies which scripts should be run and in which order:
that specifies which scripts you want to have ran and in which order:

```js
{
Expand Down Expand Up @@ -96,7 +96,7 @@ The examples above are all the same. In addition to configuring which scripts
should be ran you can also configure the following options:

- **silent** Don't output the prefixed `pre-push:` messages when things fail
or when we have nothing to run. Should a boolean.
or when we have nothing to run. Should be a boolean.
- **colors** Don't output colors when we write messages. Should be a boolean.
- **template** Path to a file who's content should be used as template for the
git push body.
Expand Down
56 changes: 41 additions & 15 deletions hook
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
#!/bin/bash
#!/usr/bin/env bash

git stash -q --keep-index
HAS_NODE=`which node 2> /dev/null || which nodejs 2> /dev/null || which iojs 2> /dev/null`

#
# There are some issues with Source Tree because paths are not set correctly for
# the given environment. Sourcing the bash_profile seems to resolve this for bash users,
# sourcing the zshrc for zshell users.
#
# https://answers.atlassian.com/questions/140339/sourcetree-hook-failing-because-paths-don-t-seem-to-be-set-correctly
#
function source_home_file {
file="$HOME/$1"
[[ -f "${file}" ]] && source "${file}"
}

if [[ -z "$HAS_NODE" ]]; then
source_home_file ".bash_profile" || source_home_file ".zshrc" || source_home_file ".bashrc" || true
fi

NODE=`which node 2> /dev/null`
NODEJS=`which nodejs 2> /dev/null`
IOJS=`which iojs 2> /dev/null`
LOCAL="/usr/local/bin/node"
BINARY=

if [[ -n $NODE ]]; then
"$NODE" $("$NODE" -e "console.log(require.resolve('pre-push'))")
elif [[ -n $NODEJS ]]; then
"$NODEJS" $("$NODEJS" -e "console.log(require.resolve('pre-push'))")
elif [[ -n $IOJS ]]; then
"$IOJS" $("$IOJS" -e "console.log(require.resolve('pre-push'))")
elif [[ -x $LOCAL ]]; then
"$LOCAL" $("$LOCAL" -e "console.log(require.resolve('pre-push'))")
#
# Figure out which binary we need to use for our script execution.
#
if [[ -n "$NODE" ]]; then
BINARY="$NODE"
elif [[ -n "$NODEJS" ]]; then
BINARY="$NODEJS"
elif [[ -n "$IOJS" ]]; then
BINARY="$IOJS"
elif [[ -x "$LOCAL" ]]; then
BINARY="$LOCAL"
fi
RESULT=$?

git stash pop -q
[ $RESULT -ne 0 ] && exit 1
exit 0

#
# Add --dry-run cli flag support so we can execute this hook without side affects
# and see if it works in the current environment
#
if [[ $* == *--dry-run* ]]; then
if [[ -z "$BINARY" ]]; then
exit 1
fi
else
"$BINARY" "$("$BINARY" -e "console.log(require.resolve('pre-push'))")"
fi
39 changes: 23 additions & 16 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
'use strict';

var spawn = require('cross-spawn')
, shelly = require('shelljs')
, which = require('which')
, path = require('path')
, util = require('util')
, tty = require('tty');
Expand Down Expand Up @@ -56,6 +56,20 @@ Object.defineProperty(Hook.prototype, 'colors', {
}
});

/**
* Execute a binary.
*
* @param {String} bin Binary that needs to be executed
* @param {Array} args Arguments for the binary
* @returns {Object}
* @api private
*/
Hook.prototype.exec = function exec(bin, args) {
return spawn.sync(bin, args, {
stdio: 'pipe'
});
};

/**
* Parse the package.json so we can create an normalize it's contents to
* a usable configuration structure.
Expand Down Expand Up @@ -134,7 +148,7 @@ Hook.prototype.log = function log(lines, exit) {
*/
Hook.prototype.initialize = function initialize() {
['git', 'npm'].forEach(function each(binary) {
try { this[binary] = this.shelly.which(binary); }
try { this[binary] = which.sync(binary); }
catch (e) {}
}, this);

Expand All @@ -145,7 +159,7 @@ Hook.prototype.initialize = function initialize() {
if (!this.npm) {
try {
process.env.PATH += path.delimiter + path.dirname(process.env._);
this.npm = this.shelly.which('npm');
this.npm = which.sync('npm');
} catch (e) {
return this.log(this.format(Hook.log.binary, 'npm'), 0);
}
Expand All @@ -156,30 +170,24 @@ Hook.prototype.initialize = function initialize() {
//
if (!this.git) return this.log(this.format(Hook.log.binary, 'git'), 0);

this.root = this.shelly.exec(this.git +' rev-parse --show-toplevel', {
silent: true
});

this.root = this.exec(this.git, ['rev-parse', '--show-toplevel']);

if (this.root.code) return this.log(Hook.log.root, 0);
this.root = this.root.output.trim();

this.root = this.root.stdout.toString().trim();

try {
this.json = require(path.join(this.root, 'package.json'));
this.parse();
} catch (e) { return this.log(this.format(Hook.log.json, e.message), 0); }



//
// If we have a git template we should configure it before checking for
// scripts so it will still be applied even if we don't have anything to
// execute.
//
if (this.config.template) {
this.shelly.exec(this.git +' config push.template "'+ this.config.template +'"', {
silent: true
});
this.exec(this.git, ['config', 'push.template', this.config.template]);
}

if (!this.config.run) return this.log(Hook.log.run, 0);
Expand All @@ -200,7 +208,7 @@ Hook.prototype.run = function runner() {

//
// There's a reason on why we're using an async `spawn` here instead of the
// `shelly.exec`. The sync `exec` is a hack that writes writes a file to
// `shelljs.exec`. The sync `exec` is a hack that writes writes a file to
// disk and they poll with sync fs calls to see for results. The problem is
// that the way they capture the output which us using input redirection and
// this doesn't have the required `isAtty` information that libraries use to
Expand All @@ -226,7 +234,6 @@ Hook.prototype.run = function runner() {
* @public
*/
Hook.prototype.format = util.format;
Hook.prototype.shelly = shelly;

/**
* The various of error and status messages that we can output.
Expand Down
85 changes: 77 additions & 8 deletions install.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
//
var fs = require('fs')
, path = require('path')
, hook = path.join(__dirname, './hook')
, root = path.resolve(__dirname, '../..')
, os = require('os')
, hook = path.join(__dirname, 'hook')
, root = path.resolve(__dirname, '..', '..')
, exists = fs.existsSync || path.existsSync;

//
Expand All @@ -15,15 +16,60 @@ var fs = require('fs')
// `pre-push` 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')
, prepush = path.resolve(hooks, 'pre-push');

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-push:');
console.log('pre-push: 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-push:');
console.log('pre-push: 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');
prepush = path.resolve(hooks, 'pre-push');
}
}

//
// 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-push:');
console.log('pre-push: Not found any .git folder for installing pre-push hook');
return;
}

var hooks = path.resolve(git, 'hooks')
, prepush = path.resolve(hooks, 'pre-push');

if (!exists(hooks)) fs.mkdirSync(hooks);

//
Expand All @@ -46,16 +92,39 @@ if (exists(prepush) && !fs.lstatSync(prepush).isSymbolicLink()) {
try { fs.unlinkSync(prepush); }
catch (e) {}

// Create generic prepush hook that launches this modules hook (as well
// as stashing - unstashing the unstaged changes)
// TODO: we could keep launching the old pre-push scripts
var hookRelativeUnixPath = hook.replace(root, '.');

if(os.platform() === 'win32') {
hookRelativeUnixPath = hookRelativeUnixPath.replace(/[\\\/]+/g, '/');
}

var prepushContent = '#!/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(hook, prepush, 'file'); }
try { fs.writeFileSync(prepush, prepushContent); }
catch (e) {
console.error('pre-push:');
console.error('pre-push: Failed to symlink the hook file in your .git/hooks folder because:');
console.error('pre-push: Failed to create the hook file in your .git/hooks folder because:');
console.error('pre-push: '+ e.message);
console.error('pre-push: The hook was not installed.');
console.error('pre-push:');
}

try { fs.chmodSync(prepush, '777'); }
catch (e) {
console.error('pre-push:');
console.error('pre-push: chmod 0777 the pre-push file in your .git/hooks folder because:');
console.error('pre-push: '+ e.message);
console.error('pre-push:');
}
14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "pre-push",
"version": "0.1.1",
"version": "0.1.2",
"description": "Automatically install pre-push hooks for your npm modules.",
"main": "index.js",
"scripts": {
Expand Down Expand Up @@ -30,14 +30,14 @@
"homepage": "https://github.com/dflourusso/pre-push",
"license": "MIT",
"dependencies": {
"spawn-sync": "^1.0.15",
"cross-spawn": "^5.1.0",
"shelljs": "0.3.x"
"cross-spawn": "^5.0.1",
"spawn-sync": "^1.0.15",
"which": "1.2.x"
},
"devDependencies": {
"assume": "1.1.x",
"istanbul": "0.3.x",
"mocha": "2.1.x",
"assume": "~1.5.0",
"istanbul": "0.4.x",
"mocha": "~3.3.0",
"pre-push": "git://github.com/dflourusso/pre-push.git"
},
"bugs": {
Expand Down
21 changes: 20 additions & 1 deletion uninstall.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,26 @@
var fs = require('fs')
, path = require('path')
, exists = fs.existsSync || path.existsSync
, prepush = path.resolve(__dirname, '../..', '.git', 'hooks', 'pre-push');
, 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-push hook, if it exists
//
var prepush = path.resolve(git, 'hooks', 'pre-push');

//
// Bail out if we don't have pre-push file, it might be removed manually.
Expand Down
Loading

0 comments on commit ec4e2e5

Please sign in to comment.