Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .pre-commit-hooks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,13 @@
pass_filenames: false
additional_dependencies: []
minimum_pre_commit_version: "2.9.2"
- id: uv-update-requirements
name: uv-update-requirements
description: "Run 'uv export' and stage the output file so the commit is not interrupted"
entry: uv-update-requirements
language: python
files: ^(uv\.lock|pyproject\.toml|uv\.toml)$
args: ["--frozen", "--output-file=requirements.txt", "--quiet"]
pass_filenames: false
additional_dependencies: []
minimum_pre_commit_version: "2.9.2"
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,34 @@ include it as an additional sync dependency:
additional_dependencies: [keyring]
```

To export `uv.lock` to `requirements.txt` **without interrupting the commit**:

```yaml
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
rev: 0.11.29
hooks:
- id: uv-update-requirements
```

Unlike `uv-export`, this hook automatically stages the generated `requirements.txt`
so that pre-commit does not abort the commit with a *"files were modified by this hook"*
error requiring a second `git commit`.

To export to a custom file, override the `args`:

```yaml
- repo: https://github.com/astral-sh/uv-pre-commit
# uv version.
rev: 0.11.29
hooks:
- id: uv-update-requirements
args: ["--frozen", "--output-file=requirements-prod.txt", "--quiet"]
```

## License


uv-pre-commit is licensed under either of

- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <https://www.apache.org/licenses/LICENSE-2.0>)
Expand Down
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ dev = [
"packaging~=23.1",
"urllib3>=2.0.5,<3.0.0",
]

[project.scripts]
uv-update-requirements = "uv_update_requirements:main"
50 changes: 50 additions & 0 deletions uv_update_requirements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
"""Hook: regenerate requirements.txt (or a custom output file) via `uv export`
and automatically stage it so the commit is not interrupted by pre-commit's
"files were modified by this hook" check.

Usage (via .pre-commit-config.yaml):
- id: uv-update-requirements
args: ["--frozen", "--output-file=requirements.txt", "--quiet"]
"""
from __future__ import annotations

import subprocess
import sys

def _parse_output_file(args: list[str]) -> str:
"""Extract the --output-file value from the args list."""
for i, arg in enumerate(args):
if arg.startswith("--output-file="):
return arg.split("=", 1)[1]
if arg == "--output-file" and i + 1 < len(args):
return args[i + 1]
return "requirements.txt"

def main(argv: list[str] | None = None) -> int:
args = argv if argv is not None else sys.argv[1:]

# Fall back to sensible defaults when no args are supplied.
if not args:
args = ["--frozen", "--output-file=requirements.txt", "--quiet"]

# Run `uv export` with the provided arguments.
export_result = subprocess.run(["uv", "export", *args])
if export_result.returncode != 0:
return export_result.returncode

# Stage the generated file so pre-commit does not abort the commit.
output_file = _parse_output_file(args)
stage_result = subprocess.run(["git", "add", output_file])
if stage_result.returncode != 0:
print(
f"uv-update-requirements: warning: could not stage '{output_file}' "
f"(git add exited with {stage_result.returncode})",
file=sys.stderr,
)
return stage_result.returncode

return 0

if __name__ == "__main__":
sys.exit(main())