From a5ce81c2a35acba5cb2eff0fec13e7d09d013543 Mon Sep 17 00:00:00 2001 From: Konstantin Epishev Date: Tue, 26 Oct 2021 11:06:43 +0200 Subject: [PATCH 1/4] feat: possibility to pass custom config path --- cli.js | 3 ++- simple-git-hooks.js | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/cli.js b/cli.js index b116e38..69b6029 100644 --- a/cli.js +++ b/cli.js @@ -5,9 +5,10 @@ * A CLI tool to change the git hooks to commands from config */ const {setHooksFromConfig} = require('./simple-git-hooks') +const [configPath] = process.argv.slice(2) try { - setHooksFromConfig() + setHooksFromConfig(process.cwd(), configPath) console.log('[INFO] Successfully set all git hooks') } catch (e) { console.log('[ERROR], Was not able to set git hooks. Error: ' + e) diff --git a/simple-git-hooks.js b/simple-git-hooks.js index 27ee494..79fc155 100644 --- a/simple-git-hooks.js +++ b/simple-git-hooks.js @@ -1,5 +1,5 @@ const fs = require('fs') -const path = require('path'); +const path = require('path') const VALID_GIT_HOOKS = [ 'applypatch-msg', @@ -131,9 +131,10 @@ function checkSimpleGitHooksInDependencies(projectRootPath) { /** * Parses the config and sets git hooks * @param {string} projectRootPath + * @param {string} [configFileName] */ -function setHooksFromConfig(projectRootPath=process.cwd()) { - const config = _getConfig(projectRootPath) +function setHooksFromConfig(projectRootPath=process.cwd(), configFileName='') { + const config = _getConfig(projectRootPath, configFileName) if (!config) { throw('[ERROR] Config was not found! Please add `.simple-git-hooks.js` or `simple-git-hooks.js` or `.simple-git-hooks.json` or `simple-git-hooks.json` or `simple-git-hooks` entry in package.json.\r\nCheck README for details') @@ -227,11 +228,12 @@ function _getPackageJson(projectPath = process.cwd()) { * First try to get command from .simple-pre-commit.json * If not found -> try to get command from package.json * @param {string} projectRootPath + * @param {string} [configFileName] * @throws TypeError if projectRootPath is not string * @return {{string: string} | undefined} * @private */ -function _getConfig(projectRootPath) { +function _getConfig(projectRootPath, configFileName='') { if (typeof projectRootPath !== 'string') { throw TypeError("Check project root path! Expected a string, but got " + typeof projectRootPath) } @@ -247,6 +249,11 @@ function _getConfig(projectRootPath) { () => _getConfigFromPackageJson(projectRootPath), ] + // if user pass his-own config path prepend custom path before the default ones + if (configFileName) { + sources.unshift(() => _getConfigFromFile(projectRootPath, configFileName)) + } + for (let executeSource of sources) { let config = executeSource() if (config && _validateHooks(config)) { From d8d36925cb29f735a995ba8dc33ad0c9283402a2 Mon Sep 17 00:00:00 2001 From: Konstantin Epishev Date: Tue, 26 Oct 2021 11:13:57 +0200 Subject: [PATCH 2/4] test: add specs for custom configuration file --- _tests/project_with_custom_configuration/git-hooks.js | 4 ++++ _tests/project_with_custom_configuration/package.json | 7 +++++++ simple-git-hooks.test.js | 11 +++++++++++ 3 files changed, 22 insertions(+) create mode 100644 _tests/project_with_custom_configuration/git-hooks.js create mode 100644 _tests/project_with_custom_configuration/package.json diff --git a/_tests/project_with_custom_configuration/git-hooks.js b/_tests/project_with_custom_configuration/git-hooks.js new file mode 100644 index 0000000..01a27f3 --- /dev/null +++ b/_tests/project_with_custom_configuration/git-hooks.js @@ -0,0 +1,4 @@ +module.exports = { + "pre-push": "exit 1", + "pre-commit": "exit 1" +} diff --git a/_tests/project_with_custom_configuration/package.json b/_tests/project_with_custom_configuration/package.json new file mode 100644 index 0000000..a6a7478 --- /dev/null +++ b/_tests/project_with_custom_configuration/package.json @@ -0,0 +1,7 @@ +{ + "name": "simple-pre-commit-test-package", + "version": "1.0.0", + "devDependencies": { + "simple-git-hooks": "1.0.0" + } +} diff --git a/simple-git-hooks.test.js b/simple-git-hooks.test.js index 0dd1eaf..738e8ee 100644 --- a/simple-git-hooks.test.js +++ b/simple-git-hooks.test.js @@ -77,6 +77,7 @@ const projectWithConfigurationInAlternativeSeparateJsPath = path.normalize(path. const projectWithConfigurationInSeparateJsonPath = path.normalize(path.join(testsFolder, 'project_with_configuration_in_separate_json')) const projectWithConfigurationInAlternativeSeparateJsonPath = path.normalize(path.join(testsFolder, 'project_with_configuration_in_alternative_separate_json')) const projectWithUnusedConfigurationInPackageJsonPath = path.normalize(path.join(testsFolder, 'project_with_unused_configuration_in_package_json')) +const projectWithCustomConfigurationFilePath = path.normalize(path.join(testsFolder, 'project_with_custom_configuration')) // Incorrect configurations @@ -260,3 +261,13 @@ test('creates git hooks and removes unused but preserves specific git hooks', () removeGitHooksFolder(projectWithUnusedConfigurationInPackageJsonPath) }) + +test('creates git hooks and removes unused but preserves specific git hooks', () => { + createGitHooksFolder(projectWithCustomConfigurationFilePath) + + spc.setHooksFromConfig(projectWithCustomConfigurationFilePath, 'git-hooks.js') + const installedHooks = getInstalledGitHooks(path.normalize(path.join(projectWithCustomConfigurationFilePath, '.git', 'hooks'))) + expect(JSON.stringify(installedHooks)).toBe(JSON.stringify({'pre-commit':`#!/bin/sh\nexit 1`, 'pre-push':`#!/bin/sh\nexit 1`})) + + removeGitHooksFolder(projectWithCustomConfigurationFilePath) +}) From 87d3f674223980d12b25396403fd66b412e6b8f8 Mon Sep 17 00:00:00 2001 From: Konstantin Epishev Date: Tue, 26 Oct 2021 11:19:44 +0200 Subject: [PATCH 3/4] docs: update readme --- README.md | 99 +++++++++++++++++++++++++++---------------------------- 1 file changed, 48 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index e69028b..498ce6c 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ # simple-git-hooks - - ![](https://img.shields.io/badge/dependencies-zero-green) [![Tests](https://github.com/toplenboren/simple-git-hooks/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/toplenboren/simple-git-hooks/actions/workflows/tests.yml) +![](https://img.shields.io/badge/dependencies-zero-green) [![Tests](https://github.com/toplenboren/simple-git-hooks/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/toplenboren/simple-git-hooks/actions/workflows/tests.yml) A tool that lets you easily manage git hooks -> The package was recently renamed from `simple-pre-commit`. - +> The package was recently renamed from `simple-pre-commit`. > See **Releases** for the `simple-pre-commit` documentation and changelog @@ -14,31 +12,31 @@ A tool that lets you easily manage git hooks - Small configuration (1 object in package.json) - Lightweight: - | Package | Unpacked size | With deps | - | ------------- | ------------- | ------------- | - | husky v4 `4.3.8` | `53.5 kB` | `~1 mB` | - | husky v6 `6.0.0` | `6.86 kB` | `6.86 kB` | - | pre-commit `1.2.2` | `~80 kB` | `~850 kB` | - | **simple-git-hooks** `2.2.0` | `10.1 kB` | `10.1 kB` | + | Package | Unpacked size | With deps | + | ---------------------------- | ------------- | --------- | + | husky v4 `4.3.8` | `53.5 kB` | `~1 mB` | + | husky v6 `6.0.0` | `6.86 kB` | `6.86 kB` | + | pre-commit `1.2.2` | `~80 kB` | `~850 kB` | + | **simple-git-hooks** `2.2.0` | `10.1 kB` | `10.1 kB` | ### Who uses simple-git-hooks? -> The package is recommended by [`lint-staged`](https://github.com/okonet/lint-staged) +> The package is recommended by [`lint-staged`](https://github.com/okonet/lint-staged) -* [Autoprefixer](https://github.com/postcss/autoprefixer) -* [PostCSS](https://github.com/postcss/postcss.org) -* [Browserslist](https://github.com/browserslist/browserslist) -* [Nano ID](https://github.com/ai/nanoid) -* [Size Limit](https://github.com/ai/size-limit) -* [Storeon](https://github.com/storeon/storeon) -* [Directus](https://github.com/directus/directus) -* [Vercel/pkg](https://github.com/vercel/pkg) -* More, see [full list](https://github.com/toplenboren/simple-git-hooks/network/dependents?package_id=UGFja2FnZS0xOTk1ODMzMTA4) +- [Autoprefixer](https://github.com/postcss/autoprefixer) +- [PostCSS](https://github.com/postcss/postcss.org) +- [Browserslist](https://github.com/browserslist/browserslist) +- [Nano ID](https://github.com/ai/nanoid) +- [Size Limit](https://github.com/ai/size-limit) +- [Storeon](https://github.com/storeon/storeon) +- [Directus](https://github.com/directus/directus) +- [Vercel/pkg](https://github.com/vercel/pkg) +- More, see [full list](https://github.com/toplenboren/simple-git-hooks/network/dependents?package_id=UGFja2FnZS0xOTk1ODMzMTA4) ### What is a git hook? A git hook is a command or script that is going to be run every time you perform a git action, like `git commit` or `git push`. - + If the execution of a git hook fails, then the git action aborts. For example, if you want to run `linter` on every commit to ensure code quality in your project, then you can create a `pre-commit` hook that would call `npx lint-staged`. @@ -53,57 +51,56 @@ You can look up about git hooks on the [Pro Git book](https://git-scm.com/book/e However, this package requires you to manually apply the changes to git hooks. If you update them often, this is probably not the best choice. -Also, this package allows you to set only one command per git hook. +Also, this package allows you to set only one command per git hook. If you need multiple verbose commands per git hook, flexible configuration or automatic update of git hooks, please check out the other packages: - -* [Lefthook](https://github.com/Arkweid/lefthook) -* [husky](https://github.com/typicode/husky) -* [pre-commit](https://github.com/pre-commit/pre-commit) +- [Lefthook](https://github.com/Arkweid/lefthook) +- [husky](https://github.com/typicode/husky) +- [pre-commit](https://github.com/pre-commit/pre-commit) ## Usage ### Add simple-git-hooks to the project 1. Install simple-git-hooks as a dev dependency: - + ```sh npm install simple-git-hooks --save-dev ``` 2. Add `simple-git-hooks` to your `package.json`. Fill it with git hooks and the corresponding commands. - For example: + For example: - ```jsonc - { - "simple-git-hooks": { - "pre-commit": "npx lint-staged", - "pre-push": "cd ../../ && npm run format", + ```jsonc + { + "simple-git-hooks": { + "pre-commit": "npx lint-staged", + "pre-push": "cd ../../ && npm run format", - // All unused hooks will be removed automatically by default - // but you can use the `preserveUnused` option like following to prevent this behavior + // All unused hooks will be removed automatically by default + // but you can use the `preserveUnused` option like following to prevent this behavior - // if you'd prefer preserve all unused hooks - "preserveUnused": true, + // if you'd prefer preserve all unused hooks + "preserveUnused": true, - // if you'd prefer preserve specific unused hooks - "preserveUnused": ["commit-msg"] - } - } - ``` + // if you'd prefer preserve specific unused hooks + "preserveUnused": ["commit-msg"] + } + } + ``` + + This configuration is going to run all linters on every `commit` and formatter on `push`. - This configuration is going to run all linters on every `commit` and formatter on `push`. - > There are more ways to configure the package. Check out [Additional configuration options](#additional-configuration-options). - + 3. Run the CLI script to update the git hooks with the commands from the config: ```sh npx simple-git-hooks ``` - + Now all the git hooks are created. ### Update git hooks command @@ -116,7 +113,6 @@ Note for **yarn2** users: Please run `yarn dlx simple-git-hooks` instead of the Note that you should manually run `npx simple-git-hooks` **every time you change a command**. - ### Additional configuration options You can also add a `.simple-git-hooks.cjs`, `.simple-git-hooks.js`, `simple-git-hooks.cjs`, `simple-git-hooks.js`, `.simple-git-hooks.json` or `simple-git-hooks.json` file to the project and write the configuration inside it. @@ -128,8 +124,8 @@ This way `simple-git-hooks` configuration in `package.json` will not take effect ```js module.exports = { "pre-commit": "npx lint-staged", - "pre-push": "cd ../../ && npm run format" -} + "pre-push": "cd ../../ && npm run format", +}; ``` `.simple-git-hooks.json` or `simple-git-hooks.json` should look like the following. @@ -141,6 +137,8 @@ module.exports = { } ``` +If you need to have multiple configuration files or just your-own configuration file, you install hooks manually from it by `npx simple-git-hooks ./my-config.js`. + ### Uninstall simple-git-hooks > Uninstallation will remove all the existing git hooks. @@ -149,14 +147,13 @@ module.exports = { npm uninstall simple-git-hooks ``` - ## Common issues ### When migrating from `husky` git hooks are not running **Why is this happening?** -Husky might change the `core.gitHooks` value to `.husky`, this way, git hooks would search `.husky` directory instead of `.git/hooks/`. +Husky might change the `core.gitHooks` value to `.husky`, this way, git hooks would search `.husky` directory instead of `.git/hooks/`. Read more on git configuration in [Git book](https://git-scm.com/docs/githooks) From 7ae0943eb6b74ab139439018f45fc246144addf6 Mon Sep 17 00:00:00 2001 From: Konstantin Epishev Date: Tue, 26 Oct 2021 11:35:44 +0200 Subject: [PATCH 4/4] refactor: make custom config flow much more flexible --- cli.js | 3 +-- simple-git-hooks.js | 21 ++++++++++++++++++--- simple-git-hooks.test.js | 2 +- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/cli.js b/cli.js index 69b6029..78b9865 100644 --- a/cli.js +++ b/cli.js @@ -5,10 +5,9 @@ * A CLI tool to change the git hooks to commands from config */ const {setHooksFromConfig} = require('./simple-git-hooks') -const [configPath] = process.argv.slice(2) try { - setHooksFromConfig(process.cwd(), configPath) + setHooksFromConfig(process.cwd(), process.argv) console.log('[INFO] Successfully set all git hooks') } catch (e) { console.log('[ERROR], Was not able to set git hooks. Error: ' + e) diff --git a/simple-git-hooks.js b/simple-git-hooks.js index 79fc155..0dbdf8d 100644 --- a/simple-git-hooks.js +++ b/simple-git-hooks.js @@ -131,10 +131,11 @@ function checkSimpleGitHooksInDependencies(projectRootPath) { /** * Parses the config and sets git hooks * @param {string} projectRootPath - * @param {string} [configFileName] + * @param {string[]} [argv] */ -function setHooksFromConfig(projectRootPath=process.cwd(), configFileName='') { - const config = _getConfig(projectRootPath, configFileName) +function setHooksFromConfig(projectRootPath=process.cwd(), argv=process.argv) { + const customConfigPath = _getCustomConfigPath(argv) + const config = _getConfig(projectRootPath, customConfigPath) if (!config) { throw('[ERROR] Config was not found! Please add `.simple-git-hooks.js` or `simple-git-hooks.js` or `.simple-git-hooks.json` or `simple-git-hooks.json` or `simple-git-hooks` entry in package.json.\r\nCheck README for details') @@ -223,6 +224,20 @@ function _getPackageJson(projectPath = process.cwd()) { return { packageJsonContent: JSON.parse(packageJsonDataRaw), packageJsonPath: targetPackageJson } } +/** + * Takes the first argument from current process argv and returns it + * Returns empty string when argument wasn't passed + * @param {string[]} [argv] + * @returns {string} + */ +function _getCustomConfigPath(argv=[]) { + const cmdIdx = argv.findIndex(val => val === 'simple-git-hooks') + + if (cmdIdx === -1) return '' + + return argv[cmdIdx + 1] || '' +} + /** * Gets user-set command either from sources * First try to get command from .simple-pre-commit.json diff --git a/simple-git-hooks.test.js b/simple-git-hooks.test.js index 738e8ee..88f044e 100644 --- a/simple-git-hooks.test.js +++ b/simple-git-hooks.test.js @@ -265,7 +265,7 @@ test('creates git hooks and removes unused but preserves specific git hooks', () test('creates git hooks and removes unused but preserves specific git hooks', () => { createGitHooksFolder(projectWithCustomConfigurationFilePath) - spc.setHooksFromConfig(projectWithCustomConfigurationFilePath, 'git-hooks.js') + spc.setHooksFromConfig(projectWithCustomConfigurationFilePath, ['npx', 'simple-git-hooks', './git-hooks.js']) const installedHooks = getInstalledGitHooks(path.normalize(path.join(projectWithCustomConfigurationFilePath, '.git', 'hooks'))) expect(JSON.stringify(installedHooks)).toBe(JSON.stringify({'pre-commit':`#!/bin/sh\nexit 1`, 'pre-push':`#!/bin/sh\nexit 1`}))