├── .github └── workflows │ └── npm.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── Makefile ├── README.md ├── bin ├── git-scripts ├── git-scripts-install ├── git-scripts-run ├── git-scripts-uninstall ├── hooks │ ├── applypatch-msg │ ├── commit-msg │ ├── hook │ ├── post-applypatch │ ├── post-checkout │ ├── post-commit │ ├── post-merge │ ├── post-receive │ ├── post-rewrite │ ├── post-update │ ├── pre-applypatch │ ├── pre-auto-gc │ ├── pre-commit │ ├── pre-push │ ├── pre-rebase │ ├── pre-receive │ ├── prepare-commit-msg │ └── update ├── install └── uninstall ├── index.js ├── lib ├── git-scripts.js └── index.js ├── package.json ├── scripts ├── package-publish-config-tag.sh └── pre-push.js └── test ├── bin.git-scripts.js ├── bin.hooks.hook.js ├── bin.install.js ├── bin.uninstall.js └── index.js /.github/workflows/npm.yml: -------------------------------------------------------------------------------- 1 | name: NPM 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: Build 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest] 11 | node: [13] 12 | 13 | runs-on: ${{ matrix.os }} 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | 21 | - name: Install Dependencies 22 | run: npm install 23 | 24 | - name: Test 25 | run: npm test 26 | 27 | publish: 28 | if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/v')) 29 | name: Publish 30 | needs: [build] 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v2 34 | - uses: actions/setup-node@v1 35 | with: 36 | node-version: 13 37 | registry-url: https://registry.npmjs.org/ 38 | 39 | - name: Install Dependencies 40 | run: | 41 | sudo apt-get install jq 42 | npm install 43 | NODE_JQ_SKIP_INSTALL_BINARY=true 44 | npm install -g pkg-jq 45 | 46 | - name: Set Publish Config 47 | run: ./scripts/package-publish-config-tag.sh 48 | 49 | - name: Check Branch 50 | id: check-branch 51 | run: | 52 | if [[ ${{ github.ref }} =~ ^refs/heads/(master|v[0-9]+\.[0-9]+.*)$ ]]; then 53 | echo ::set-output name=match::true 54 | fi # See: https://stackoverflow.com/a/58869470/1123955 55 | - name: Is A Publish Branch 56 | if: steps.check-branch.outputs.match == 'true' 57 | run: | 58 | NAME=$(npx pkg-jq -r .name) 59 | VERSION=$(npx pkg-jq -r .version) 60 | if npx version-exists "$NAME" "$VERSION" 61 | then echo "$NAME@$VERSION exists on NPM, skipped." 62 | else npm run build && npm publish 63 | fi 64 | env: 65 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} 66 | - name: Is Not A Publish Branch 67 | if: steps.check-branch.outputs.match != 'true' 68 | run: echo 'Not A Publish Branch' 69 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | lib-cov 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | pids 12 | logs 13 | results 14 | 15 | npm-debug.log 16 | node_modules 17 | 18 | .DS_Store 19 | package-lock.json 20 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | Makefile 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 13 4 | - 12 5 | - 11 6 | - 10 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Naoyuki Kanezawa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | MOCHA_OPTS = --check-leaks --bail 3 | REPORTER = dot 4 | 5 | build: 6 | @cp ./bin/hooks/hook ./bin/hooks/applypatch-msg 7 | @cp ./bin/hooks/hook ./bin/hooks/pre-applypatch 8 | @cp ./bin/hooks/hook ./bin/hooks/post-applypatch 9 | @cp ./bin/hooks/hook ./bin/hooks/pre-commit 10 | @cp ./bin/hooks/hook ./bin/hooks/prepare-commit-msg 11 | @cp ./bin/hooks/hook ./bin/hooks/commit-msg 12 | @cp ./bin/hooks/hook ./bin/hooks/post-commit 13 | @cp ./bin/hooks/hook ./bin/hooks/pre-rebase 14 | @cp ./bin/hooks/hook ./bin/hooks/post-checkout 15 | @cp ./bin/hooks/hook ./bin/hooks/post-merge 16 | @cp ./bin/hooks/hook ./bin/hooks/pre-receive 17 | @cp ./bin/hooks/hook ./bin/hooks/update 18 | @cp ./bin/hooks/hook ./bin/hooks/post-receive 19 | @cp ./bin/hooks/hook ./bin/hooks/post-update 20 | @cp ./bin/hooks/hook ./bin/hooks/pre-auto-gc 21 | @cp ./bin/hooks/hook ./bin/hooks/post-rewrite 22 | 23 | test: 24 | @NODE_ENV=test ./node_modules/.bin/mocha \ 25 | --reporter $(REPORTER) \ 26 | $(MOCHA_OPTS) 27 | 28 | .PHONY: build test 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # git-scripts [![NPM Version](https://badge.fury.io/js/git-scripts.svg)](https://www.npmjs.com/package/git-scripts) 2 | 3 | ![NPM](https://github.com/zixia/git-scripts/workflows/NPM/badge.svg) 4 | 5 | Manage [git hooks](http://git-scm.com/book/en/Customizing-Git-Git-Hooks) with the "git.scripts" field of the package.json like [npm-scripts](https://npmjs.org/doc/scripts.html). 6 | 7 | ```json 8 | { 9 | "name": "your-project", 10 | "version": "0.0.0", 11 | "description": "", 12 | "devDependencies": { 13 | "git-scripts": "*" 14 | }, 15 | "git": { 16 | "scripts": { 17 | "pre-commit": "npm test", 18 | "post-merge": "npm install" 19 | } 20 | } 21 | } 22 | ``` 23 | 24 | See also [this module's package.json](https://github.com/nkzawa/git-scripts/blob/master/package.json) as an example. 25 | 26 | ## Installation 27 | $ npm install git-scripts 28 | 29 | NOTE: `.git/hooks` of your project is replaced when installed. You can restore it by uninstalling the module. 30 | 31 | ## Description 32 | git-scripts supports all hooks including the following: 33 | 34 | * applypatch-msg 35 | * pre-applypatch 36 | * post-applypatch 37 | * pre-commit 38 | * prepare-commit-msg 39 | * commit-msg 40 | * post-commit 41 | * pre-rebase 42 | * post-checkout 43 | * post-merge 44 | * pre-receive 45 | * update 46 | * post-receive 47 | * post-update 48 | * pre-auto-gc 49 | * post-rewrite 50 | * pre-push 51 | 52 | ## CLI 53 | $ npm install -g git-scripts 54 | 55 | ``` 56 | Usage: git-scripts [options] 57 | 58 | Options: 59 | 60 | -h, --help output usage information 61 | -V, --version output the version number 62 | 63 | Commands: 64 | 65 | install [path] install git-scripts 66 | uninstall [path] uninstall git-scripts 67 | run [args ...] run arbitrary scripts 68 | ``` 69 | 70 | NOTE: Usually, you don't need CLI. Hooks are enabled automatically when you install the module locally. 71 | 72 | ## Using Programmatically 73 | If you would like to use git-scripts programmatically, you can do that. 74 | 75 | ```js 76 | var scripts = require('git-scripts'); 77 | var project = scripts('somewhere/yourproject'); 78 | 79 | project.install(function(err) { 80 | if (err) throw err; 81 | console.log('git-scripts was installed.'); 82 | 83 | project.run('pre-commit', function(err) { 84 | if (err) throw err; 85 | console.log('pre-commit script succeeded.'); 86 | }); 87 | }); 88 | ``` 89 | 90 | ### Documentation 91 | ```js 92 | var scripts = require('git-scripts'); 93 | var project = scripts('somewhere/yourproject'); 94 | ``` 95 | 96 | #### scripts([path]) 97 | Create a git-scripts instance for `path`. 98 | 99 | #### project.install([callback]) 100 | Install git-scripts. 101 | 102 | #### project.uninstall([callback]) 103 | Uninstall git-scripts. 104 | 105 | #### project.run(name, [args ...], [callback]) 106 | Run an arbitrary command of `name`. 107 | 108 | ## Author 109 | 110 | Naoyuki Kanezawa 111 | 112 | ## Maintainer 113 | 114 | [Huan LI](https://github.com/huan) ([李卓桓](http://linkedin.com/in/zixia)) 115 | 116 | ## License 117 | 118 | MIT 119 | -------------------------------------------------------------------------------- /bin/git-scripts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var program = require('commander') 4 | , pkg = require('../package.json') 5 | , fs = require('fs'); 6 | 7 | program 8 | .version(pkg.version) 9 | .usage(' [options]') 10 | .parse(process.argv); 11 | 12 | program.on('--help', function() { 13 | console.log(' Commands:'); 14 | console.log(); 15 | console.log(' install [path] install git-scripts'); 16 | console.log(' uninstall [path] uninstall git-scripts'); 17 | console.log(' run [args ...] run arbitrary scripts'); 18 | console.log(); 19 | process.exit(); 20 | }); 21 | 22 | var args = program.args.slice(1) 23 | , cmd = program.args[0]; 24 | 25 | if (!cmd) { 26 | process.stdout.write(program.helpInformation()); 27 | program.emit('--help'); 28 | process.exit(); 29 | } 30 | 31 | // execute the subcommand. 32 | 33 | /** 34 | * Issue #11 35 | */ 36 | bin = __dirname + '/git-scripts-' + cmd 37 | 38 | process.argv = [ 39 | process.argv[0], // /usr/bin/node 40 | bin, 41 | ...args, 42 | ] 43 | 44 | require(bin) 45 | 46 | // var bin = __dirname + '/git-scripts-' + cmd 47 | // , proc = require('child_process').spawn(bin, args, {stdio: 'inherit', customFds: [0, 1, 2]}); 48 | 49 | // proc.on('close', function(code) { 50 | // process.exit(code); 51 | // }); 52 | -------------------------------------------------------------------------------- /bin/git-scripts-install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var program = require('commander') 4 | , scripts = require('../'); 5 | 6 | 7 | program 8 | .usage('[path]') 9 | .parse(process.argv); 10 | 11 | var path = program.args.shift() 12 | , project = scripts(path); 13 | 14 | project.isGitRepo(function(result) { 15 | if (!result) { 16 | console.warn('Not a git repository.'); 17 | return; 18 | } 19 | 20 | project.installed(function(result) { 21 | if (result) { 22 | // git-scripts hooks are already installed. 23 | return; 24 | } 25 | 26 | project.install(function(err) { 27 | if (err) throw err; 28 | }); 29 | }); 30 | }); 31 | 32 | 33 | -------------------------------------------------------------------------------- /bin/git-scripts-run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var program = require('commander') 4 | , scripts = require('../') 5 | , project = scripts(); 6 | 7 | 8 | program 9 | .usage(' [args ...]') 10 | .parse(process.argv); 11 | 12 | var name = program.args[0]; 13 | if (!name) { 14 | process.stdout.write(program.helpInformation()); 15 | process.exit(); 16 | } 17 | 18 | project.run.apply(project, program.args.concat([function(err) { 19 | if (err) throw err; 20 | }])); 21 | 22 | -------------------------------------------------------------------------------- /bin/git-scripts-uninstall: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var program = require('commander') 4 | , scripts = require('../'); 5 | 6 | 7 | program 8 | .usage('[path]') 9 | .parse(process.argv); 10 | 11 | var path = program.args.shift() 12 | , project = scripts(path); 13 | 14 | project.isGitRepo(function(result) { 15 | if (!result) { 16 | console.warn('Not a git repository.'); 17 | return; 18 | } 19 | 20 | project.installed(function(result) { 21 | if (!result) { 22 | console.warn('git-scripts hooks are not installed.'); 23 | return; 24 | } 25 | 26 | project.uninstall(function(err) { 27 | if (err) throw err; 28 | }); 29 | }); 30 | }); 31 | 32 | -------------------------------------------------------------------------------- /bin/hooks/applypatch-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/hook: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/post-applypatch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/post-checkout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/post-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/post-merge: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/post-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/post-rewrite: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | const args = process.argv.slice(2); 9 | const readline = require('readline').createInterface({ 10 | input: process.stdin, 11 | }); 12 | 13 | readline 14 | .on('line', (line) => { 15 | const arr = line.split(' ') 16 | args.push(...arr) 17 | }) 18 | .on('close', () => { 19 | project.run(name, args, function (err) { 20 | if (err) { 21 | // If the hook actually succeeded, but you want git to think it failed, return 42 22 | if (err.code !== 42) { 23 | console.error('Failed to exec ' + name + ' hook script') 24 | } 25 | process.exit(err.code); 26 | } 27 | }); 28 | }) 29 | .on('error', (err) => { 30 | console.error('Failed to exec ' + name + ' hook script') 31 | process.exit(1); 32 | }) 33 | -------------------------------------------------------------------------------- /bin/hooks/post-update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/pre-applypatch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/pre-auto-gc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | const args = process.argv.slice(2); 9 | const readline = require('readline').createInterface({ 10 | input: process.stdin, 11 | }); 12 | 13 | readline 14 | .on('line', (line) => { 15 | const arr = line.split(' ') 16 | args.push(...arr) 17 | }) 18 | .on('close', () => { 19 | project.run(name, args, function (err) { 20 | if (err) { 21 | // If the hook actually succeeded, but you want git to think it failed, return 42 22 | if (err.code !== 42) { 23 | console.error('Failed to exec ' + name + ' hook script') 24 | } 25 | process.exit(err.code); 26 | } 27 | }); 28 | }) 29 | .on('error', (err) => { 30 | console.error('Failed to exec ' + name + ' hook script') 31 | process.exit(1); 32 | }) 33 | -------------------------------------------------------------------------------- /bin/hooks/pre-rebase: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/pre-receive: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | const args = process.argv.slice(2); 9 | const readline = require('readline').createInterface({ 10 | input: process.stdin, 11 | }); 12 | 13 | readline 14 | .on('line', (line) => { 15 | const arr = line.split(' ') 16 | args.push(...arr) 17 | }) 18 | .on('close', () => { 19 | project.run(name, args, function (err) { 20 | if (err) { 21 | // If the hook actually succeeded, but you want git to think it failed, return 42 22 | if (err.code !== 42) { 23 | console.error('Failed to exec ' + name + ' hook script') 24 | } 25 | process.exit(err.code); 26 | } 27 | }); 28 | }) 29 | .on('error', (err) => { 30 | console.error('Failed to exec ' + name + ' hook script') 31 | process.exit(1); 32 | }) 33 | -------------------------------------------------------------------------------- /bin/hooks/prepare-commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/hooks/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , scripts = require('../../') 5 | , name = path.basename(__filename) 6 | , project = scripts(); 7 | 8 | 9 | project.run(name, process.argv.slice(2), function(err) { 10 | if (err) { 11 | // If the hook actually succeeded, but you want git to think it failed, return 42 12 | if (err.code !== 42) { 13 | console.error('Failed to exec ' + name + ' hook script') 14 | } 15 | process.exit(err.code); 16 | } 17 | }); 18 | 19 | -------------------------------------------------------------------------------- /bin/install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path'); 4 | 5 | // check if git-scripts is in the `node_modules` directory. 6 | 7 | if (path.basename(path.normalize(__dirname + '/../..')) != 'node_modules') { 8 | process.exit(); 9 | } 10 | 11 | // execute install on the project root. 12 | 13 | var bin = path.join(__dirname, 'git-scripts') 14 | , root = path.normalize(__dirname + '/../../..'); 15 | 16 | process.argv.push('install') 17 | process.chdir(root); 18 | require(bin); 19 | 20 | -------------------------------------------------------------------------------- /bin/uninstall: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | var path = require('path') 4 | , spawn = require('child_process').spawn; 5 | 6 | 7 | // check if git-scripts is in the `node_modules` directory. 8 | 9 | if (path.basename(path.normalize(__dirname + '/../..')) != 'node_modules') { 10 | process.exit(); 11 | } 12 | 13 | // execute uninstall on the project root. 14 | 15 | var bin = __dirname + '/git-scripts' 16 | , args = ['uninstall'] 17 | , root = __dirname + '/../../..' 18 | , proc = spawn(bin, args, {cwd: root, stdio: 'inherit', customFds: [0, 1, 2]}); 19 | 20 | proc.on('close', function(code) { 21 | process.exit(code); 22 | }); 23 | 24 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = require('./lib'); 3 | -------------------------------------------------------------------------------- /lib/git-scripts.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs') 3 | , os = require('os') 4 | , path = require('path') 5 | , spawn = require('child_process').spawn; 6 | 7 | 8 | module.exports = GitScripts; 9 | 10 | 11 | function GitScripts(pathStr) { 12 | this.path = pathStr = pathStr || '.'; 13 | this.packagePath = pathStr + '/package.json'; 14 | this.gitPath = this.getGitPath(pathStr); 15 | this.hooksPath = this.gitPath + '/hooks'; 16 | this.oldHooksPath = this.gitPath + '/hooks.old'; 17 | } 18 | 19 | /** 20 | * Install git-scripts. 21 | * 22 | * @param {Function} callback 23 | * @api public 24 | */ 25 | 26 | GitScripts.prototype.install = function(callback) { 27 | callback = callback || function() {}; 28 | 29 | var self = this; 30 | 31 | this.isGitRepo(function(result) { 32 | if (!result) { 33 | return callback(new Error('Not a git repository.')); 34 | } 35 | 36 | self.installed(function(installed) { 37 | if (installed) { 38 | return callback(new Error('Already installed.')); 39 | } 40 | 41 | fs.exists(self.hooksPath, function(exists) { 42 | if (exists) { 43 | install(); 44 | } else { 45 | fs.mkdir(self.hooksPath, function(err) { 46 | if (err) return callback(err); 47 | install(); 48 | }); 49 | } 50 | 51 | function install() { 52 | fs.rename(self.hooksPath, self.oldHooksPath, function(err) { 53 | if (err) return callback(err); 54 | 55 | var hooksSrcPath = __dirname + '/../bin/hooks'; 56 | fs.symlink(path.relative(self.gitPath, hooksSrcPath), self.hooksPath, os.platform() == 'win32' ? 'junction' : 'file', callback); 57 | }); 58 | } 59 | }); 60 | }); 61 | }); 62 | }; 63 | 64 | /** 65 | * Uninstall git-scripts. 66 | * 67 | * @param {Function} callback 68 | * @api public 69 | */ 70 | 71 | GitScripts.prototype.uninstall = function(callback) { 72 | callback = callback || function() {}; 73 | 74 | var self = this; 75 | 76 | this.isGitRepo(function(result) { 77 | if (!result) { 78 | return callback(new Error('Not a git repository.')); 79 | } 80 | 81 | self.installed(function(result) { 82 | if (!result) { 83 | return callback(new Error('Not installed.')); 84 | } 85 | 86 | fs.unlink(self.hooksPath, function(err) { 87 | if (err) return callback(err); 88 | 89 | fs.rename(self.oldHooksPath, self.hooksPath, callback); 90 | }); 91 | }); 92 | }); 93 | }; 94 | 95 | /** 96 | * Run a git-hooks script. 97 | * 98 | * @param {String} hook name 99 | * @param {String} args 100 | * @param {Function} callback 101 | * @api public 102 | */ 103 | 104 | GitScripts.prototype.run = function(name) { 105 | var args = Array.prototype.slice.call(arguments) 106 | , callback = function() {}; 107 | 108 | if ('function' == typeof args[args.length - 1]) { 109 | callback = args.pop(); 110 | } 111 | 112 | this.readCommand(name, function(err, cmd) { 113 | if (err) return callback(err); 114 | if (!cmd) return callback(); 115 | 116 | var conf = {stdio: 'inherit', customFds: [0, 1, 2]} 117 | , proc = spawn('sh', ['-c', cmd + ' $*'].concat(...args), conf); 118 | 119 | proc.on('close', function(code) { 120 | var err; 121 | if (code) { 122 | err = new Error('Failed to exec ' + name + ' hook script'); 123 | err.code = code; 124 | } 125 | 126 | callback(err); 127 | }); 128 | }); 129 | }; 130 | 131 | /** 132 | * Read the script command. 133 | * 134 | * @api private 135 | */ 136 | 137 | GitScripts.prototype.readCommand = function(name, callback) { 138 | fs.readFile(this.packagePath, function(err, data) { 139 | if (err) return callback(err); 140 | 141 | var pkg = JSON.parse(data) 142 | , git = pkg.git || {} 143 | , scripts = git.scripts || {}; 144 | 145 | callback(null, scripts[name]); 146 | }); 147 | }; 148 | 149 | /** 150 | * @api private 151 | */ 152 | 153 | GitScripts.prototype.isGitRepo = function(callback) { 154 | fs.exists(this.gitPath, callback); 155 | }; 156 | 157 | /** 158 | * @api private 159 | */ 160 | 161 | GitScripts.prototype.getGitPath = function(pathStr) { 162 | var gitPath = pathStr + '/.git'; 163 | if (fs.existsSync(gitPath)) { 164 | var pathStats = fs.lstatSync(gitPath); 165 | // file contains a pointer to the git path 166 | if (pathStats.isFile()) { 167 | var gitPathTxt = fs.readFileSync(gitPath, 'utf8'); 168 | gitPath = path.join(pathStr, gitPathTxt.replace('gitdir: ', '')).replace('\n', ''); 169 | } 170 | } 171 | return gitPath; 172 | }; 173 | 174 | /** 175 | * @api private 176 | */ 177 | 178 | GitScripts.prototype.installed = function(callback) { 179 | fs.exists(this.oldHooksPath, callback); 180 | }; 181 | 182 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | 2 | var GitScripts = require('./git-scripts'); 3 | 4 | 5 | module.exports = function(path) { 6 | return new GitScripts(path); 7 | }; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-scripts", 3 | "version": "0.6.4", 4 | "description": "Git hooks integration for Node.js projects", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "make test", 8 | "build": "make build", 9 | "postinstall": "node bin/install", 10 | "postupdate": "node bin/install", 11 | "preuninstall": "node bin/uninstall" 12 | }, 13 | "bin": { 14 | "git-scripts": "bin/git-scripts" 15 | }, 16 | "keywords": [ 17 | "git", 18 | "hooks", 19 | "npm", 20 | "scripts" 21 | ], 22 | "author": "Naoyuki Kanezawa ", 23 | "contributors": [ 24 | "Huan LI (李卓桓) (https://github.com/huan)" 25 | ], 26 | "license": "MIT", 27 | "dependencies": { 28 | "commander": "2.6.0" 29 | }, 30 | "devDependencies": { 31 | "chai": "~1.10.0", 32 | "git-scripts": "latest", 33 | "mktmpdir": "~0.1.1", 34 | "mocha": "~2.1.0", 35 | "rimraf": "~2.2.8", 36 | "shelljs": "^0.8.3" 37 | }, 38 | "repository": { 39 | "type": "git", 40 | "url": "git://github.com/nkzawa/git-scripts.git" 41 | }, 42 | "publishConfig": { 43 | "access": "public", 44 | "tag": "next" 45 | }, 46 | "git": { 47 | "scripts": { 48 | "pre-push": "node scripts/pre-push.js" 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /scripts/package-publish-config-tag.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | VERSION=$(npx pkg-jq -r .version) 5 | 6 | if npx --package @chatie/semver semver-is-prod $VERSION; then 7 | npx pkg-jq -i '.publishConfig.tag="latest"' 8 | echo "production release: publicConfig.tag set to latest." 9 | else 10 | npx pkg-jq -i '.publishConfig.tag="next"' 11 | echo 'development release: publicConfig.tag set to next.' 12 | fi 13 | 14 | -------------------------------------------------------------------------------- /scripts/pre-push.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | /** 3 | * 4 | * An example hook script to verify what is about to be committed. 5 | * Called by "git commit" with no arguments. The hook should 6 | * exit with non-zero status after issuing an appropriate message if 7 | * it wants to stop the commit. 8 | * 9 | * To enable this hook, rename this file to "pre-commit". 10 | * 11 | */ 12 | const shell = require('shelljs') 13 | 14 | const NO_HOOK_VAR = 'NO_HOOK' 15 | const INNER_PRE_HOOK = 'CHATIE_INNER_PRE_HOOK' 16 | 17 | if (process.env[NO_HOOK_VAR]) { 18 | // user set NO_HOOK=1 to prevent this hook works 19 | process.exit(0) 20 | } 21 | 22 | if (process.env[INNER_PRE_HOOK]) { 23 | // http://stackoverflow.com/a/21334985/1123955 24 | process.exit(0) 25 | } 26 | 27 | const argv = process.argv.slice(2) 28 | const remoteName = argv[0] || '' 29 | const remoteUrl = argv[1] || '' 30 | const refs = [] 31 | 32 | for (let i = 2; i + 4 <= argv.length;) { 33 | const ref = { 34 | localBranch : argv[i++] || '', 35 | localCommit : argv[i++] || '', 36 | remoteBranch : argv[i++] || '', 37 | remoteCommit : argv[i++] || '', 38 | } 39 | if (ref.localCommit.match(/^0+$/)) { 40 | ref.localBranch = '' 41 | } 42 | refs.push(ref) 43 | } 44 | 45 | if (refs[0] && refs[0].localCommit.match(/^0+$/)) { 46 | // delete remote branch 47 | process.exit(0) 48 | } 49 | 50 | console.info('[Step-1]', 'Checking last commit...', '\n') 51 | const packageVersion = require('../package.json').version 52 | const lastCommitMsg = checkReturn(shell.exec('git log --pretty=format:"%s" HEAD^0 -1', { silent : true })).stdout 53 | 54 | if (packageVersion === lastCommitMsg) { 55 | console.info('[Step-1]', 'No need to bump the package version.', '\n') 56 | process.exit(0) 57 | } 58 | 59 | console.info('[Step-2]', 'Bump the package version...', '\n') 60 | checkReturn(shell.rm('-f', 'package-lock.json')) 61 | checkReturn(shell.exec('npm version patch --no-package-lock', { silent : true })) 62 | process.env[INNER_PRE_HOOK] = '1' 63 | 64 | console.info('[Step-3]', 'Remove git tag...', '\n') 65 | const version = checkReturn(shell.exec('git log --pretty=format:"%s" HEAD^0 -1', { silent : true })).stdout 66 | checkReturn(shell.exec(`git tag -d v${version}`, { silent : true })) 67 | 68 | console.info('[Step-4]', 'Push...', '\n') 69 | const refMaps = refs.map(ref => ref.remoteBranch ? ref.localBranch + ':' + ref.remoteBranch : '') 70 | const cmd = ['git push', remoteName, ...refMaps].join(' ') 71 | checkReturn(shell.exec(cmd, { silent : true })) 72 | 73 | console.info(String.raw` 74 | ____ _ _ ____ _ 75 | / ___(_) |_ | _ \ _ _ ___| |__ 76 | | | _| | __| | |_) | | | / __| '_ \ 77 | | |_| | | |_ | __/| |_| \__ \ | | | 78 | \____|_|\__| |_| \__,_|___/_| |_| 79 | 80 | ____ _ _ 81 | / ___| _ _ ___ ___ ___ ___ __| | | 82 | \___ \| | | |/ __/ __/ _ \/ _ \/ _^ | | 83 | ___) | |_| | (_| (_| __/ __/ (_| |_| 84 | |____/ \__,_|\___\___\___|\___|\__,_(_) 85 | `) 86 | 87 | console.info(` 88 | 89 | ### Npm version bumped and pushed by inner push inside hook pre-push ### 90 | ------- vvvvvvv outer push will be canceled, never mind vvvvvvv ------- 91 | 92 | `) 93 | 94 | process.exit(42) 95 | 96 | function checkReturn(ret) { 97 | if (ret.code !== 0) { 98 | const line = '------------------------------------------' 99 | console.error(`Error:\n${line}\n\n${ret.stderr}\n\n${line}\n`) 100 | process.exit(1) 101 | } 102 | return ret 103 | } -------------------------------------------------------------------------------- /test/bin.git-scripts.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect 2 | , fs = require('fs') 3 | , path = require('path') 4 | , exec = require('child_process').exec 5 | , rimraf = require('rimraf') 6 | , mktmpdir = require('mktmpdir'); 7 | 8 | 9 | describe('bin/git-scripts', function() { 10 | before(function() { 11 | this.PATH = process.env.PATH; 12 | process.env.PATH = __dirname + '/../bin:' + process.env.PATH 13 | }); 14 | 15 | after(function() { 16 | process.env.PATH = this.PATH; 17 | }); 18 | 19 | beforeEach(function(done) { 20 | var self = this; 21 | mktmpdir(function(err, path) { 22 | self.path = path; 23 | done(err); 24 | }); 25 | }); 26 | 27 | afterEach(function(done) { 28 | rimraf(this.path, done); 29 | }); 30 | 31 | describe('no command', function() { 32 | it('shoud print help information', function(done) { 33 | exec('git-scripts', {cwd: this.path}, function(err, stdout, stderr) { 34 | expect(stdout).to.contain('Usage: git-scripts [options]'); 35 | expect(stderr).to.be.empty; 36 | done(err); 37 | }); 38 | }); 39 | }); 40 | 41 | describe('when the path is not a git repo', function() { 42 | describe('install', function() { 43 | it('shoud print a message to stderr', function(done) { 44 | exec('git-scripts install', {cwd: this.path}, function(err, stdout, stderr) { 45 | expect(err).to.not.exist; 46 | expect(stdout).to.be.empty; 47 | expect(stderr).to.not.be.empty; 48 | done(); 49 | }); 50 | }); 51 | }); 52 | 53 | describe('install ', function() { 54 | it('shoud print a message to stderr', function(done) { 55 | exec('git-scripts install ' + this.path, function(err, stdout, stderr) { 56 | expect(err).to.not.exist; 57 | expect(stdout).to.be.empty; 58 | expect(stderr).to.not.be.empty; 59 | done(); 60 | }); 61 | }); 62 | }); 63 | 64 | describe('uninstall', function() { 65 | it('shoud print a message to stderr', function(done) { 66 | exec('git-scripts uninstall', {cwd: this.path}, function(err, stdout, stderr) { 67 | expect(err).to.not.exist; 68 | expect(stdout).to.be.empty; 69 | expect(stderr).to.not.be.empty; 70 | done(); 71 | }); 72 | }); 73 | }); 74 | 75 | describe('uninstall ', function() { 76 | it('shoud print a message to stderr', function(done) { 77 | exec('git-scripts uninstall ' + this.path, function(err, stdout, stderr) { 78 | expect(err).to.not.exist; 79 | expect(stdout).to.be.empty; 80 | expect(stderr).to.not.be.empty; 81 | done(); 82 | }); 83 | }); 84 | }); 85 | }); 86 | 87 | describe('when the path is a git repo', function() { 88 | beforeEach(function(done) { 89 | exec('git init', {cwd: this.path}, done); 90 | }); 91 | 92 | describe('install', function() { 93 | it('should move the old hooks directory and create the new symlink', function(done) { 94 | var self = this; 95 | exec('git-scripts install', {cwd: this.path}, function(err, stdout, stderr) { 96 | var gitPath = self.path + '/.git' 97 | , binHooksPath = path.resolve(gitPath, fs.readlinkSync(gitPath + '/hooks')); 98 | 99 | expect(binHooksPath).to.equal(path.normalize(__dirname + '/../bin/hooks')); 100 | expect(fs.existsSync(self.path + '/.git/hooks.old')).to.be.true; 101 | done(err); 102 | }); 103 | }); 104 | }); 105 | 106 | describe('install ', function() { 107 | it('should move the old hooks directory and create the new symlink', function(done) { 108 | var self = this; 109 | exec('git-scripts install ' + this.path, function(err, stdout, stderr) { 110 | var gitPath = self.path + '/.git' 111 | , binHooksPath = path.resolve(gitPath, fs.readlinkSync(gitPath + '/hooks')); 112 | 113 | expect(binHooksPath).to.equal(path.normalize(__dirname + '/../bin/hooks')); 114 | expect(fs.existsSync(self.path + '/.git/hooks.old')).to.be.true; 115 | done(err); 116 | }); 117 | }); 118 | }); 119 | 120 | describe('uninstall', function() { 121 | it('shoud print a message to stderr', function(done) { 122 | exec('git-scripts uninstall', {cwd: this.path}, function(err, stdout, stderr) { 123 | expect(err).to.not.exist; 124 | expect(stdout).to.be.empty; 125 | expect(stderr).to.not.be.empty; 126 | done(); 127 | }); 128 | }); 129 | }); 130 | 131 | describe('uninstall ', function() { 132 | it('shoud print a message to stderr', function(done) { 133 | exec('git-scripts uninstall ' + this.path, function(err, stdout, stderr) { 134 | expect(err).to.not.exist; 135 | expect(stdout).to.be.empty; 136 | expect(stderr).to.not.be.empty; 137 | done(); 138 | }); 139 | }); 140 | }); 141 | }); 142 | 143 | describe('when installed', function() { 144 | beforeEach(function(done) { 145 | var self = this; 146 | exec('git init', {cwd: this.path}, function(err) { 147 | if (err) return done(err); 148 | exec('git-scripts install', {cwd: self.path}, done); 149 | }); 150 | }); 151 | 152 | describe('install', function() { 153 | it('shoud exit gracefully ', function(done) { 154 | exec('git-scripts install', {cwd: this.path}, function(err, stdout, stderr) { 155 | expect(stdout).to.be.empty; 156 | expect(stderr).to.be.empty; 157 | done(err); 158 | }); 159 | }); 160 | }); 161 | 162 | describe('install ', function() { 163 | it('shoud exit gracefully ', function(done) { 164 | exec('git-scripts install ' + this.path, function(err, stdout, stderr) { 165 | expect(stdout).to.be.empty; 166 | expect(stderr).to.be.empty; 167 | done(err); 168 | }); 169 | }); 170 | }); 171 | 172 | describe('uninstall', function() { 173 | it('should restore the hooks directory', function(done) { 174 | var self = this; 175 | exec('git-scripts uninstall', {cwd: this.path}, function(err, stdout, stderr) { 176 | var stat = fs.lstatSync(self.path + '/.git/hooks'); 177 | expect(stat.isSymbolicLink()).to.be.false; 178 | expect(fs.existsSync(self.path + '/.git/hooks.old')).to.be.false; 179 | done(err); 180 | }); 181 | }); 182 | }); 183 | 184 | describe('uninstall ', function() { 185 | it('should restore the hooks directory', function(done) { 186 | var self = this; 187 | exec('git-scripts uninstall ' + this.path, function(err, stdout, stderr) { 188 | var stat = fs.lstatSync(self.path + '/.git/hooks'); 189 | expect(stat.isSymbolicLink()).to.be.false; 190 | expect(fs.existsSync(self.path + '/.git/hooks.old')).to.be.false; 191 | done(err); 192 | }); 193 | }); 194 | }); 195 | }); 196 | }); 197 | -------------------------------------------------------------------------------- /test/bin.hooks.hook.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect 2 | , fs = require('fs') 3 | , path = require('path') 4 | , exec = require('child_process').exec 5 | , rimraf = require('rimraf') 6 | , mktmpdir = require('mktmpdir'); 7 | 8 | 9 | describe('bin/hooks/hook', function() { 10 | before(function() { 11 | this.PATH = process.env.PATH; 12 | process.env.PATH = __dirname + '/../bin:' + process.env.PATH 13 | }); 14 | 15 | after(function() { 16 | process.env.PATH = this.PATH; 17 | }); 18 | 19 | beforeEach(function(done) { 20 | var self = this; 21 | mktmpdir(function(err, path) { 22 | self.path = path; 23 | exec('git init', {cwd: path}, function(err) { 24 | if (err) return done(err); 25 | exec('git config user.email "foo@example.com"', {cwd: path}, function(err) { 26 | if (err) return done(err); 27 | exec('git config user.name "Foo"', {cwd: path}, function(err) { 28 | if (err) return done(err); 29 | exec('git-scripts install', {cwd: path}, done); 30 | }); 31 | }); 32 | }); 33 | }); 34 | }); 35 | 36 | afterEach(function(done) { 37 | rimraf(this.path, done); 38 | }); 39 | 40 | it('shoud be executed', function(done) { 41 | var pkg = {git: {scripts: {'pre-commit': 'echo "[Executed]" && exit 1'}}}; 42 | var self = this; 43 | 44 | fs.writeFileSync(this.path + '/package.json', JSON.stringify(pkg)); 45 | exec('git add package.json', {cwd: this.path}, function(err) { 46 | if (err) return done(err); 47 | 48 | exec('git commit -m "test commit"', {cwd: self.path}, function(err, stdout, stderr) { 49 | expect(err).to.be.an.instanceof(Error); 50 | expect(stderr).to.contain('[Executed]'); 51 | expect(stderr).to.contain('pre-commit'); 52 | done(); 53 | }); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/bin.install.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect 2 | , util = require('util') 3 | , fs = require('fs') 4 | , path = require('path') 5 | , exec = require('child_process').exec 6 | , rimraf = require('rimraf') 7 | , mktmpdir = require('mktmpdir'); 8 | 9 | 10 | describe('bin/install', function() { 11 | this.timeout(5000); 12 | 13 | beforeEach(function(done) { 14 | var self = this; 15 | mktmpdir(function(err, path) { 16 | if (err) return done(err); 17 | 18 | self.path = path; 19 | exec('git init', {cwd: path}, function(err) { 20 | if (err) return done(err); 21 | 22 | fs.mkdir(path + '/node_modules', function(err) { 23 | if (err) return done(err); 24 | var cmd = util.format('cp -a %s %s', 25 | __dirname + '/..', path + '/node_modules/git-scripts'); 26 | exec(cmd, done); 27 | }); 28 | }); 29 | }); 30 | }); 31 | 32 | afterEach(function(done) { 33 | rimraf(this.path, done); 34 | }); 35 | 36 | it('should replace `.git/hooks`', function(done) { 37 | var self = this; 38 | exec(this.path + '/node_modules/git-scripts/bin/install', function(err, stdout, stderr) { 39 | var gitPath = self.path + '/.git' 40 | , binHooksPath = path.resolve(gitPath, fs.readlinkSync(gitPath + '/hooks')); 41 | 42 | expect(binHooksPath).to.equal(path.normalize(self.path + '/node_modules/git-scripts/bin/hooks')); 43 | expect(fs.existsSync(self.path + '/.git/hooks.old')).to.be.true; 44 | done(err); 45 | }); 46 | }); 47 | }); 48 | -------------------------------------------------------------------------------- /test/bin.uninstall.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect 2 | , util = require('util') 3 | , fs = require('fs') 4 | , path = require('path') 5 | , exec = require('child_process').exec 6 | , rimraf = require('rimraf') 7 | , mktmpdir = require('mktmpdir'); 8 | 9 | 10 | describe('bin/uninstall', function() { 11 | this.timeout(5000); 12 | 13 | beforeEach(function(done) { 14 | var self = this; 15 | mktmpdir(function(err, path) { 16 | if (err) return done(err); 17 | 18 | self.path = path; 19 | exec('git init', {cwd: path}, function(err) { 20 | if (err) return done(err); 21 | 22 | fs.mkdir(path + '/node_modules', function(err) { 23 | if (err) return done(err); 24 | 25 | var cmd = util.format('cp -a %s %s', 26 | __dirname + '/..', path + '/node_modules/git-scripts'); 27 | exec(cmd, function(err) { 28 | if (err) return done(err); 29 | 30 | exec(path + '/node_modules/git-scripts/bin/install', done); 31 | }); 32 | }); 33 | }); 34 | }); 35 | }); 36 | 37 | afterEach(function(done) { 38 | rimraf(this.path, done); 39 | }); 40 | 41 | it('should restore `.git/hooks`', function(done) { 42 | var self = this; 43 | exec(this.path + '/node_modules/git-scripts/bin/uninstall', function(err, stdout, stderr) { 44 | var stat = fs.lstatSync(self.path + '/.git/hooks'); 45 | expect(stat.isSymbolicLink()).to.be.false; 46 | expect(fs.existsSync(self.path + '/.git/hooks.old')).to.be.false; 47 | done(err); 48 | }); 49 | }); 50 | }); 51 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect 2 | , fs = require('fs') 3 | , path = require('path') 4 | , exec = require('child_process').exec 5 | , rimraf = require('rimraf') 6 | , mktmpdir = require('mktmpdir') 7 | , scripts = require('../'); 8 | 9 | 10 | describe('git-scripts', function() { 11 | beforeEach(function(done) { 12 | var self = this; 13 | mktmpdir(function(err, dir) { 14 | self.proj = scripts(dir); 15 | done(err); 16 | }); 17 | }); 18 | 19 | afterEach(function(done) { 20 | rimraf(this.proj.path, done); 21 | }); 22 | 23 | describe('when the path is not a git repo', function() { 24 | describe('isGitRepo()', function() { 25 | it('should be false', function(done) { 26 | this.proj.isGitRepo(function(result) { 27 | expect(result).to.be.false; 28 | done(); 29 | }); 30 | }); 31 | }); 32 | 33 | describe('installed()', function() { 34 | it('should be false', function(done) { 35 | this.proj.installed(function(result) { 36 | expect(result).to.be.false; 37 | done(); 38 | }); 39 | }); 40 | }); 41 | 42 | describe('install()', function() { 43 | it('should be an error', function(done) { 44 | this.proj.install(function(err) { 45 | expect(err).to.be.an.instanceof(Error); 46 | done(); 47 | }); 48 | }); 49 | }); 50 | 51 | describe('uninstall()', function() { 52 | it('should be an error', function(done) { 53 | this.proj.uninstall(function(err) { 54 | expect(err).to.be.an.instanceof(Error); 55 | done(); 56 | }); 57 | }); 58 | }); 59 | }); 60 | 61 | describe('when the path is a git repo', function() { 62 | beforeEach(function(done) { 63 | exec('git init', {cwd: this.proj.path}, done); 64 | }); 65 | 66 | describe('isGitRepo()', function() { 67 | it('should be true', function(done) { 68 | this.proj.isGitRepo(function(result) { 69 | expect(result).to.be.true; 70 | done(); 71 | }); 72 | }); 73 | }); 74 | 75 | describe('installed()', function() { 76 | it('should be false', function(done) { 77 | this.proj.installed(function(result) { 78 | expect(result).to.be.false; 79 | done(); 80 | }); 81 | }); 82 | }); 83 | 84 | describe('install()', function() { 85 | it('should move the old hooks directory and create the new symlink', function(done) { 86 | var self = this; 87 | this.proj.install(function(err) { 88 | var gitPath = self.proj.path + '/.git' 89 | , binHooksPath = path.resolve(gitPath, fs.readlinkSync(gitPath + '/hooks')); 90 | 91 | expect(binHooksPath).to.equal(path.normalize(__dirname + '/../bin/hooks')); 92 | expect(fs.existsSync(self.proj.path + '/.git/hooks.old')).to.be.true; 93 | done(err); 94 | }); 95 | }); 96 | 97 | it('should work if no hooks directory exist', function(done) { 98 | var hooksPath = this.proj.path + '/.git/hooks'; 99 | var self = this; 100 | 101 | rimraf(hooksPath, function(err) { 102 | if (err) throw err; 103 | self.proj.install(done); 104 | }); 105 | }); 106 | }); 107 | 108 | describe('uninstall()', function() { 109 | it('should be an error', function(done) { 110 | this.proj.uninstall(function(err) { 111 | expect(err).to.be.an.instanceof(Error); 112 | done(); 113 | }); 114 | }); 115 | }); 116 | }); 117 | 118 | describe('when the gitdir is specified in a text file', function() { 119 | beforeEach(function(done) { 120 | var self = this; 121 | mktmpdir(function(err, dir) { 122 | function setupProj() { 123 | exec('git init', {cwd: dir}, moveGitDir); 124 | } 125 | function moveGitDir() { 126 | exec('mv .git .git2', {cwd: dir}, createGitFile); 127 | } 128 | function createGitFile() { 129 | exec('echo "gitdir: ./.git2" > .git', {cwd: dir}, updateProj); 130 | } 131 | function updateProj() { 132 | self.proj = scripts(dir); 133 | done(err); 134 | } 135 | 136 | setupProj(); 137 | }); 138 | }); 139 | 140 | describe('isGitRepo()', function() { 141 | it('should be true', function(done) { 142 | this.proj.isGitRepo(function(result) { 143 | expect(result).to.be.true; 144 | done(); 145 | }); 146 | }); 147 | }); 148 | 149 | describe('installed()', function() { 150 | it('should be false', function(done) { 151 | this.proj.installed(function(result) { 152 | expect(result).to.be.false; 153 | done(); 154 | }); 155 | }); 156 | }); 157 | 158 | describe('install()', function() { 159 | it('should move the old hooks directory and create the new symlink', function(done) { 160 | var self = this; 161 | this.proj.install(function(err) { 162 | var gitPath = self.proj.gitPath 163 | , binHooksPath = path.resolve(gitPath, fs.readlinkSync(gitPath + '/hooks')); 164 | 165 | expect(binHooksPath).to.equal(path.normalize(__dirname + '/../bin/hooks')); 166 | expect(fs.existsSync(gitPath + '/hooks.old')).to.be.true; 167 | done(err); 168 | }); 169 | }); 170 | 171 | it('should work if no hooks directory exist', function(done) { 172 | var hooksPath = this.proj.path + '/hooks'; 173 | var self = this; 174 | 175 | rimraf(hooksPath, function(err) { 176 | if (err) throw err; 177 | self.proj.install(done); 178 | }); 179 | }); 180 | }); 181 | 182 | describe('uninstall()', function() { 183 | it('should be an error', function(done) { 184 | this.proj.uninstall(function(err) { 185 | expect(err).to.be.an.instanceof(Error); 186 | done(); 187 | }); 188 | }); 189 | }); 190 | }); 191 | 192 | describe('when installed', function() { 193 | beforeEach(function(done) { 194 | var self = this; 195 | exec('git init', {cwd: this.proj.path}, function(err) { 196 | if (err) return done(err); 197 | self.proj.install(done); 198 | }); 199 | }); 200 | 201 | describe('installed()', function() { 202 | it('should be true', function(done) { 203 | this.proj.installed(function(result) { 204 | expect(result).to.be.true; 205 | done(); 206 | }); 207 | }); 208 | }); 209 | 210 | describe('install()', function() { 211 | it('should be an error', function(done) { 212 | this.proj.install(function(err) { 213 | expect(err).to.be.an.instanceof(Error); 214 | done(); 215 | }); 216 | }); 217 | }); 218 | 219 | describe('uninstall()', function() { 220 | it('should delete the symlink and move old hooks directory', function(done) { 221 | var self = this; 222 | this.proj.uninstall(function(err) { 223 | expect(fs.existsSync(self.proj.path + '/.git/hooks')).to.be.true; 224 | expect(fs.existsSync(self.proj.path + '/.git/hooks.old')).to.be.false; 225 | done(err); 226 | }); 227 | }); 228 | }); 229 | }); 230 | 231 | describe('when the path has no package.json', function() { 232 | describe('readCommand()', function() { 233 | it('should be an error', function(done) { 234 | this.proj.readCommand('foo', function(err, cmd) { 235 | expect(err).to.be.an.instanceof(Error); 236 | expect(cmd).to.not.exist; 237 | done(); 238 | }); 239 | }); 240 | }); 241 | 242 | describe('run()', function() { 243 | it('should be an error', function(done) { 244 | this.proj.run('foo', function(err) { 245 | expect(err).to.be.an.instanceof(Error); 246 | done(); 247 | }); 248 | }); 249 | }); 250 | }); 251 | 252 | describe('when the path has package.json', function() { 253 | beforeEach(function(done) { 254 | var pkg = {git: {scripts: { 255 | foo: 'echo "hello, $1"', 256 | bar: 'exit 1' 257 | }}}; 258 | fs.writeFile(this.proj.path + '/package.json', JSON.stringify(pkg), done); 259 | }); 260 | 261 | describe('readCommand()', function() { 262 | it('should parse command', function(done) { 263 | this.proj.readCommand('foo', function(err, cmd) { 264 | expect(err).to.not.exist; 265 | expect(cmd).to.equal('echo "hello, $1"'); 266 | done(); 267 | }); 268 | }); 269 | 270 | it('should not be error when the command does not exist', function(done) { 271 | this.proj.readCommand('any', function(err, cmd) { 272 | expect(err).to.not.exist; 273 | expect(cmd).to.not.exist; 274 | done(); 275 | }); 276 | }); 277 | }); 278 | 279 | describe('run()', function() { 280 | it('should be able to supply arguments', function(done) { 281 | this.proj.run('foo', ['world'], done); 282 | }); 283 | 284 | it('should be an error when the command failed', function(done) { 285 | this.proj.run('bar', function(err) { 286 | expect(err).to.be.an.instanceof(Error); 287 | expect(err).to.have.property('code', 1); 288 | done(); 289 | }); 290 | }); 291 | 292 | it('should not be an error when the command does not exist', function(done) { 293 | this.proj.run('any', function(err) { 294 | expect(err).to.not.exist; 295 | done(); 296 | }); 297 | }); 298 | }); 299 | }); 300 | }); 301 | --------------------------------------------------------------------------------