Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 72c59b3

Browse files
committed
Support workdir - experiment
1 parent cc4a522 commit 72c59b3

6 files changed

Lines changed: 78 additions & 9 deletions

File tree

README.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,40 @@
1-
[![build status](https://github.com/pre-commit/pre-commit/actions/workflows/main.yml/badge.svg)](https://github.com/pre-commit/pre-commit/actions/workflows/main.yml)
2-
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/pre-commit/pre-commit/main.svg)](https://results.pre-commit.ci/latest/github/pre-commit/pre-commit/main)
1+
A fork of [pre-commit](https://pre-commit.com/) with experimental `workdir` setting support.
32

4-
## pre-commit
3+
Using this fork, you can configure some hooks to run "as-if" the selected subfolders were in repository root.
54

6-
A framework for managing and maintaining multi-language pre-commit hooks.
5+
E.g., with the following directory structure:
76

8-
For more information see: https://pre-commit.com/
7+
```
8+
.
9+
├── .git
10+
├── backend
11+
│   ├── pyproject.toml
12+
│   └── uv.lock
13+
├── frontend
14+
│   ├── package.json
15+
│   ├── pnpm-lock.yaml
16+
│   ├── biome.jsonc
17+
│   ├── src
18+
│   ├── tsconfig.json
19+
│   └── vite.config.js
20+
└── .pre-commig-config.yaml
21+
```
22+
23+
the following file will find backend-related and frontend-related files correctly:
24+
25+
```yaml
26+
repos:
27+
- repo: https://github.com/astral-sh/uv-pre-commit
28+
rev: 0.4.24
29+
hooks:
30+
- id: uv-lock
31+
workdir: backend
32+
33+
- repo: https://github.com/biomejs/pre-commit
34+
rev: "v0.5.0"
35+
hooks:
36+
- id: biome-check
37+
additional_dependencies: ["@biomejs/biome"]
38+
language_version: "20.18.0"
39+
workdir: frontend
40+
```

pre_commit/clientlib.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ def remove_default(self, dct: dict[str, Any]) -> None:
219219
cfgv.Optional('fail_fast', cfgv.check_bool, False),
220220
cfgv.Optional('pass_filenames', cfgv.check_bool, True),
221221
cfgv.Optional('description', cfgv.check_string, ''),
222+
cfgv.Optional('workdir', cfgv.check_string, ''),
222223
cfgv.Optional('language_version', cfgv.check_string, C.DEFAULT),
223224
cfgv.Optional('log_file', cfgv.check_string, ''),
224225
cfgv.Optional('require_serial', cfgv.check_bool, False),

pre_commit/commands/run.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
from collections.abc import Iterable
1414
from collections.abc import MutableMapping
1515
from collections.abc import Sequence
16+
from pathlib import Path
1617
from typing import Any
1718

1819
from identify.identify import tags_from_path
@@ -27,6 +28,7 @@
2728
from pre_commit.repository import install_hook_envs
2829
from pre_commit.staged_files_only import staged_files_only
2930
from pre_commit.store import Store
31+
from pre_commit.util import chdir_context
3032
from pre_commit.util import cmd_output_b
3133

3234

@@ -61,12 +63,21 @@ def filter_by_include_exclude(
6163
names: Iterable[str],
6264
include: str,
6365
exclude: str,
66+
workdir: str = '',
6467
) -> Generator[str]:
6568
include_re, exclude_re = re.compile(include), re.compile(exclude)
69+
if not workdir:
70+
return (
71+
filename for filename in names
72+
if include_re.search(filename)
73+
if not exclude_re.search(filename)
74+
)
6675
return (
67-
filename for filename in names
68-
if include_re.search(filename)
69-
if not exclude_re.search(filename)
76+
str(filename)
77+
for filename in map(Path, names)
78+
if filename.is_relative_to(workdir) and
79+
include_re.search(str(filename.relative_to(workdir))) and
80+
not exclude_re.search(str(filename.relative_to(workdir)))
7081
)
7182

7283

@@ -103,6 +114,7 @@ def filenames_for_hook(self, hook: Hook) -> Generator[str]:
103114
self.filenames,
104115
hook.files,
105116
hook.exclude,
117+
hook.workdir,
106118
),
107119
hook.types,
108120
hook.types_or,
@@ -189,7 +201,16 @@ def _run_single_hook(
189201
filenames = ()
190202
time_before = time.monotonic()
191203
language = languages[hook.language]
192-
with language.in_env(hook.prefix, hook.language_version):
204+
with contextlib.ExitStack() as stack:
205+
stack.enter_context(
206+
language.in_env(hook.prefix, hook.language_version),
207+
)
208+
if hook.workdir:
209+
stack.enter_context(chdir_context(hook.workdir))
210+
filenames = tuple(
211+
str(Path(filename).relative_to(hook.workdir))
212+
for filename in filenames
213+
)
193214
retcode, out = language.run_hook(
194215
hook.prefix,
195216
hook.entry,

pre_commit/hook.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class Hook(NamedTuple):
2929
fail_fast: bool
3030
pass_filenames: bool
3131
description: str
32+
workdir: str
3233
language_version: str
3334
log_file: str
3435
minimum_pre_commit_version: str

pre_commit/util.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,19 @@
1616
from pre_commit import parse_shebang
1717

1818

19+
if sys.version_info >= (3, 11, 0):
20+
from contextlib import chdir as chdir_context
21+
else:
22+
@contextlib.contextmanager # type: ignore[no-redef]
23+
def chdir_context(target: str) -> Generator[None]:
24+
cwd = os.getcwd()
25+
os.chdir(target)
26+
try:
27+
yield
28+
finally:
29+
os.chdir(cwd)
30+
31+
1932
def force_bytes(exc: Any) -> bytes:
2033
with contextlib.suppress(TypeError):
2134
return bytes(exc)

tests/repository_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,7 @@ def test_manifest_hooks(tempdir_factory, store):
426426
always_run=False,
427427
args=[],
428428
description='',
429+
workdir='',
429430
entry='bin/hook.sh',
430431
exclude='^$',
431432
exclude_types=[],

0 commit comments

Comments
 (0)