Skip to content

Commit fb4b620

Browse files
committed
fix(template): skip the hook install when core.hooksPath is set
pre-commit refuses to install hooks while git's core.hooksPath is set in any scope, and a failing _task makes copier delete the whole copy. The task now checks the setting first and skips with a hint on stderr, so a machine with a global hooks directory still gets a complete render; the hooks can be installed by hand once the setting is gone. A generation test pins the skip: the copy succeeds, no hook lands, the hint prints.
1 parent 861cfe1 commit fb4b620

4 files changed

Lines changed: 32 additions & 1 deletion

File tree

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Fixed
1111

12+
- The copy-time hook install no longer aborts (and rolls back) the whole copy when
13+
git's `core.hooksPath` is set; pre-commit refuses to install under it, so the task
14+
skips with a hint on stderr instead.
1215
- The generated `.gitignore` ignores pytest-cov's `.coverage` data file alongside
1316
`coverage.xml`.
1417

‎README.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ To update a downstream project after a new template release:
1616
copier update --trust
1717
```
1818

19+
Hooks are installed on copy unless git's `core.hooksPath` is set (pre-commit refuses to install under it); the copy then skips that step with a hint instead of failing.
20+
1921
## Toggles
2022

2123
All toggles default to `true` — every guardrail layer ships unless you opt out.

‎copier.yml‎

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,14 @@ _tasks:
3434
when: "{{ _copier_operation == 'copy' }}"
3535
- command: uv sync
3636
when: "{{ _copier_operation == 'copy' }}"
37-
- command: uv run pre-commit install --install-hooks
37+
# pre-commit refuses to install while core.hooksPath is set (any scope), and a failing
38+
# task rolls the whole copy back — skip with a hint on stderr instead.
39+
- command: |
40+
if git config --get core.hooksPath >/dev/null; then
41+
echo "core.hooksPath is set: skipping 'pre-commit install'. Run 'uv run pre-commit install --install-hooks' yourself once it is unset, or dispatch pre-commit from your global hooks." >&2
42+
else
43+
uv run pre-commit install --install-hooks
44+
fi
3845
when: "{{ _copier_operation == 'copy' and enable_precommit_install }}"
3946
4047
# _migrations run ONLY on update (never copy), version-gated to the release that

‎tests/test_generation.py‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,25 @@ def test_precommit_install_task_runs(render: RenderFn, tmp_path: Path) -> None:
311311
assert (dst / ".git" / "hooks" / "pre-push").exists()
312312

313313

314+
def test_precommit_install_skipped_when_hookspath_set(
315+
render: RenderFn, tmp_path: Path, capfd: pytest.CaptureFixture[str]
316+
) -> None:
317+
"""A global core.hooksPath must not abort the copy: the install task skips with a hint.
318+
319+
pre-commit refuses to install hooks while core.hooksPath is set (any scope), and a
320+
failing _task makes copier roll the whole copy back. The task guards on the setting
321+
instead, so the render succeeds hook-less and says why on stderr.
322+
"""
323+
hooks = tmp_path / "hooks"
324+
hooks.mkdir()
325+
with git_global_config(tmp_path / "gitconfig", hooks_path=hooks):
326+
project = render({**MINIMAL, "enable_precommit_install": True}, tmp_path / "out")
327+
assert (project / "uv.lock").is_file()
328+
assert not (project / ".git" / "hooks" / "pre-commit").exists()
329+
assert not (hooks / "pre-commit").exists()
330+
assert "core.hooksPath" in capfd.readouterr().err
331+
332+
314333
def test_property_layer(render: RenderFn, tmp_path: Path) -> None:
315334
on = render({**MINIMAL, "enable_property_tests": True}, tmp_path / "on")
316335
assert (on / "tests" / "property" / "test_example_property.py").is_file()

0 commit comments

Comments
 (0)