Skip to content

Commit bd80603

Browse files
authored
docs: document rs hooks lifecycle (#410)
1 parent f05b1db commit bd80603

13 files changed

Lines changed: 524 additions & 484 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ It also covers local development needs outside Rstack's scope, with Prettier for
2323
| [`rs check`](https://rstack.rs/guide/cli/check) | Run static checks, including lint and format |
2424
| [`rs lib`](https://rstack.rs/guide/cli/lib) | Build library |
2525
| [`rs doc`](https://rstack.rs/guide/cli/doc) | Serve or build docs |
26-
| [`rs setup`](https://rstack.rs/guide/cli/setup) | Install Git hooks |
26+
| [`rs hooks`](https://rstack.rs/guide/cli/hooks) | Manage Git hooks |
2727
| [`rs staged`](https://rstack.rs/guide/cli/staged) | Run tasks on staged Git files |
2828

2929
Rstack CLI fits into your existing project workflow. It does not replace your runtime, package manager, or task runner, such as [pnpm](https://github.com/pnpm/pnpm), [Bun](https://github.com/oven-sh/bun), [Turborepo](https://github.com/vercel/turborepo), [Nx](https://github.com/nrwl/nx), and [Nub](https://github.com/nubjs/nub).
@@ -63,7 +63,7 @@ bun add -d rstack
6363
"lib": "rs lib",
6464
"doc": "rs doc",
6565
"format": "rs fmt",
66-
"prepare": "rs setup"
66+
"prepare": "rs hooks"
6767
}
6868
}
6969
```

website/docs/en/guide/cli/_meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
"check",
99
"lint",
1010
"fmt",
11-
"setup",
11+
"hooks",
1212
"staged"
1313
]
Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
---
2+
description: 'Install, configure, troubleshoot, and safely remove repository-level Git hooks with Rstack CLI.'
3+
---
4+
5+
# hooks
6+
7+
import { PackageManagerTabs } from '@rspress/core/theme';
8+
9+
The `rs hooks` command installs, updates, and removes repository-level [Git hooks](https://git-scm.com/docs/githooks). Hook scripts run in the project that installed them.
10+
11+
## Usage
12+
13+
```bash
14+
rs hooks [options]
15+
rs hooks uninstall
16+
```
17+
18+
Run `rs hooks` without a subcommand to install or update hooks.
19+
20+
:::tip Alias
21+
`rs setup` is an alias for `rs hooks`.
22+
:::
23+
24+
## Install hooks
25+
26+
By default, hook scripts are stored in `.rstack/hooks` at the Git repository root. If the current directory is outside a Git repository, the command skips installation.
27+
28+
Add `rs hooks` to the `prepare` script of the project that owns the repository hooks:
29+
30+
```json title="package.json"
31+
{
32+
"scripts": {
33+
"prepare": "rs hooks"
34+
}
35+
}
36+
```
37+
38+
Run the script once to install the hooks:
39+
40+
<PackageManagerTabs
41+
command={{
42+
npm: 'npm run prepare',
43+
yarn: 'yarn run prepare',
44+
pnpm: 'pnpm run prepare',
45+
bun: 'bun run prepare',
46+
}}
47+
/>
48+
49+
For example, create a `pre-commit` hook that runs [`rs staged`](./staged):
50+
51+
```sh title=".rstack/hooks/pre-commit"
52+
rs staged
53+
```
54+
55+
You can safely run the installation more than once, and it does not load `rstack.config.*`. Run `rs hooks` again after cloning the repository or if the generated hook files are missing.
56+
57+
:::warning Existing Git hook managers
58+
59+
`rs hooks` updates the repository's [`core.hooksPath`](https://git-scm.com/docs/git-config#Documentation/git-config.txt-corehooksPath). If it detects another hooks path or existing Git hooks, it skips installation. Run `rs hooks --force` to let Rstack manage hooks instead.
60+
61+
:::
62+
63+
## Install options
64+
65+
### `--force`
66+
67+
`--force` (or `-f`) installs Rstack hooks even when another Git hook setup already exists:
68+
69+
```bash
70+
rs hooks --force
71+
```
72+
73+
Rstack keeps the existing hook files and points `core.hooksPath` to its generated hooks directory. While this setting is active, Git does not run hooks from the previous location.
74+
75+
`--force` cannot replace hooks owned by another Rstack project.
76+
77+
:::tip
78+
Run `rs hooks --force` only once. Use `rs hooks` without `--force` in the `prepare` script.
79+
:::
80+
81+
### `--hooks-dir`
82+
83+
Specifies a custom directory for hook scripts, relative to the Git repository root.
84+
85+
```bash
86+
rs hooks --hooks-dir config/git-hooks
87+
88+
# Quote paths that contain spaces
89+
rs hooks --hooks-dir "config/git hooks"
90+
```
91+
92+
When using a custom directory, add the full command to the `prepare` script of the project that manages hooks:
93+
94+
```json title="package.json"
95+
{
96+
"scripts": {
97+
"prepare": "rs hooks --hooks-dir config/git-hooks"
98+
}
99+
}
100+
```
101+
102+
> The path must not contain `..`. This prevents hook files from being created or overwritten outside the repository through a parent directory path.
103+
104+
### `--help`
105+
106+
`--help` (or `-h`) displays the command's usage, subcommands, and installation options.
107+
108+
```bash
109+
rs hooks --help
110+
```
111+
112+
## Uninstall hooks
113+
114+
Run the command from the project that installed the hooks:
115+
116+
```bash
117+
rs hooks uninstall
118+
```
119+
120+
Rstack automatically finds the active hooks and removes the `core.hooksPath` setting and the generated `_` directory. It does not delete project hook files such as `.rstack/hooks/pre-commit`. Hooks managed by another project or tool are left untouched.
121+
122+
Remove `rs hooks` from the `prepare` script if you do not want the hooks to be installed again.
123+
124+
If `--force` previously took over hooks in `.git/hooks`, those hooks become active again after uninstalling.
125+
126+
## Hook files
127+
128+
The default directory structure is:
129+
130+
```text
131+
.rstack/
132+
└── hooks/
133+
├── pre-commit # Repository hook script: edit and commit
134+
└── _/ # Generated by rs hooks; ignored by Git
135+
├── .gitignore
136+
├── .owner
137+
├── runner
138+
├── pre-commit
139+
├── commit-msg
140+
└── ...
141+
```
142+
143+
Files alongside `_` are repository hook scripts. The `_` directory contains generated files and is ignored by Git. `rs hooks` points `core.hooksPath` to `.rstack/hooks/_`.
144+
145+
## Supported hooks
146+
147+
Rstack CLI supports these client-side Git hooks:
148+
149+
- `pre-commit`
150+
- `pre-merge-commit`
151+
- `prepare-commit-msg`
152+
- `commit-msg`
153+
- `post-commit`
154+
- `applypatch-msg`
155+
- `pre-applypatch`
156+
- `post-applypatch`
157+
- `pre-rebase`
158+
- `post-rewrite`
159+
- `post-checkout`
160+
- `post-merge`
161+
- `pre-push`
162+
- `pre-auto-gc`
163+
164+
Create a file with the matching name alongside the `_` directory.
165+
166+
## Hook runtime
167+
168+
Rstack CLI runs hook scripts with POSIX `sh -e`. It forwards Git's arguments and standard input, then returns the hook's exit code. Before running a hook, Rstack changes to the project that installed the hooks and prepends that project's `node_modules/.bin` to `PATH`.
169+
170+
### Disable and debug
171+
172+
Set `RSTACK_HOOKS=0` to skip installation or hook execution:
173+
174+
```bash
175+
RSTACK_HOOKS=0 git commit -m "Skip hooks"
176+
```
177+
178+
Set `RSTACK_HOOKS=2` to trace the Rstack CLI hook runtime, including how it invokes the hook script and handles its exit code; to trace commands inside the hook script, add `set -x` to the script:
179+
180+
```bash
181+
RSTACK_HOOKS=2 git commit -m "Trace hooks"
182+
```
183+
184+
### Configure the hook environment
185+
186+
Before running a hook script, Rstack CLI loads this optional POSIX shell file:
187+
188+
```text
189+
${XDG_CONFIG_HOME:-$HOME/.config}/rstack/hooks-init.sh
190+
```
191+
192+
Use it to initialize a Node.js version manager, update `PATH`, or set `RSTACK_HOOKS=0` for the current user.
193+
194+
## Ownership safety
195+
196+
Each generated hooks directory records its owning project. Only that project should include `rs hooks` in its `prepare` script. Rstack skips installation from any other project, even with `--force`, and refuses to remove hooks owned by another project.
197+
198+
To transfer ownership:
199+
200+
1. Remove `rs hooks` from the previous owner's `prepare` script.
201+
2. Run `rs hooks uninstall` from the previous owner.
202+
3. Add `rs hooks` to the new owner's `prepare` script and run it once.
203+
204+
## Monorepo
205+
206+
In a monorepo, the project that provides Rstack CLI may live in a subdirectory such as `frontend/`. Running `rs hooks` there still installs hooks at the Git repository root:
207+
208+
```text
209+
repo/.rstack/hooks/
210+
repo/.rstack/hooks/_/
211+
core.hooksPath=.rstack/hooks/_
212+
```
213+
214+
Rstack records `frontend` as the owning project. The hook scripts stay at the repository root but run from `frontend`, allowing them to use its configuration and dependencies without an explicit `cd`:
215+
216+
```sh title=".rstack/hooks/pre-commit"
217+
rs staged
218+
```
219+
220+
## Worktree behavior
221+
222+
Rstack preserves the Git configuration scope of the active `core.hooksPath`. If a linked worktree uses a worktree-scoped hooks path, `rs hooks --force` replaces it in that scope instead of writing a local setting that Git would ignore.
223+
224+
`rs hooks uninstall` also removes the setting from that scope. Removing a worktree-scoped installation affects only the current worktree and leaves hooks in other linked checkouts unchanged. A local `core.hooksPath` setting is shared across linked checkouts, so changing or removing it affects every checkout that does not override it.
225+
226+
## Troubleshooting
227+
228+
### Hook does not run
229+
230+
- Check that the hook script has a [supported name](#supported-hooks) and is next to the `_` directory.
231+
- Run `git config --show-scope --get core.hooksPath` and verify the effective scope and path.
232+
- Rerun `rs hooks` to restore generated files and executable permissions.
233+
- Check that `RSTACK_HOOKS` is not set to `0` in the environment or initialization file.
234+
- If another hook setup is detected, run `rs hooks --force`.
235+
- If another project is reported as the hooks owner, follow the steps in [Ownership safety](#ownership-safety).
236+
237+
Hook scripts do not need to be executable because Rstack CLI runs them with `sh`.
238+
239+
### Command not found
240+
241+
For exit code 127, Rstack CLI prints the effective `PATH`. If a GUI Git client cannot find Node.js or the package manager, initialize them in `hooks-init.sh`.
242+
243+
### Windows and Yarn
244+
245+
On Windows, hooks run in the POSIX shell included with [Git for Windows](https://gitforwindows.org/). Use LF line endings and `/` path separators in hooks.
246+
247+
[Yarn PnP](https://yarnpkg.com/features/pnp) does not provide `node_modules/.bin`. Run tools through a Yarn script, such as `yarn run test`, and make Node.js and Yarn available through `hooks-init.sh` when needed.

0 commit comments

Comments
 (0)