Skip to content
Merged
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
2 changes: 2 additions & 0 deletions changelog.d/+cooled-prek-run-env.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
`cooled-prek` builds hook environments under the seven-day cutoff, then runs the hooks with the caller's environment, so
a system hook that calls `uv run` resolves the project from its own lock instead of under the cutoff.
36 changes: 29 additions & 7 deletions tests/cooled_prek/test_cooled_prek.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,49 @@ def test_cooled_prek_action_passes_each_argument(tmp_path: Path, action_scripts:
]


def test_cooled_prek_isolates_hook_installs(tmp_path: Path) -> None:
@pytest.mark.parametrize(
("arguments", "install"),
[
pytest.param(["run", "--all-files"], ["install-hooks"], id="every-hook"),
pytest.param(["run", "mdformat", "--files", "CHANGELOG.md"], ["install-hooks", "mdformat"], id="named-hook"),
],
)
def test_cooled_prek_builds_hooks_cooled_and_runs_them_with_the_caller_environment(
tmp_path: Path, arguments: list[str], install: list[str]
) -> None:
fake = tmp_path / "prek"
fake.write_text('#!/bin/sh\nprintf "%s\\n" "$@" >args\nenv >env\n')
fake.write_text(
'#!/bin/sh\nn=$(ls calls 2>/dev/null | wc -l)\nmkdir -p calls/$n\nprintf "%s\\n" "$@" >calls/$n/args\nenv >calls/$n/env\n'
)
fake.chmod(0o755)
leaked = dict.fromkeys(("UV_INDEX_URL", "UV_EXCLUDE_NEWER_PACKAGE", "UV_SYSTEM_CERTS", "SSL_CERT_FILE"), "leak")

subprocess.run(
[str(Path(__file__).resolve().parents[2] / "tools/cooled_prek.sh"), "run", "--all-files"],
[str(Path(__file__).resolve().parents[2] / "tools/cooled_prek.sh"), *arguments],
cwd=tmp_path,
env=os.environ | leaked | {"PATH": f"{tmp_path}:{os.environ['PATH']}", "UV_EXCLUDE_NEWER": "1 day"},
check=True,
)

assert (tmp_path / "args").read_text().splitlines() == ["--config", ".pre-commit-config.yaml", "run", "--all-files"]
environment = dict(line.split("=", 1) for line in (tmp_path / "env").read_text().splitlines() if "=" in line)
assert {name: environment.get(name) for name in ("UV_EXCLUDE_NEWER", "UV_NO_CONFIG", "UV_NO_SOURCES")} == {
calls = [
(
(call / "args").read_text().splitlines(),
dict(line.split("=", 1) for line in (call / "env").read_text().splitlines() if "=" in line),
)
for call in sorted((tmp_path / "calls").iterdir(), key=lambda path: int(path.name))
]
(install_args, install_env), (run_args, run_env) = calls
assert install_args == ["--config", ".pre-commit-config.yaml", *install]
assert {name: install_env.get(name) for name in ("UV_EXCLUDE_NEWER", "UV_NO_CONFIG", "UV_NO_SOURCES")} == {
"UV_EXCLUDE_NEWER": "7 days",
"UV_NO_CONFIG": "1",
"UV_NO_SOURCES": "1",
}
assert not leaked.keys() & environment.keys()
assert not leaked.keys() & install_env.keys()
assert run_args == ["--config", ".pre-commit-config.yaml", *arguments]
assert run_env.get("UV_EXCLUDE_NEWER") == "1 day"
assert "UV_NO_CONFIG" not in run_env
assert leaked.items() <= run_env.items()


def test_cooled_prek_admits_nab_cutoff_exceptions(tmp_path: Path) -> None:
Expand Down
50 changes: 33 additions & 17 deletions tools/cooled_prek.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#!/usr/bin/env bash
# Hook environments build from PyPI under the seven-day cutoff, ignoring the repository's uv settings.
# Hook environments build from PyPI under the seven-day cutoff, ignoring the repository's uv settings. A run then
# executes the hooks with the caller's environment, so a system hook that calls uv resolves the project as usual.
set -euo pipefail
unset UV_CONFIG_FILE UV_INDEX UV_INDEX_URL UV_DEFAULT_INDEX UV_EXTRA_INDEX_URL
unset UV_FIND_LINKS UV_EXCLUDE_NEWER_PACKAGE
unset UV_INSECURE_HOST UV_SYSTEM_CERTS SSL_CERT_FILE SSL_CERT_DIR
export UV_EXCLUDE_NEWER="7 days" UV_NO_CONFIG=1 UV_NO_SOURCES=1
# uv pip, which prek calls, reads per-package cutoffs only from a config file, so the [tool.nab] exceptions the
# release lock already admits are written to one that holds nothing else.
exceptions=$(
python3 -I - <<'PY'
cooled() (
unset UV_CONFIG_FILE UV_INDEX UV_INDEX_URL UV_DEFAULT_INDEX UV_EXTRA_INDEX_URL
unset UV_FIND_LINKS UV_EXCLUDE_NEWER_PACKAGE
unset UV_INSECURE_HOST UV_SYSTEM_CERTS SSL_CERT_FILE SSL_CERT_DIR
export UV_EXCLUDE_NEWER="7 days" UV_NO_CONFIG=1 UV_NO_SOURCES=1
# uv pip, which prek calls, reads per-package cutoffs only from a config file, so the [tool.nab] exceptions the
# release lock already admits are written to one that holds nothing else.
exceptions=$(
python3 -I - <<'PY'
import pathlib
import tomllib

Expand All @@ -19,14 +21,28 @@ for name, settings in sorted(packages.items()):
value = cutoff.isoformat().replace("+00:00", "Z") if hasattr(cutoff, "isoformat") else str(cutoff)
print(f'"{name}" = "{value}"')
PY
)
if [[ -n "$exceptions" ]]; then
config=$(mktemp)
printf '[exclude-newer-package]\n%s\n' "$exceptions" >"$config"
export UV_CONFIG_FILE="$config"
echo "Running prek $* with hook environments built under the 7 days cutoff, repository uv settings ignored," \
"[tool.nab] cutoff exceptions admitted: $(tr '\n' ' ' <<<"$exceptions")"
else
echo "Running prek $* with hook environments built under the 7 days cutoff, repository uv settings ignored"
fi
exec prek --config .pre-commit-config.yaml "$@"
)
if [[ -n "$exceptions" ]]; then
config=$(mktemp)
printf '[exclude-newer-package]\n%s\n' "$exceptions" >"$config"
export UV_CONFIG_FILE="$config"
echo "Running prek $* with hook environments built under the 7 days cutoff, repository uv settings ignored," \
"[tool.nab] cutoff exceptions admitted: $(tr '\n' ' ' <<<"$exceptions")"
else
echo "Running prek $* with hook environments built under the 7 days cutoff, repository uv settings ignored"
if [[ "${1:-}" != run ]]; then
cooled "$@"
exit
fi
# The hook IDs a run names come before its first option; none means every hook.
hooks=()
for argument in "${@:2}"; do
[[ "$argument" == -* ]] && break
hooks+=("$argument")
done
cooled install-hooks ${hooks[@]+"${hooks[@]}"}
echo "Running prek $* with the caller's environment"
exec prek --config .pre-commit-config.yaml "$@"