Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Windows Issues #6

Merged
merged 4 commits into from
Oct 7, 2017
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
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