├── bin └── install.js ├── .gitignore ├── LICENSE.md ├── package.json ├── install-git-hooks.js └── README.md /bin/install.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | var igh = require('../install-git-hooks'); 5 | return igh(process.argv); 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | jsconfig.json 3 | .vscode 4 | 5 | # OSX 6 | # 7 | .DS_Store 8 | 9 | # Xcode 10 | # 11 | build/ 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata 21 | *.xccheckout 22 | *.moved-aside 23 | DerivedData 24 | *.hmap 25 | *.ipa 26 | 27 | # Android/IJ 28 | # 29 | .idea 30 | .gradle 31 | local.properties 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | package-lock.json 38 | yarn.lock 39 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ISC License (ISC) 2 | Copyright 2020 Peace Chen 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-git-hooks", 3 | "version": "1.0.7", 4 | "description": "Automate git hook deployment", 5 | "main": "install-git-hook.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/peacechen/node-git-hooks.git" 12 | }, 13 | "bin": { 14 | "node-git-hooks": "bin/install.js" 15 | }, 16 | "engines": { 17 | "node": ">=4.0.0" 18 | }, 19 | "keywords": [ 20 | "node", 21 | "git", 22 | "hook", 23 | "hooks", 24 | "commit", 25 | "pre-commit", 26 | "post-commit", 27 | "install" 28 | ], 29 | "author": "Peace Chen", 30 | "license": "ISC", 31 | "bugs": { 32 | "url": "https://github.com/peacechen/node-git-hooks/issues" 33 | }, 34 | "homepage": "https://github.com/peacechen/node-git-hooks#readme" 35 | } 36 | -------------------------------------------------------------------------------- /install-git-hooks.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | function installGitHooks(args) { 5 | const libPath = path.dirname(require.main.filename); 6 | const regexPath = /^(.*?)node_modules/.exec(libPath); 7 | const appRoot = regexPath ? regexPath[1] : libPath; 8 | const package = require(`${appRoot}package.json`); 9 | let repoPath = "./"; 10 | let gitHooksPath = "./.githooks"; 11 | 12 | if (package["node-git-hooks"]) { 13 | if (package["node-git-hooks"]["repo-path"]) { 14 | repoPath = package["node-git-hooks"]["repo-path"]; 15 | } 16 | if (package["node-git-hooks"]["githooks-path"]) { 17 | gitHooksPath = package["node-git-hooks"]["githooks-path"]; 18 | } 19 | } 20 | 21 | if (!fs.existsSync(`${repoPath}.git`) || !fs.existsSync(`${repoPath}.git/hooks`)) { 22 | console.log("The installation isn't a Git repo. Skipping hooks installation."); 23 | return 0; 24 | } 25 | 26 | if (!fs.existsSync(gitHooksPath)) { 27 | console.log("No .githooks folder found. Skipping hooks installation."); 28 | return 0; 29 | } 30 | 31 | console.log("Installing Git hooks..."); 32 | try { 33 | copyDir(gitHooksPath, `${repoPath}.git/hooks`); 34 | } catch (error) { 35 | console.error("Error copying Git hooks: ", error); 36 | return 1; 37 | } 38 | return 0; 39 | } 40 | 41 | function copyDir(src, dest) { 42 | const files = fs.readdirSync(src); 43 | for (let file of files) { 44 | const current = fs.lstatSync(path.join(src, file)); 45 | if (current.isDirectory()) { 46 | copyDir(path.join(src, file), path.join(dest, file)); 47 | } else if (current.isSymbolicLink()) { 48 | const symlink = fs.readlinkSync(path.join(src, file)); 49 | fs.symlinkSync(symlink, path.join(dest, file)); 50 | } else { 51 | copy(path.join(src, file), path.join(dest, file)); 52 | } 53 | } 54 | }; 55 | 56 | function copy(src, dest) { 57 | fs.copyFileSync(src, dest); 58 | }; 59 | 60 | module.exports = installGitHooks; 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cross-Platform Installation of Git Hooks 2 | 3 | Git hooks are a powerful tool to help developers maintain consistency in their code. 4 | Unfortunately hooks are not part of the repository and thus require developers to 5 | manually set up and/or install them. There are a number of projects that address 6 | this, but they are not as automated, deployment-friendly, or cross-platform. 7 | 8 | ## Node JS for Cross-Platform Support 9 | 10 | Instead of using shell scripts which are dependent on the OS, this project uses 11 | Node to abstract filesystem operations. This allows it to run on Linux, OSX, 12 | Windows, any OS supported by Node. 13 | 14 | ## Deployment Friendly 15 | 16 | While automated installation of Git hooks is great for developers, it can cause 17 | problems during deployment. If an npm package post-installs 18 | hooks inside node_modules, build systems may throw the error 19 | `Appears to be a git repo or submodule`. This project will only copy hooks 20 | if the `.git` folder exists. 21 | 22 | ## Tutorial 23 | 24 | [This article on dev.to](https://dev.to/peacechen/automated-cross-platform-git-hooks-with-npm-1iof) has a detailed explanation and setup walk-through. 25 | 26 | ## Installation 27 | 28 | > npm install node-git-hooks --save-dev 29 | 30 | ## Usage 31 | 32 | Create a `.githooks` folder and place hooks inside named corresponding to what they should be in `.git/hooks`. For example, the script `pre-commit` 33 | runs before a commit and is often used to perform linting. Remember to set the executable flag for hook files on \*NIX systems: 34 | > chmod +x .githooks/* 35 | 36 | Add the following `prepare` script to package.json: 37 | ``` 38 | "scripts": { 39 | "prepare": "node-git-hooks" 40 | }, 41 | ``` 42 | 43 | Run `npm install` to initialize the hooks. 44 | 45 | ## Advanced Usage 46 | 47 | If `package.json` is not located at the root of the repo (e.g. a multi-project repo), add the key 48 | `node-git-hooks` with the `repo-path` as such: 49 | ``` 50 | { 51 | "node-git-hooks": { 52 | "repo-path": "../" 53 | } 54 | } 55 | ``` 56 | 57 | It is also possible to set the githooks folder path following the same logic: 58 | ``` 59 | { 60 | "node-git-hooks": { 61 | "githooks-path": "../" 62 | } 63 | } 64 | ``` 65 | --------------------------------------------------------------------------------