diff --git a/README.md b/README.md index b53ae9f..a6d3607 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,11 @@ Every shared `prek` command runs through `tools/cooled_prek.sh`, which the `cool passes `--config .pre-commit-config.yaml`. Without it, prek prefers a root `prek.toml` and also runs nested configurations, which no guard checks. +Hook environments install under the same seven-day upload cutoff, with the repository's uv settings ignored. A package +with a `packages..uploaded-prior-to` exception in `[tool.nab]` is admitted up to that date in hook environments +too, so a hook dependency pinned to a release newer than the cutoff, such as `sphinx` for `docstrfmt`, needs the same +temporary exception the release lock uses. Delete the exception once the release is a week old. + ## Start with a caller The [release preparation workflow](.github/workflows/prepare-release.yaml) in this repository calls the reusable diff --git a/changelog.d/+cooled-prek-exceptions.change.md b/changelog.d/+cooled-prek-exceptions.change.md new file mode 100644 index 0000000..e4215ff --- /dev/null +++ b/changelog.d/+cooled-prek-exceptions.change.md @@ -0,0 +1,3 @@ +Admit the `[tool.nab]` per-package `uploaded-prior-to` exceptions in `cooled-prek` hook environments, so a hook +dependency pinned past the seven-day cutoff installs the same way as the release lock. Before, prepare failed on such a +pin. diff --git a/tests/cooled_prek/test_cooled_prek.py b/tests/cooled_prek/test_cooled_prek.py index e563f3e..0a83279 100644 --- a/tests/cooled_prek/test_cooled_prek.py +++ b/tests/cooled_prek/test_cooled_prek.py @@ -89,3 +89,42 @@ def test_cooled_prek_isolates_hook_installs(tmp_path: Path) -> None: "UV_NO_SOURCES": "1", } assert not leaked.keys() & environment.keys() + + +def test_cooled_prek_admits_nab_cutoff_exceptions(tmp_path: Path) -> None: + fake = tmp_path / "prek" + fake.write_text('#!/bin/sh\ncat "$UV_CONFIG_FILE" >config\n') + fake.chmod(0o755) + (tmp_path / "pyproject.toml").write_text( + '[tool.nab]\nuploaded-prior-to = "P7D"\npackages.sphinx.uploaded-prior-to = 2026-10-21T00:05:13Z\n' + 'packages.release-creator.uploaded-prior-to = "2026-10-26T18:24:46Z"\npackages.uv.decision-order = "stable"\n' + ) + + subprocess.run( + [str(Path(__file__).resolve().parents[2] / "tools/cooled_prek.sh"), "install-hooks"], + cwd=tmp_path, + env=os.environ | {"PATH": f"{tmp_path}:{os.environ['PATH']}"}, + check=True, + ) + + assert (tmp_path / "config").read_text().splitlines() == [ + "[exclude-newer-package]", + '"release-creator" = "2026-10-26T18:24:46Z"', + '"sphinx" = "2026-10-21T00:05:13Z"', + ] + + +def test_cooled_prek_without_exceptions_sets_no_config_file(tmp_path: Path) -> None: + fake = tmp_path / "prek" + fake.write_text('#!/bin/sh\nprintf "%s" "${UV_CONFIG_FILE-unset}" >config\n') + fake.chmod(0o755) + (tmp_path / "pyproject.toml").write_text('[tool.nab]\nuploaded-prior-to = "P7D"\n') + + subprocess.run( + [str(Path(__file__).resolve().parents[2] / "tools/cooled_prek.sh"), "install-hooks"], + cwd=tmp_path, + env=os.environ | {"PATH": f"{tmp_path}:{os.environ['PATH']}", "UV_CONFIG_FILE": "/leak/uv.toml"}, + check=True, + ) + + assert (tmp_path / "config").read_text() == "unset" diff --git a/tools/cooled_prek.sh b/tools/cooled_prek.sh index 961791b..0627d7b 100755 --- a/tools/cooled_prek.sh +++ b/tools/cooled_prek.sh @@ -5,5 +5,28 @@ 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 -echo "Running prek $* with hook environments built under the 7 days cutoff, repository uv settings ignored" +# 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 + +path = pathlib.Path("pyproject.toml") +packages = tomllib.loads(path.read_text()).get("tool", {}).get("nab", {}).get("packages", {}) if path.is_file() else {} +for name, settings in sorted(packages.items()): + if (cutoff := settings.get("uploaded-prior-to")) is not None: + 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 "$@"