mirror of
https://github.com/hanzoai/husky.git
synced 2026-09-24 16:59:45 +00:00
v9 (#1333)
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
root = true
|
||||
[*]
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
insert_final_newline = false
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": "@typicode"
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
*.sh linguist-language=JavaScript
|
||||
*.ts linguist-language=JavaScript
|
||||
+8
-32
@@ -6,42 +6,11 @@
|
||||
|
||||
Husky improves your commits and more 🐶 _woof!_
|
||||
|
||||
# Install
|
||||
|
||||
```
|
||||
npm install husky -D
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
Edit `package.json > prepare` script and run it once:
|
||||
|
||||
```sh
|
||||
npm pkg set scripts.prepare="husky install"
|
||||
npm run prepare
|
||||
```
|
||||
|
||||
Add a hook:
|
||||
|
||||
```sh
|
||||
npx husky add .husky/pre-commit "npm test"
|
||||
git add .husky/pre-commit
|
||||
```
|
||||
|
||||
Make a commit:
|
||||
|
||||
```sh
|
||||
git commit -m "Keep calm and commit"
|
||||
# `npm test` will run every time you commit
|
||||
```
|
||||
|
||||
_For more use cases (project in sub-directory, custom directory, CI support, ...), see documentation._
|
||||
|
||||
## Documentation
|
||||
|
||||
https://typicode.github.io/husky
|
||||
|
||||
**Important** Upgrading from v4 to v8 requires migrating previous config, please see the docs.
|
||||
**Important** Upgrading from v4 to v9 requires migrating previous config, please see the docs.
|
||||
|
||||
## Articles
|
||||
|
||||
@@ -54,6 +23,13 @@ MIT
|
||||
|
||||
# Sponsors
|
||||
|
||||
<p align="center">
|
||||
<a href="./docs/sponsorkit/sponsors.svg">
|
||||
<img src='./docs/sponsorkit/sponsors.svg'/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
## Companies
|
||||
|
||||
Does your company use husky? Ask your manager or marketing team if your company would be interested in supporting this project.
|
||||
|
||||
@@ -19,11 +19,12 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 20
|
||||
cache: npm
|
||||
- run: npm ci
|
||||
- name: Build
|
||||
run: npm run docs:build
|
||||
working-directory: ./docs
|
||||
run: npx vitepress build
|
||||
- uses: actions/configure-pages@v2
|
||||
- uses: actions/upload-pages-artifact@v1
|
||||
with:
|
||||
|
||||
@@ -8,20 +8,10 @@ permissions:
|
||||
contents: read # to fetch code (actions/checkout)
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 20
|
||||
- run: npm ci --ignore-scripts
|
||||
- run: npm run lint
|
||||
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16, 18, 20]
|
||||
node-version: [18, 20]
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@@ -31,4 +21,4 @@ jobs:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm --version
|
||||
- run: npm ci --ignore-scripts
|
||||
- run: npm test
|
||||
- run: ./test.sh
|
||||
|
||||
@@ -18,9 +18,8 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
node-version: 20
|
||||
- run: ./test.sh
|
||||
|
||||
publish-npm:
|
||||
needs: build
|
||||
@@ -31,9 +30,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 18
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
node-version: 20
|
||||
- run: npm publish --provenance --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
+4
-3
@@ -1,7 +1,8 @@
|
||||
lib
|
||||
node_modules
|
||||
husky-*.tgz
|
||||
tsconfig.tsbuildinfo
|
||||
*.log
|
||||
docs/.vitepress/cache
|
||||
docs/.vitepress/dist
|
||||
docs/.env
|
||||
docs/sponsorkit/.cache.json
|
||||
docs/sponsorkit/sponsors.json
|
||||
docs/sponsorkit/sponsors.png
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no -- commitlint --edit "$1"
|
||||
+1
-3
@@ -1,4 +1,2 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npm test
|
||||
./test.sh
|
||||
@@ -0,0 +1,3 @@
|
||||
.*
|
||||
test*
|
||||
docs
|
||||
@@ -0,0 +1 @@
|
||||
disable=SC1090,SC2164
|
||||
@@ -1 +0,0 @@
|
||||
By contributing, you agree to release your modifications under the MIT license (see the file LICENSE).
|
||||
@@ -1,38 +1 @@
|
||||
# husky
|
||||
|
||||
> Modern native Git hooks made easy
|
||||
|
||||
Husky improves your commits and more 🐶 *woof!*
|
||||
|
||||
# Install
|
||||
|
||||
```
|
||||
npm install husky --save-dev
|
||||
```
|
||||
|
||||
# Usage
|
||||
|
||||
Edit `package.json > prepare` script and run it once:
|
||||
|
||||
```sh
|
||||
npm pkg set scripts.prepare="husky install"
|
||||
npm run prepare
|
||||
```
|
||||
|
||||
Add a hook:
|
||||
|
||||
```sh
|
||||
npx husky add .husky/pre-commit "npm test"
|
||||
git add .husky/pre-commit
|
||||
```
|
||||
|
||||
Make a commit:
|
||||
|
||||
```sh
|
||||
git commit -m "Keep calm and commit"
|
||||
# `npm test` will run
|
||||
```
|
||||
|
||||
# Documentation
|
||||
|
||||
https://typicode.github.io/husky
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env node
|
||||
import f, { writeFileSync as w } from 'fs'
|
||||
import i from './index.js'
|
||||
|
||||
let a = process.argv[2]
|
||||
|
||||
if (a == 'init') {
|
||||
let p = process.env.npm_package_json
|
||||
let d = JSON.parse(f.readFileSync(p))
|
||||
d.scripts.prepare = 'husky'
|
||||
w('package.json', JSON.stringify(d, null, /\t/.test() ? '\t' : 2))
|
||||
process.stdout.write(i())
|
||||
w('.husky/pre-commit', process.env.npm_config_user_agent.split('/')[0] + ' test')
|
||||
process.exit()
|
||||
}
|
||||
|
||||
let d = c => console.error(`${c} command is deprecated`)
|
||||
if (['add', 'set', 'uninstall'].includes(a)) { d(a); process.exit(1) }
|
||||
if (a == 'install') d(a)
|
||||
|
||||
process.stdout.write(i(a == 'install' ? undefined : a))
|
||||
@@ -0,0 +1,25 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: "Husky",
|
||||
description: "Git hooks made easy",
|
||||
base: '/husky/',
|
||||
themeConfig: {
|
||||
// outline: [2, 3],
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/typicode/husky' },
|
||||
],
|
||||
carbonAds: {
|
||||
code: 'CWYDP53L',
|
||||
placement: 'typicodegithubio',
|
||||
},
|
||||
sidebar: [
|
||||
{ text: 'Introduction', link: '/' },
|
||||
{ text: 'Get Started', link: '/get-started' },
|
||||
{ text: 'How To', link: '/how-to' },
|
||||
{ text: 'Troubleshoot', link: '/troubleshoot' },
|
||||
{ text: 'Migrate from v4', link: '/migrate-from-v4' },
|
||||
],
|
||||
}
|
||||
})
|
||||
@@ -1,45 +0,0 @@
|
||||
import { defineConfig } from 'vitepress'
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: '🐶 husky',
|
||||
description: 'Git hooks made easy',
|
||||
base: '/husky/',
|
||||
themeConfig: {
|
||||
outline: [2, 3],
|
||||
socialLinks: [
|
||||
{ icon: 'github', link: 'https://github.com/typicode/husky' },
|
||||
],
|
||||
carbonAds: {
|
||||
code: 'CWYDP53L',
|
||||
placement: 'typicodegithubio',
|
||||
},
|
||||
sidebar: [
|
||||
{ text: 'Introduction', link: '/' },
|
||||
{ text: 'Getting started', link: '/getting-started' },
|
||||
{ text: 'Guide', link: '/guide' },
|
||||
{ text: 'Troubleshooting', link: '/troubleshooting' },
|
||||
{ text: 'Migrating from v4', link: '/migrating-from-v4' },
|
||||
],
|
||||
},
|
||||
locales: {
|
||||
root: {
|
||||
label: 'English',
|
||||
lang: 'en',
|
||||
},
|
||||
zh: {
|
||||
label: '简体中文',
|
||||
lang: 'zh-CN',
|
||||
link: '/zh/',
|
||||
themeConfig: {
|
||||
sidebar: [
|
||||
{ text: '介绍', link: '/zh/' },
|
||||
{ text: '开始使用', link: '/zh/getting-started' },
|
||||
{ text: '指南', link: '/zh/guide' },
|
||||
{ text: '疑难解答', link: '/zh/troubleshooting' },
|
||||
{ text: '从 v4 迁移', link: '/zh/migrating-from-v4' },
|
||||
],
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,63 @@
|
||||
# Get started
|
||||
|
||||
## Install
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [npm]
|
||||
npm install --save-dev husky
|
||||
```
|
||||
|
||||
```shell [pnpm]
|
||||
pnpm add --save-dev husky
|
||||
```
|
||||
|
||||
```shell [yarn]
|
||||
yarn add --dev husky
|
||||
# Add pinst ONLY if your package is not private
|
||||
yarn add --dev pinst
|
||||
```
|
||||
|
||||
```shell [bun]
|
||||
bun add --dev husky
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## `husky init` (recommended)
|
||||
|
||||
The `init` command simplifies setting up husky in a project. It creates a `pre-commit` script in `.husky/` and updates the `prepare` script in `package.json`. Modifications can be made later to suit your workflow.
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [npm]
|
||||
npx husky init
|
||||
```
|
||||
|
||||
```shell [pnpm]
|
||||
pnpm exec husky init
|
||||
```
|
||||
|
||||
```shell [yarn]
|
||||
# Due to specific caveats and differences with other package managers,
|
||||
# refer to the How To section.
|
||||
```
|
||||
|
||||
```shell [bun]
|
||||
bunx husky init
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
|
||||
## Try it
|
||||
|
||||
Congratulations! You've successfully set up your first Git hook with just one command 🎉. Let's test it:
|
||||
|
||||
```shell
|
||||
git commit -m "Keep calm and commit"
|
||||
# test script will run every time you commit
|
||||
```
|
||||
|
||||
_For manual setup and more information, see the [How To](how-to) section._
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
# Getting started
|
||||
|
||||
## Automatic (recommended)
|
||||
|
||||
`husky-init` is a one-time command to quickly initialize a project with husky.
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [npm]
|
||||
npx husky-init && npm install
|
||||
```
|
||||
|
||||
```shell [pnpm]
|
||||
pnpm dlx husky-init && pnpm install
|
||||
```
|
||||
|
||||
```shell [yarn]
|
||||
yarn dlx husky-init --yarn2 && yarn
|
||||
```
|
||||
|
||||
```shell [bun]
|
||||
bunx husky-init && bun install
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
It will:
|
||||
|
||||
1. Add `prepare` script to `package.json`
|
||||
1. Create a sample `pre-commit` hook that you can edit (by default, `npm test` will run when you commit)
|
||||
1. Configure Git hooks path
|
||||
|
||||
To add another hook use `husky add`. For example:
|
||||
|
||||
```shell
|
||||
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
|
||||
```
|
||||
|
||||
::: info
|
||||
For Windows users, if you see the help message when running `npx husky add ...`, try `node node_modules/husky/lib/bin add ...` instead. This isn't an issue with husky code.
|
||||
:::
|
||||
|
||||
## Manual
|
||||
|
||||
### Install
|
||||
|
||||
1. Install `husky`
|
||||
|
||||
```shell
|
||||
npm install husky --save-dev
|
||||
```
|
||||
|
||||
2. Enable Git hooks
|
||||
|
||||
```shell
|
||||
npx husky install
|
||||
```
|
||||
|
||||
3. To automatically have Git hooks enabled after install, edit `package.json`
|
||||
|
||||
```shell
|
||||
npm pkg set scripts.prepare="husky install"
|
||||
```
|
||||
|
||||
You should have:
|
||||
|
||||
::: code-group
|
||||
|
||||
```json [package.json]
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "husky install" // [!code hl]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: info
|
||||
Yarn 2+ doesn't support `prepare` lifecycle script, so husky needs to be installed differently (this doesn't apply to Yarn 1 though). See [Yarn 2+ install](#yarn-2).
|
||||
:::
|
||||
|
||||
## Create a hook
|
||||
|
||||
To add a command to a hook or create a new one, use `husky add <file> [cmd]` (don't forget to run `husky install` before).
|
||||
|
||||
```shell
|
||||
npx husky add .husky/pre-commit "npm test"
|
||||
git add .husky/pre-commit
|
||||
```
|
||||
|
||||
Try to make a commit
|
||||
|
||||
```shell
|
||||
git commit -m "Keep calm and commit"
|
||||
```
|
||||
|
||||
If `npm test` command fails, your commit will be automatically aborted.
|
||||
|
||||
::: warning
|
||||
**Using Yarn to run commands? There's an issue on Windows with Git Bash, see [Yarn on Windows](#yarn-on-windows).**
|
||||
:::
|
||||
|
||||
_For Windows users, if you see the help message when running `npx husky add ...`, try `node node_modules/.bin/husky add ...` instead. This isn't an issue with husky code and is fixed in recent versions of npm 8._
|
||||
|
||||
### Uninstall
|
||||
|
||||
```shell
|
||||
npm uninstall husky && git config --unset core.hooksPath
|
||||
```
|
||||
|
||||
## Yarn 2
|
||||
|
||||
### Install
|
||||
|
||||
1. Install `husky`
|
||||
|
||||
```shell
|
||||
yarn add husky --dev
|
||||
yarn add pinst --dev # ONLY if your package is not private
|
||||
```
|
||||
|
||||
2. Enable Git hooks
|
||||
|
||||
```shell
|
||||
yarn husky install
|
||||
```
|
||||
|
||||
3. To automatically have Git hooks enabled after install, edit `package.json`
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [package.json]
|
||||
{
|
||||
"private": true, // ← your package is private, you only need postinstall
|
||||
"scripts": {
|
||||
"postinstall": "husky install"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: tip
|
||||
if your package is not private and you're publishing it on a registry like [npmjs.com](https://npmjs.com), you need to disable `postinstall` script using [pinst](https://github.com/typicode/pinst)\*\*. Otherwise, `postinstall` will run when someone installs your package and result in an error.
|
||||
:::
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [package.json]
|
||||
{
|
||||
"private": false, // ← your package is public
|
||||
"scripts": {
|
||||
"postinstall": "husky install",
|
||||
"prepack": "pinst --disable",
|
||||
"postpack": "pinst --enable"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Uninstall
|
||||
|
||||
Remove `"postinstall": "husky install"` from `package.json` and run:
|
||||
|
||||
```shell
|
||||
yarn remove husky && git config --unset core.hooksPath
|
||||
```
|
||||
-180
@@ -1,180 +0,0 @@
|
||||
# Guide
|
||||
|
||||
## Monorepo
|
||||
|
||||
It's recommended to add husky in root `package.json`. You can use tools like [lerna](https://github.com/lerna/lerna) and filters to only run scripts in packages that have been changed.
|
||||
|
||||
## Custom directory
|
||||
|
||||
If you want to install husky in another directory, for example `.config`, you can pass it to `install` command. For example:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [package.json]
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "husky install .config/husky"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Another case you may be in is if your `package.json` file and `.git` directory are not at the same level. For example, `project/.git` and `project/front/package.json`.
|
||||
|
||||
By design, `husky install` must be run in the same directory as `.git`, but you can change directory during `prepare` script and pass a subdirectory:
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [package.json]
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "cd .. && husky install front/.husky"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
In your hooks, you'll also need to change directory:
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [.husky/pre-commit]
|
||||
# ...
|
||||
cd front
|
||||
npm test
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Bypass hooks
|
||||
|
||||
You can bypass `pre-commit` and `commit-msg` hooks using Git `-n/--no-verify` option:
|
||||
|
||||
```shell
|
||||
git commit -m "yolo!" --no-verify
|
||||
```
|
||||
|
||||
For Git commands that don't have a `--no-verify` option, you can use `HUSKY` environment variable:
|
||||
|
||||
```shell
|
||||
HUSKY=0 git push # yolo!
|
||||
```
|
||||
|
||||
## Disable husky in CI/Docker/Prod
|
||||
|
||||
There's no right or wrong way to disable husky in CI/Docker/Prod context and is highly **dependent on your use-case**.
|
||||
|
||||
### With npm
|
||||
|
||||
If you want to prevent husky from installing completely, you can specifically disable `prepare` script with:
|
||||
|
||||
```shell
|
||||
npm pkg delete scripts.prepare
|
||||
npm ci --omit=dev
|
||||
```
|
||||
|
||||
Alternatively, you can ignore scripts during installation. Be cautious, this argument ignores dependencies scripts as well.
|
||||
|
||||
```shell
|
||||
npm ci --omit=dev --ignore-scripts
|
||||
```
|
||||
|
||||
When using `--omit=dev`, `npm` will set `NODE_ENV` to `production` for lifecycle scripts, so another alternative is to check it to conditionally install husky.
|
||||
|
||||
```shell
|
||||
npm pkg set scripts.prepare="node -e \"if (process.env.NODE_ENV !== 'production') { require('husky').install() }\""
|
||||
```
|
||||
|
||||
### With a custom script
|
||||
|
||||
You can create a custom JS script and conditionally require husky and install hooks.
|
||||
|
||||
::: code-group
|
||||
|
||||
```json [package.json]
|
||||
"prepare": "node ./prepare.js"
|
||||
```
|
||||
|
||||
```js [prepare.js]
|
||||
const isCi = process.env.CI !== undefined
|
||||
if (!isCi) {
|
||||
require('husky').install()
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Or make `prepare` script fail silently if husky is not installed:
|
||||
|
||||
```json [package.json]
|
||||
"prepare": "node -e \"try { require('husky').install() } catch (e) {if (e.code !== 'MODULE_NOT_FOUND') throw e}\""
|
||||
```
|
||||
|
||||
### With env variables
|
||||
|
||||
You can set `HUSKY` environment variable to `0` in your CI config file, to disable hooks installation.
|
||||
|
||||
Alternatively, most Continuous Integration Servers set a `CI` environment variable. You can use it in your hooks to detect if it's running in a CI.
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [.husky/pre-commit]
|
||||
# ...
|
||||
[ -n "$CI" ] && exit 0
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### With is-ci
|
||||
|
||||
You can also use [is-ci](https://github.com/watson/is-ci) in your `prepare` script to conditionally install husky
|
||||
|
||||
```shell
|
||||
npm install is-ci --save-dev
|
||||
```
|
||||
|
||||
::: code-group
|
||||
|
||||
```js [package.json]
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "is-ci || husky install"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Test hooks
|
||||
|
||||
If you want to test a hook, you can add `exit 1` at the end of the script to abort git command.
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [.husky/pre-commit]
|
||||
# ...
|
||||
exit 1 # Commit will be aborted
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
## Git-flow
|
||||
|
||||
If using [git-flow](https://github.com/petervanderdoes/gitflow-avh/) you need to ensure your git-flow hooks directory is set to use Husky's (`.husky` by default).
|
||||
|
||||
```shell
|
||||
git config gitflow.path.hooks .husky
|
||||
```
|
||||
|
||||
**Note:**
|
||||
|
||||
- If you are configuring git-flow _after_ you have installed husky, the git-flow setup process will correctly suggest the .husky directory.
|
||||
- If you have set a [custom directory](#custom-directory) for husky you need to specify that (ex. `git config gitflow.path.hooks .config/husky`)
|
||||
|
||||
To **revert** the git-flow hooks directory back to its default you need to reset the config to point to the default Git hooks directory.
|
||||
|
||||
```shell
|
||||
git config gitflow.path.hooks .git/hooks
|
||||
```
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
# How To
|
||||
|
||||
## Startup files
|
||||
|
||||
Husky allows you to execute local commands before running hooks. It reads commands from these files:
|
||||
|
||||
- `$XDG_CONFIG_HOME/husky/init.sh`
|
||||
- `~/.config/home/init.sh`
|
||||
- `~/.huskyrc` (deprecated)
|
||||
|
||||
## Skipping Git Hooks
|
||||
|
||||
### For a Single Command
|
||||
|
||||
Most Git commands include a `-n/--no-verify` option to skip hooks:
|
||||
|
||||
```sh
|
||||
git commit -m "..." -n # Skips Git hooks
|
||||
```
|
||||
|
||||
For commands without this flag, disable hooks temporarily with HUSKY=0:
|
||||
|
||||
```shell
|
||||
HUSKY=0 git ... # Temporarily disables all Git hooks
|
||||
git ... # Hooks will run again
|
||||
```
|
||||
|
||||
### For multiple commands
|
||||
|
||||
To disable hooks for an extended period (e.g., during rebase/merge):
|
||||
|
||||
```shell
|
||||
export HUSKY=0 # Disables all Git hooks
|
||||
git ...
|
||||
git ...
|
||||
unset HUSKY # Re-enables hooks
|
||||
```
|
||||
|
||||
### For a GUI or Globally
|
||||
|
||||
To disable Git hooks in a GUI client or globally, modify the husky config:
|
||||
|
||||
```sh
|
||||
# ~/.config/husky/init.sh
|
||||
export HUSKY=0 # Husky won't install and won't run hooks on your machine
|
||||
```
|
||||
|
||||
## CI server and Docker
|
||||
|
||||
To avoid installing Git Hooks on CI servers or in Docker, use `HUSKY=0`. For instance, in GitHub Actions:
|
||||
|
||||
```yml
|
||||
# https://docs.github.com/en/actions/learn-github-actions/variables
|
||||
env:
|
||||
HUSKY: 0
|
||||
```
|
||||
|
||||
If installing only `dependencies` (not `devDependencies`), the `"prepare": "husky"` script may fail because Husky won't be installed.
|
||||
|
||||
You have multiple solutions.
|
||||
|
||||
Modify the `prepare` script to never fail:
|
||||
|
||||
```json
|
||||
// package.json
|
||||
"prepare": "husky || true"
|
||||
```
|
||||
|
||||
You'll still get a `command not found` error message in your output which may be confusing. To make it silent, create `.husky/install.js`:
|
||||
|
||||
```js
|
||||
// Skip Husky install in production and CI
|
||||
if (process.env.NODE_ENV === 'production' || process.env.CI === '1') {
|
||||
process.exit(0)
|
||||
}
|
||||
const husky = await import('husky')
|
||||
husky()
|
||||
```
|
||||
|
||||
Then, use it in `prepare`:
|
||||
|
||||
```json
|
||||
"prepare": "node .husky/install.js"
|
||||
```
|
||||
|
||||
## Testing Hooks Without Committing
|
||||
|
||||
To test a hook, add `exit 1` to the hook script to abort the Git command:
|
||||
|
||||
```shell
|
||||
# .husky/pre-commit
|
||||
|
||||
# Your WIP script
|
||||
# ...
|
||||
|
||||
exit 1
|
||||
```
|
||||
|
||||
```shell
|
||||
git commit -m "testing pre-commit code"
|
||||
# A commit will not be created
|
||||
```
|
||||
|
||||
## Project Not in Git Root Directory
|
||||
|
||||
Husky doesn't install in parent directories (`../`) for security reasons. However, you can change the directory in the `prepare` script.
|
||||
|
||||
Consider this project structure:
|
||||
|
||||
```
|
||||
.
|
||||
├── .git/
|
||||
├── backend/ # No package.json
|
||||
└── frontend/ # Package.json with husky
|
||||
```
|
||||
|
||||
Set your prepare script like this:
|
||||
|
||||
```json
|
||||
"prepare": "cd .. && husky frontend/.husky"
|
||||
```
|
||||
|
||||
In your hook script, change the directory back to the relevant subdirectory:
|
||||
|
||||
```shell
|
||||
# frontend/.husky/pre-commit
|
||||
cd frontend
|
||||
npm test
|
||||
```
|
||||
|
||||
## Node Version Managers and GUIs
|
||||
|
||||
If you're using Git hooks in GUIs with Node installed via a version manager (like `nvm`, `n`, `fnm`, `asdf`, `volta`, etc...), you might face a `command not found` error due to `PATH` environment variable issues.
|
||||
|
||||
### Understanding `PATH` and Version Managers
|
||||
|
||||
`PATH` is an environment variable containing a list of directories. Your shell searches these directories for commands. If it doesn't find a command, you get a `command not found` message.
|
||||
|
||||
Run `echo $PATH` in a shell to view its contents.
|
||||
|
||||
Version managers work by:
|
||||
1. Adding initialization code to your shell startup file (`.zshrc`, `.bashrc`, etc.), which runs each time you open a terminal.
|
||||
2. Downloading Node versions to a directory in your home folder.
|
||||
|
||||
For example, if you have two Node versions:
|
||||
|
||||
```shell
|
||||
~/version-manager/Node-X/node
|
||||
~/version-manager/Node-Y/node
|
||||
```
|
||||
|
||||
Opening a terminal initializes the version manager, which picks a version (say `Node-Y`) and prepends its path to `PATH`:
|
||||
|
||||
```shell
|
||||
echo $PATH
|
||||
# Output
|
||||
~/version-manager/Node-Y/:...
|
||||
```
|
||||
|
||||
Now, node refers to `Node-Y`. Switching to `Node-X` changes `PATH` accordingly:
|
||||
|
||||
```shell
|
||||
echo $PATH
|
||||
# Output
|
||||
~/version-manager/Node-X/:...
|
||||
```
|
||||
|
||||
The issue arises because GUIs, launched outside a terminal, don't initialize the version manager, leaving `PATH` without the Node install path. Thus, Git hooks from GUIs often fail.
|
||||
|
||||
### Solution
|
||||
|
||||
Husky sources `~/.config/husky/init.sh` before each Git hook. Copy your version manager initialization code here to ensure it runs in GUIs.
|
||||
|
||||
Example with `nvm`:
|
||||
|
||||
```shell
|
||||
# ~/.config/husky/init.sh
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
||||
```
|
||||
|
||||
Alternatively, if your shell startup file is fast and lightweight, source it directly:
|
||||
|
||||
```shell
|
||||
# ~/.config/husky/init.sh
|
||||
. ~/.zshrc
|
||||
```
|
||||
|
||||
## Manual setup
|
||||
|
||||
Git needs to be configured and husky needs to setup files in `.husky/`.
|
||||
|
||||
Run the `husky` command once in your repo. Ideally, include it in the `prepare` script in `package.json` for automatic execution after each install (recommended).
|
||||
|
||||
::: code-group
|
||||
|
||||
```json [npm]
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "husky" // [!code hl]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json [pnpm]
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "husky" // [!code hl]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json [yarn]
|
||||
{
|
||||
"scripts": {
|
||||
// Yarn doesn't support prepare script
|
||||
"postinstall": "husky",
|
||||
// Include this if publishing to npmjs.com
|
||||
"prepack": "pinst --disable",
|
||||
"postpack": "pinst --enable"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json [pnpm]
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "husky" // [!code hl]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Run `prepare` once:
|
||||
|
||||
::: code-group
|
||||
|
||||
```sh [npm]
|
||||
npm run prepare
|
||||
```
|
||||
|
||||
```sh [pnpm]
|
||||
pnpm run prepare
|
||||
```
|
||||
|
||||
```sh [yarn]
|
||||
yarn run prepare
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
bun run prepare
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
Create a `pre-commit` file in the `.husky/` directory:
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [npm]
|
||||
# .husky/pre-commit
|
||||
npm test
|
||||
```
|
||||
|
||||
```shell [pnpm]
|
||||
# .husky/pre-commit
|
||||
pnpm test
|
||||
```
|
||||
|
||||
```shell [yarn]
|
||||
# .husky/pre-commit
|
||||
yarn test
|
||||
```
|
||||
|
||||
```sh [bun]
|
||||
# .husky/pre-commit
|
||||
bun test
|
||||
```
|
||||
|
||||
:::
|
||||
+48
-62
@@ -1,67 +1,28 @@
|
||||
[](https://opencollective.com/husky)
|
||||
|
||||
[](https://www.npmjs.org/package/husky)
|
||||
|
||||
[](https://github.com/typicode/husky/actions)
|
||||

|
||||
|
||||
> Modern native git hooks made easy
|
||||
|
||||
Husky improves your commits and more 🐶 _woof!_
|
||||
Husky enhances your commits and more 🐶 _woof!_
|
||||
|
||||
You can use it to **lint your commit messages**, **run tests**, **lint code**, etc... when you commit or push. Husky supports [all client-side Git hooks](https://git-scm.com/docs/githooks).
|
||||
Automatically **lint your commit messages**, **code**, and **run tests** upon committing or pushing.
|
||||
|
||||
Click [here](/getting-started) to get started.
|
||||
Get started [here](/get-started).
|
||||
|
||||
## Features
|
||||
|
||||
- Lightweight with zero dependencies (`6 kB`)
|
||||
- Powered by modern new Git feature (`core.hooksPath`)
|
||||
- Follows [npm](https://docs.npmjs.com/cli/v8/using-npm/scripts#best-practices) and [Yarn](https://yarnpkg.com/advanced/lifecycle-scripts#a-note-about-postinstall) best practices regarding `autoinstall`
|
||||
- User-friendly messages
|
||||
- Opt-in/opt-out
|
||||
- **Supports**
|
||||
- macOS, Linux and Windows
|
||||
- Git GUIs
|
||||
- Custom hooks directory
|
||||
- Nested projects
|
||||
- Monorepos
|
||||
|
||||
## Used by
|
||||
|
||||
Husky is used by these awesome projects:
|
||||
|
||||
- [webpack/webpack](https://github.com/webpack/webpack)
|
||||
- [angular/angular](https://github.com/angular/angular)
|
||||
- [angular/angular-cli](https://github.com/angular/angular-cli)
|
||||
- [angular/components](https://github.com/angular/components)
|
||||
- [vercel/hyper](https://github.com/vercel/hyper)
|
||||
- [blitz-js/blitz](https://github.com/blitz-js/blitz)
|
||||
- [facebook/docusaurus](https://github.com/facebook/docusaurus)
|
||||
- [typescript-eslint/typescript-eslint](https://github.com/typescript-eslint/typescript-eslint)
|
||||
- [11ty/eleventy](https://github.com/11ty/eleventy)
|
||||
- [stylelint/stylelint](https://github.com/stylelint/stylelint)
|
||||
- [rollup/rollup](https://github.com/rollup/rollup)
|
||||
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri)
|
||||
- [NativeScript/NativeScript](https://github.com/NativeScript/NativeScript)
|
||||
- [formatjs/formatjs](https://github.com/formatjs/formatjs)
|
||||
- [react-bootstrap/react-bootstrap](https://github.com/react-bootstrap/react-bootstrap)
|
||||
- [react-dnd/react-dnd](https://github.com/react-dnd/react-dnd)
|
||||
- [react-grid-layout/react-grid-layout](https://github.com/react-grid-layout/react-grid-layout)
|
||||
- [snabbdom/snabbdom](https://github.com/snabbdom/snabbdom)
|
||||
- [logaretm/vee-validate](https://github.com/logaretm/vee-validate)
|
||||
- [zenorocha/clipboard.js](https://github.com/zenorocha/clipboard.js)
|
||||
- [NodeBB/NodeBB](https://github.com/NodeBB/NodeBB)
|
||||
- [ant-design/ant-design](https://github.com/ant-design/ant-design)
|
||||
- And [**more than 1.1M projects**](https://github.com/typicode/husky/network/dependents?package_id=UGFja2FnZS0xODQzNTgwNg%3D%3D)
|
||||
|
||||
## Articles
|
||||
|
||||
- [Why husky has dropped conventional JS config](https://blog.typicode.com/posts/husky-git-hooks-javascript-config/)
|
||||
- [Why husky doesn't autoinstall anymore](https://blog.typicode.com/posts/husky-git-hooks-autoinstall/)
|
||||
See what's new in `v9` in the [Changelog]() 🚀
|
||||
|
||||
## Sponsors
|
||||
|
||||
Does your company use husky? Ask your manager or marketing team if your company would be interested in supporting this project.
|
||||
Support this project by becoming a sponsor [here](https://github.com/sponsors/typicode) 💖
|
||||
|
||||
### GitHub
|
||||
|
||||
<p align="center">
|
||||
<a href="./sponsorkit/sponsors.svg">
|
||||
<img src='./sponsorkit/sponsors.svg'/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
### Open Collective
|
||||
|
||||
<a href="https://opencollective.com/husky/tiers/company/0/website"><img src="https://opencollective.com/husky/tiers/company/0/avatar.svg?avatarHeight=120"></a>
|
||||
<a href="https://opencollective.com/husky/tiers/company/1/website"><img src="https://opencollective.com/husky/tiers/company/1/avatar.svg?avatarHeight=120"></a>
|
||||
@@ -69,13 +30,38 @@ Does your company use husky? Ask your manager or marketing team if your company
|
||||
<a href="https://opencollective.com/husky/tiers/company/3/website"><img src="https://opencollective.com/husky/tiers/company/3/avatar.svg?avatarHeight=120"></a>
|
||||
<a href="https://opencollective.com/husky/tiers/company/4/website"><img src="https://opencollective.com/husky/tiers/company/4/avatar.svg?avatarHeight=120"></a>
|
||||
<a href="https://opencollective.com/husky/tiers/company/5/website"><img src="https://opencollective.com/husky/tiers/company/5/avatar.svg?avatarHeight=120"></a>
|
||||
<a href="https://opencollective.com/husky/tiers/company/6/website"><img src="https://opencollective.com/husky/tiers/company/6/avatar.svg?avatarHeight=120"></a>
|
||||
<a href="https://opencollective.com/husky/tiers/company/7/website"><img src="https://opencollective.com/husky/tiers/company/7/avatar.svg?avatarHeight=120"></a>
|
||||
<a href="https://opencollective.com/husky/tiers/company/8/website"><img src="https://opencollective.com/husky/tiers/company/8/avatar.svg?avatarHeight=120"></a>
|
||||
<a href="https://opencollective.com/husky/tiers/company/9/website"><img src="https://opencollective.com/husky/tiers/company/9/avatar.svg?avatarHeight=120"></a>
|
||||
|
||||
Find husky helpful? Become a backer and show your appreciation with a monthly donation on [Open Collective](https://opencollective.com/husky). You can also tip with a one-time donation.
|
||||
## Features
|
||||
|
||||
<a href="https://opencollective.com/husky" target="_blank"><img src="https://opencollective.com/husky/tiers/individual.svg?avatarHeight=32"/></a>
|
||||
- Just `2 kB` (📦 _gzipped_) with no dependencies
|
||||
- Uses new Git feature (`core.hooksPath`)
|
||||
- Adheres to Git's native hook organization
|
||||
- Aligns with [npm](https://docs.npmjs.com/cli/v10/using-npm/scripts#best-practices) best practices using `prepare` script
|
||||
- Clear user messages
|
||||
- O pt-in/opt-out options
|
||||
- Branch-specific hooks
|
||||
- Supports:
|
||||
- macOS, Linux, Windows
|
||||
- Git GUIs, Node version managers, custom hooks directory, nested projects, monorepos
|
||||
- [All 13 client-side Git hooks](https://git-scm.com/docs/githooks)
|
||||
|
||||
GitHub sponsors can be viewed on my [profile](https://github.com/typicode). All past and current Open Collective sponsors can be viewed on [here](https://opencollective.com/husky).
|
||||
## Used by
|
||||
|
||||
Husky is used in [**over 1.3M projects**](https://github.com/typicode/husky/network/dependents?package_id=UGFja2FnZS0xODQzNTgwNg%3D%3D) on GitHub, including:
|
||||
|
||||
- [vercel/next.js](https://github.com/vercel/next.js)
|
||||
- [vercel/hyper](https://github.com/vercel/hyper)
|
||||
- [webpack/webpack](https://github.com/webpack/webpack)
|
||||
- [angular/angular](https://github.com/angular/angular)
|
||||
- [facebook/docusaurus](https://github.com/facebook/docusaurus)
|
||||
- [microsoft/vscode](https://github.com/microsoft/vscode)
|
||||
- [11ty/eleventy](https://github.com/11ty/eleventy)
|
||||
- [stylelint/stylelint](https://github.com/stylelint/stylelint)
|
||||
- [colinhacks/zod](https://github.com/colinhacks/zod)
|
||||
- [rollup/rollup](https://github.com/rollup/rollup)
|
||||
- ...
|
||||
|
||||
## Articles
|
||||
|
||||
- [Why husky has dropped conventional JS config](https://blog.typicode.com/husky-git-hooks-javascript-config/)
|
||||
- [Why husky doesn't autoinstall anymore](https://blog.typicode.com/husky-git-hooks-autoinstall/)
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
# Migrating from v4
|
||||
|
||||
## CLI
|
||||
|
||||
See [husky-4-to-8](https://github.com/typicode/husky-4-to-8) CLI to quickly migrate from v4 to v8.
|
||||
|
||||
## Manually
|
||||
# Migrate from v4
|
||||
|
||||
If you were calling `package.json` scripts using `npm` or `yarn`, **you can simply copy your commands** from your config file to the corresponding hook:
|
||||
|
||||
::: code-group
|
||||
Husky v4
|
||||
|
||||
```js [.huskyrc.json (v4)]
|
||||
```json
|
||||
// package.json
|
||||
{
|
||||
"hooks": {
|
||||
"pre-commit": "npm test && npm run foo"
|
||||
"pre-commit": "npm test && npm run foo" // [!code hl]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```shell [.husky/pre-commit (v8)]
|
||||
# ...
|
||||
npm test
|
||||
npm run foo
|
||||
```
|
||||
Husky v9
|
||||
|
||||
:::
|
||||
```shell
|
||||
# .husky/pre-commit
|
||||
# Note that you can now have commands on multiple lines
|
||||
npm test // [!code hl]
|
||||
npm run foo // [!code hl]
|
||||
```
|
||||
|
||||
If you were calling locally installed binaries, **you need to run them via your package manager now**:
|
||||
|
||||
@@ -38,7 +34,7 @@ If you were calling locally installed binaries, **you need to run them via your
|
||||
}
|
||||
```
|
||||
|
||||
```shell [.husky/pre-commit (v8)]
|
||||
```shell [.husky/commit-msg (v9)]
|
||||
# ...
|
||||
npx --no jest
|
||||
# or
|
||||
@@ -59,7 +55,7 @@ yarn jest
|
||||
}
|
||||
```
|
||||
|
||||
```shell [.husky/commit-msg (v8)]
|
||||
```shell [.husky/commit-msg (v9)]
|
||||
# ...
|
||||
npx --no -- commitlint --edit $1
|
||||
# or
|
||||
Generated
+3936
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "vitepress",
|
||||
"build": "sponsorkit"
|
||||
},
|
||||
"devDependencies": {
|
||||
"sponsorkit": "^0.9.1",
|
||||
"vitepress": "^1.0.0-rc.40"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import { defineConfig, presets } from 'sponsorkit'
|
||||
|
||||
export default defineConfig({
|
||||
// includePrivate: true,
|
||||
tiers: [
|
||||
{
|
||||
title: 'Past Sponsors',
|
||||
monthlyDollars: -1,
|
||||
preset: presets.xs,
|
||||
},
|
||||
{
|
||||
title: 'Backers',
|
||||
preset: presets.base,
|
||||
},
|
||||
{
|
||||
title: 'Sponsors',
|
||||
monthlyDollars: 10,
|
||||
preset: presets.medium,
|
||||
// to insert custom elements after the tier block
|
||||
composeAfter: (composer, _tierSponsors, _config) => {
|
||||
composer.addSpan(10)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Bronze Sponsors',
|
||||
monthlyDollars: 100,
|
||||
preset: presets.large,
|
||||
},
|
||||
{
|
||||
title: 'Silver Sponsors',
|
||||
monthlyDollars: 250,
|
||||
preset: presets.large,
|
||||
},
|
||||
{
|
||||
title: 'Gold Sponsors',
|
||||
monthlyDollars: 500,
|
||||
preset: presets.xl,
|
||||
},
|
||||
],
|
||||
})
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 508 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 76 KiB |
@@ -0,0 +1,41 @@
|
||||
# Troubleshoot
|
||||
|
||||
## Command not found
|
||||
|
||||
See [How To](how-to) for solutions.
|
||||
|
||||
## Hooks not running
|
||||
|
||||
1. Verify the file name is correct. For example, `precommit` or `pre-commit.sh` are invalid names. Refer to the Git hooks [documentation](https://git-scm.com/docs/githooks) for valid names.
|
||||
2. Run `git config core.hooksPath` and ensure it points to `.husky/_` (or your custom hooks directory).
|
||||
1. Confirm your Git version is above `2.9`.
|
||||
|
||||
## `.git/hooks/` Not Working After Uninstall
|
||||
|
||||
If hooks in `.git/hooks/` don't work post-uninstalling `husky`, execute `git config --unset core.hooksPath`.
|
||||
|
||||
## Yarn on Windows
|
||||
|
||||
Git hooks might fail with Yarn on Windows using Git Bash (`stdin is not a tty`). For Windows users, implement this workaround:
|
||||
|
||||
1. Create `.husky/common.sh`:
|
||||
|
||||
```shell
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Workaround for Windows 10, Git Bash, and Yarn
|
||||
if command_exists winpty && test -t 1; then
|
||||
exec < /dev/tty
|
||||
fi
|
||||
```
|
||||
|
||||
2. Source it where Yarn commands are run:
|
||||
|
||||
```shell
|
||||
# .husky/pre-commit
|
||||
. "$(dirname -- "$0")/common.sh"
|
||||
|
||||
yarn ...
|
||||
```
|
||||
@@ -1,77 +0,0 @@
|
||||
# Troubleshooting
|
||||
|
||||
## Command not found
|
||||
|
||||
If you're running Git from an app and the command can be found in your terminal, this means that the `PATH` in your app is different from your terminal.
|
||||
|
||||
You can `echo $PATH` in your terminal and configure your app to use the same value.
|
||||
|
||||
If you've installed your command using `brew`, see the [Homebrew FAQ](https://docs.brew.sh/FAQ) to make your command available to your app.
|
||||
|
||||
Finally, if you're using a script for managing versions like `nvm`, `n`, `rbenv`, `pyenv`, ... you can use `~/.huskyrc` to load the necessary before running hooks.
|
||||
|
||||
For example, for `nvm` that would be:
|
||||
|
||||
::: code-group
|
||||
|
||||
```shell [~/.huskyrc]
|
||||
# This loads nvm.sh, sets the correct PATH before running hook, and ensures the project version of Node
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
|
||||
# If you have an .nvmrc file, we use the relevant node version
|
||||
if [ -f ".nvmrc" ]; then
|
||||
nvm use
|
||||
fi
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
::: info
|
||||
For some apps (e.g., VS Code), you can resolve this simply by restarting the app. Try this before following any of these steps above!\*\*
|
||||
:::
|
||||
|
||||
## Hooks not running
|
||||
|
||||
1. Ensure that you don't have a typo in your filename. For example, `precommit` or `pre-commit.sh` are invalid names. See Git hooks [documentation](https://git-scm.com/docs/githooks) for valid names.
|
||||
1. Check that `git config core.hooksPath` returns `.husky` (or your custom hooks directory).
|
||||
1. Verify that hook files are executable. This is automatically set when using `husky add` command but you can run `chmod +x .husky/<hookname>` to fix that.
|
||||
1. Check that your version of Git is greater than `2.9`.
|
||||
|
||||
## .git/hooks/ not working after uninstall
|
||||
|
||||
If after uninstalling `husky`, hooks in `.git/hooks/` aren't working. Run `git config --unset core.hooksPath`.
|
||||
|
||||
Note: this was done automatically by `npm <7` when uninstalling husky, however `preuninstall` is now unsupported.
|
||||
|
||||
## Yarn on Windows
|
||||
|
||||
Git hooks may fail when using Yarn on Windows with Git Bash (`stdin is not a tty`). If you have users on Windows, it's highly recommended to add the following workaround.
|
||||
|
||||
1. Create `.husky/common.sh`:
|
||||
|
||||
```shell
|
||||
command_exists () {
|
||||
command -v "$1" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Workaround for Windows 10, Git Bash and Yarn
|
||||
if command_exists winpty && test -t 1; then
|
||||
exec < /dev/tty
|
||||
fi
|
||||
```
|
||||
|
||||
2. Source it in in places where Yarn is used to run commands:
|
||||
|
||||
```shell
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
. "$(dirname -- "$0")/common.sh"
|
||||
|
||||
yarn ...
|
||||
```
|
||||
|
||||
## Does it work on Windows?
|
||||
|
||||
Yes. When you install Git on Windows, it comes with the necessary software to run shell scripts.
|
||||
@@ -1,44 +1,20 @@
|
||||
#!/usr/bin/env sh
|
||||
if [ -z "$husky_skip_init" ]; then
|
||||
debug() {
|
||||
if [ "$HUSKY_DEBUG" = "1" ]; then
|
||||
echo "husky (debug) - $1"
|
||||
fi
|
||||
}
|
||||
[ "$HUSKY" = "2" ] && set -x
|
||||
h="${0##*/}"
|
||||
s="${0%/*/*}/$h"
|
||||
|
||||
readonly hook_name="${0##*/}"
|
||||
debug "starting $hook_name..."
|
||||
[ ! -f "$s" ] && exit 0
|
||||
|
||||
if [ "$HUSKY" = "0" ]; then
|
||||
debug "HUSKY env variable is set to 0, skipping hook"
|
||||
exit 0
|
||||
fi
|
||||
for f in "${XDG_CONFIG_HOME:-$HOME/.config}/husky/init.sh" "$HOME/.huskyrc.sh"; do
|
||||
# shellcheck disable=SC1090
|
||||
[ -f "$f" ] && . "$f"
|
||||
done
|
||||
|
||||
for file in "${XDG_CONFIG_HOME:-$HOME/.config}/husky/init.sh" "$HOME/.huskyrc.sh"; do
|
||||
if [ -f "$file" ]; then
|
||||
debug "sourcing $file"
|
||||
. "$file"
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ "$HUSKY" = "0" ] && exit 0
|
||||
|
||||
readonly husky_skip_init=1
|
||||
export husky_skip_init
|
||||
sh -e "$s" "$@"
|
||||
c=$?
|
||||
|
||||
if [ "${SHELL##*/}" = "zsh" ]; then
|
||||
zsh --emulate sh -e "$0" "$@"
|
||||
else
|
||||
sh -e "$0" "$@"
|
||||
fi
|
||||
exitCode="$?"
|
||||
|
||||
if [ $exitCode != 0 ]; then
|
||||
echo "husky - $hook_name hook exited with code $exitCode (error)"
|
||||
fi
|
||||
|
||||
if [ $exitCode = 127 ]; then
|
||||
echo "husky - command not found in PATH=$PATH"
|
||||
fi
|
||||
|
||||
exit $exitCode
|
||||
fi
|
||||
[ $c != 0 ] && echo "husky - $h script failed (code $c)"
|
||||
[ $c = 127 ] && echo "husky - command not found in PATH=$PATH"
|
||||
exit 1
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
export default function (dir?: string): string;
|
||||
@@ -0,0 +1,23 @@
|
||||
import c from 'child_process'
|
||||
import f, { writeFileSync as w } from 'fs'
|
||||
import p from 'path'
|
||||
|
||||
let l = [ 'pre-commit', 'prepare-commit-msg', 'commit-msg', 'post-commit', 'applypatch-msg', 'pre-applypatch', 'post-applypatch', 'pre-rebase', 'post-rewrite', 'post-checkout', 'post-merge', 'pre-push', 'pre-auto-gc' ]
|
||||
|
||||
export default (d = '.husky') => {
|
||||
if (process.env.HUSKY === '0') return 'HUSKY=0 skip install'
|
||||
if (d.includes('..')) return '.. not allowed'
|
||||
if (!f.existsSync('.git')) return `.git can't be found`
|
||||
|
||||
let _ = (x = '') => p.join(d, '_', x)
|
||||
let { status: s, stderr: e } = c.spawnSync('git', ['config', 'core.hooksPath', _()])
|
||||
if (s == null) return 'git command not found'
|
||||
if (s) return '' + e
|
||||
|
||||
f.mkdirSync(_(), { recursive: true })
|
||||
w(_('.gitignore'), '*')
|
||||
f.copyFileSync(new URL('husky.sh', import.meta.url), _('h'))
|
||||
l.forEach(h => w(_(h), `#!/usr/bin/env sh\n. "\${0%/*}/h"`, { mode: 0o755 }))
|
||||
w(_('husky.sh'), '')
|
||||
return ''
|
||||
}
|
||||
Generated
+20
-4700
File diff suppressed because it is too large
Load Diff
+23
-41
@@ -1,43 +1,25 @@
|
||||
{
|
||||
"name": "husky",
|
||||
"version": "8.0.3",
|
||||
"description": "Modern native Git hooks made easy",
|
||||
"keywords": [
|
||||
"git",
|
||||
"hooks",
|
||||
"pre-commit"
|
||||
],
|
||||
"homepage": "https://typicode.github.io/husky",
|
||||
"repository": "typicode/husky",
|
||||
"funding": "https://github.com/sponsors/typicode",
|
||||
"license": "MIT",
|
||||
"author": "Typicode <[email protected]>",
|
||||
"bin": "lib/bin.js",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
"lib",
|
||||
"husky.sh"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"test": "sh test/all.sh",
|
||||
"lint": "eslint src --ext .ts",
|
||||
"serve": "docsify serve docs",
|
||||
"prepare": "npm run build && node lib/bin install",
|
||||
"docs:dev": "vitepress dev docs",
|
||||
"docs:build": "vitepress build docs",
|
||||
"docs:preview": "vitepress preview docs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^17.6.3",
|
||||
"@commitlint/config-conventional": "^17.6.3",
|
||||
"@tsconfig/node14": "^1.0.3",
|
||||
"@types/node": "^20.1.4",
|
||||
"@typicode/eslint-config": "^1.1.0",
|
||||
"typescript": "^5.0.4",
|
||||
"vitepress": "^1.0.0-beta.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
"name": "husky",
|
||||
"version": "8.0.3",
|
||||
"description": "Modern native Git hooks",
|
||||
"keywords": [
|
||||
"git",
|
||||
"hooks",
|
||||
"pre-commit"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/typicode/husky.git"
|
||||
},
|
||||
"funding": "https://github.com/sponsors/typicode",
|
||||
"license": "MIT",
|
||||
"author": "typicode",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
"husky": "bin.js"
|
||||
},
|
||||
"exports": "./index.js",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
import p = require('path')
|
||||
import h = require('./')
|
||||
|
||||
// Show usage and exit with code
|
||||
function help(code: number) {
|
||||
console.log(`Usage:
|
||||
husky install [dir] (default: .husky)
|
||||
husky uninstall
|
||||
husky set|add <file> [cmd]`)
|
||||
process.exit(code)
|
||||
}
|
||||
|
||||
// Get CLI arguments
|
||||
const [, , cmd, ...args] = process.argv
|
||||
const ln = args.length
|
||||
const [x, y] = args
|
||||
|
||||
// Set or add command in hook
|
||||
const hook = (fn: (a1: string, a2: string) => void) => (): void =>
|
||||
// Show usage if no arguments or more than 2 are provided
|
||||
!ln || ln > 2 ? help(2) : fn(x, y)
|
||||
|
||||
// CLI commands
|
||||
const cmds: { [key: string]: () => void } = {
|
||||
install: (): void => (ln > 1 ? help(2) : h.install(x)),
|
||||
uninstall: h.uninstall,
|
||||
set: hook(h.set),
|
||||
add: hook(h.add),
|
||||
['-v']: () =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-var-requires
|
||||
console.log(require(p.join(__dirname, '../package.json')).version),
|
||||
}
|
||||
|
||||
// Run CLI
|
||||
try {
|
||||
// Run command or show usage for unknown command
|
||||
cmds[cmd] ? cmds[cmd]() : help(0)
|
||||
} catch (e) {
|
||||
console.error(e instanceof Error ? `husky - ${e.message}` : e)
|
||||
process.exit(1)
|
||||
}
|
||||
-112
@@ -1,112 +0,0 @@
|
||||
import cp = require('child_process')
|
||||
import fs = require('fs')
|
||||
import p = require('path')
|
||||
import os = require('os')
|
||||
|
||||
// Logger
|
||||
const l = (msg: string): void => console.log(`husky - ${msg}`)
|
||||
|
||||
// Execute Git command
|
||||
const git = (args: string[]): cp.SpawnSyncReturns<Buffer> =>
|
||||
cp.spawnSync('git', args, { stdio: 'inherit' })
|
||||
|
||||
// Install husky
|
||||
export function install(dir = '.husky'): void {
|
||||
if (process.env.HUSKY === '0') {
|
||||
l('HUSKY env variable is set to 0, skipping install')
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure that we're inside a Git repository
|
||||
// If git command is not found, status is null and we should return
|
||||
// That's why status value needs to be checked explicitly
|
||||
if (git(['rev-parse']).status !== 0) {
|
||||
l(`git command not found, skipping install`)
|
||||
return
|
||||
}
|
||||
|
||||
// Custom dir help
|
||||
const url = 'https://typicode.github.io/husky/guide.html#custom-directory'
|
||||
|
||||
// Ensure that we're not trying to install outside of cwd
|
||||
if (!p.resolve(process.cwd(), dir).startsWith(process.cwd())) {
|
||||
throw new Error(`.. not allowed (see ${url})`)
|
||||
}
|
||||
|
||||
// Ensure that cwd is git top level
|
||||
if (!fs.existsSync('.git')) {
|
||||
throw new Error(`.git can't be found (see ${url})`)
|
||||
}
|
||||
|
||||
try {
|
||||
// Create .husky/_
|
||||
fs.mkdirSync(p.join(dir, '_'), { recursive: true })
|
||||
|
||||
// Create .husky/_/.gitignore
|
||||
fs.writeFileSync(p.join(dir, '_/.gitignore'), '*')
|
||||
|
||||
// Copy husky.sh to .husky/_/husky.sh
|
||||
fs.copyFileSync(p.join(__dirname, '../husky.sh'), p.join(dir, '_/husky.sh'))
|
||||
|
||||
// Configure repo
|
||||
const { error } = git(['config', 'core.hooksPath', dir])
|
||||
if (error) {
|
||||
throw error
|
||||
}
|
||||
} catch (e) {
|
||||
l('Git hooks failed to install')
|
||||
throw e
|
||||
}
|
||||
|
||||
l('Git hooks installed')
|
||||
}
|
||||
|
||||
// Create a hook file if it doesn't exist or overwrite it
|
||||
export function set(file: string, cmd: string): void {
|
||||
const dir = p.dirname(file)
|
||||
if (!fs.existsSync(dir)) {
|
||||
throw new Error(
|
||||
`can't create hook, ${dir} directory doesn't exist (try running husky install)`,
|
||||
)
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
file,
|
||||
`#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
${cmd}
|
||||
`,
|
||||
{ mode: 0o0755 },
|
||||
)
|
||||
|
||||
l(`created ${file}`)
|
||||
|
||||
if (os.type() === 'Windows_NT') {
|
||||
l(
|
||||
`Due to a limitation on Windows systems, the executable bit of the file cannot be set without using git.
|
||||
To fix this, the file ${file} has been automatically moved to the staging environment and the executable bit has been set using git.
|
||||
Note that, if you remove the file from the staging environment, the executable bit will be removed.
|
||||
You can add the file back to the staging environment and include the executable bit using the command 'git update-index -add --chmod=+x ${file}'.
|
||||
If you have already committed the file, you can add the executable bit using 'git update-index --chmod=+x ${file}'.
|
||||
You will have to commit the file to have git keep track of the executable bit.`,
|
||||
)
|
||||
|
||||
git(['update-index', '--add', '--chmod=+x', file])
|
||||
}
|
||||
}
|
||||
|
||||
// Create a hook if it doesn't exist or append command to it
|
||||
export function add(file: string, cmd: string): void {
|
||||
if (fs.existsSync(file)) {
|
||||
fs.appendFileSync(file, `${cmd}\n`)
|
||||
l(`updated ${file}`)
|
||||
} else {
|
||||
set(file, cmd)
|
||||
}
|
||||
}
|
||||
|
||||
// Uninstall husky
|
||||
export function uninstall(): void {
|
||||
git(['config', '--unset', 'core.hooksPath'])
|
||||
}
|
||||
Regular → Executable
+5
-4
@@ -1,10 +1,11 @@
|
||||
#!/bin/sh
|
||||
# To run tests, type ./test.sh in your terminal
|
||||
set -e
|
||||
npm run build
|
||||
npm pack && mv husky-*.tgz /tmp/husky.tgz
|
||||
sh test/1_default.sh
|
||||
sh test/2_in-sub-dir.sh
|
||||
sh test/3_from-sub-dir.sh
|
||||
sh test/4_not-git-dir.sh
|
||||
sh test/5_set-add.sh
|
||||
sh test/6_git_command_not_found.sh
|
||||
sh test/7_command_not_found.sh
|
||||
sh test/5_git_command_not_found.sh
|
||||
sh test/6_command_not_found.sh
|
||||
# sh test/7_time.sh
|
||||
Regular → Executable
+5
-8
@@ -1,17 +1,14 @@
|
||||
. "$(dirname -- "$0")/functions.sh"
|
||||
#!/bin/sh
|
||||
. test/functions.sh
|
||||
setup
|
||||
install
|
||||
|
||||
npx --no-install husky install
|
||||
npx --no-install husky
|
||||
|
||||
# Test core.hooksPath
|
||||
expect_hooksPath_to_be ".husky"
|
||||
expect_hooksPath_to_be ".husky/_"
|
||||
|
||||
# Test pre-commit
|
||||
git add package.json
|
||||
npx --no-install husky add .husky/pre-commit "echo \"pre-commit\" && exit 1"
|
||||
echo "echo \"pre-commit\" && exit 1" >.husky/pre-commit
|
||||
expect 1 "git commit -m foo"
|
||||
|
||||
# Uninstall
|
||||
npx --no-install husky uninstall
|
||||
expect 1 "git config core.hooksPath"
|
||||
|
||||
Regular → Executable
+5
-4
@@ -1,14 +1,15 @@
|
||||
. "$(dirname -- "$0")/functions.sh"
|
||||
#!/bin/sh
|
||||
. test/functions.sh
|
||||
setup
|
||||
install
|
||||
|
||||
# Test custom dir support
|
||||
mkdir sub
|
||||
npx --no-install husky install sub/husky
|
||||
npx --no-install husky add sub/husky/pre-commit "echo \"pre-commit\" && exit 1"
|
||||
npx --no-install husky sub/husky
|
||||
echo "echo \"pre-commit\" && exit 1" >sub/husky/pre-commit
|
||||
|
||||
# Test core.hooksPath
|
||||
expect_hooksPath_to_be "sub/husky"
|
||||
expect_hooksPath_to_be "sub/husky/_"
|
||||
|
||||
# Test pre-commit
|
||||
git add package.json
|
||||
|
||||
Regular → Executable
+6
-5
@@ -1,4 +1,5 @@
|
||||
. "$(dirname -- "$0")/functions.sh"
|
||||
#!/bin/sh
|
||||
. test/functions.sh
|
||||
setup
|
||||
|
||||
# Skip test for npm 6
|
||||
@@ -12,10 +13,10 @@ npm --version | grep "^6\." && exit 0
|
||||
mkdir sub
|
||||
cd sub
|
||||
npm install ../../husky.tgz
|
||||
cat > package.json << EOL
|
||||
cat >package.json <<EOL
|
||||
{
|
||||
"scripts": {
|
||||
"prepare": "cd .. && husky install sub/.husky"
|
||||
"prepare": "cd .. && husky sub/.husky"
|
||||
}
|
||||
}
|
||||
EOL
|
||||
@@ -24,10 +25,10 @@ EOL
|
||||
npm run prepare
|
||||
|
||||
# Add hook
|
||||
npx --no-install husky add .husky/pre-commit "echo \"pre-commit hook\" && exit 1"
|
||||
echo "echo \"pre-commit hook\" && exit 1" >.husky/pre-commit
|
||||
|
||||
# Test core.hooksPath
|
||||
expect_hooksPath_to_be "sub/.husky"
|
||||
expect_hooksPath_to_be "sub/.husky/_"
|
||||
|
||||
# Test pre-commit
|
||||
git add package.json
|
||||
|
||||
Regular → Executable
+6
-2
@@ -1,7 +1,11 @@
|
||||
. "$(dirname -- "$0")/functions.sh"
|
||||
#!/bin/sh
|
||||
. test/functions.sh
|
||||
setup
|
||||
install
|
||||
|
||||
# Should not fail
|
||||
rm -rf .git
|
||||
expect 0 "npx --no-install husky install"
|
||||
expect 0 "npx --no-install husky"
|
||||
|
||||
mkdir .git
|
||||
expect 0 "npx --no-install husky"
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
. test/functions.sh
|
||||
setup
|
||||
install
|
||||
|
||||
cat >index.mjs <<EOL
|
||||
process.env.PATH = ''
|
||||
import h from 'husky'
|
||||
console.log(h())
|
||||
EOL
|
||||
expect 0 "node index.mjs"
|
||||
@@ -1,17 +0,0 @@
|
||||
. "$(dirname -- "$0")/functions.sh"
|
||||
setup
|
||||
install
|
||||
|
||||
f=".husky/pre-commit"
|
||||
|
||||
npx --no-install husky install
|
||||
|
||||
npx --no-install husky add $f "foo"
|
||||
grep -m 1 _ $f && grep foo $f && ok
|
||||
|
||||
npx --no-install husky add .husky/pre-commit "bar"
|
||||
grep -m 1 _ $f && grep foo $f && grep bar $f && ok
|
||||
|
||||
npx --no-install husky set .husky/pre-commit "baz"
|
||||
grep -m 1 _ $f && grep foo $f || grep bar $f || grep baz $f && ok
|
||||
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
. test/functions.sh
|
||||
setup
|
||||
install
|
||||
|
||||
npx --no-install husky
|
||||
|
||||
# Test core.hooksPath
|
||||
expect_hooksPath_to_be ".husky/_"
|
||||
|
||||
# Test pre-commit with 127 exit code
|
||||
git add package.json
|
||||
echo "exit 127" >.husky/pre-commit
|
||||
expect 1 "git commit -m foo"
|
||||
@@ -1,10 +0,0 @@
|
||||
. "$(dirname -- "$0")/functions.sh"
|
||||
setup
|
||||
install
|
||||
|
||||
cat > index.js << EOL
|
||||
process.env.PATH = ''
|
||||
require('husky').install()
|
||||
EOL
|
||||
expect 0 "node index.js"
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
. "$(dirname -- "$0")/functions.sh"
|
||||
setup
|
||||
install
|
||||
|
||||
npx --no-install husky install
|
||||
|
||||
# Test core.hooksPath
|
||||
expect_hooksPath_to_be ".husky"
|
||||
|
||||
# Test pre-commit with 127 exit code
|
||||
git add package.json
|
||||
npx --no-install husky add .husky/pre-commit "exit 127"
|
||||
expect 1 "git commit -m foo"
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
. test/functions.sh
|
||||
setup
|
||||
install
|
||||
|
||||
npx --no-install husky
|
||||
|
||||
git add package.json
|
||||
echo "echo pre-commit" >.husky/pre-commit
|
||||
time git commit -m foo
|
||||
Regular → Executable
+3
-4
@@ -2,7 +2,7 @@
|
||||
set -eu
|
||||
|
||||
setup() {
|
||||
name="$(basename -- $0)"
|
||||
name="$(basename -- "$0")"
|
||||
testDir="/tmp/husky-test-$name"
|
||||
echo
|
||||
echo "-------------------"
|
||||
@@ -21,8 +21,7 @@ setup() {
|
||||
git config user.name "test"
|
||||
|
||||
# Init package.json
|
||||
npm_config_loglevel="error"
|
||||
npm init -y 1>/dev/null
|
||||
npm_config_loglevel="error" npm init -y 1>/dev/null
|
||||
}
|
||||
|
||||
install() {
|
||||
@@ -40,7 +39,7 @@ expect() {
|
||||
}
|
||||
|
||||
expect_hooksPath_to_be() {
|
||||
readonly hooksPath=`git config core.hooksPath`
|
||||
hooksPath=$(git config core.hooksPath)
|
||||
if [ "$hooksPath" != "$1" ]; then
|
||||
error "core.hooksPath should be $1, was $hooksPath"
|
||||
fi
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
{
|
||||
"extends": "@tsconfig/node16/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "lib",
|
||||
"declaration": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"removeComments": true
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user