From 201fc26d94f8100bfc0b8232072a5d39c39f5796 Mon Sep 17 00:00:00 2001 From: Blair Hamilton Date: Wed, 12 Aug 2026 18:58:51 -0400 Subject: [PATCH] fix(pre-commit-advisory): key hook-env cache on the resolved Python patch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hook-env cache key interpolated the `python-version` input, which callers give as a bare X.Y ("3.13"). The venvs go-pre-commit builds under ~/.cache/pre-commit/repo*/py_env-* bind the interpreter by full patch path (/opt/hostedtoolcache/Python/X.Y.Z), so a runner-image patch bump left the key matching exactly while restoring venvs whose interpreter was gone — every `language: python` hook then failed with [ERROR] hook execution error: fork/exec .../bin/: no such file or directory Key on steps.setup-python.outputs.python-version (a full X.Y.Z) instead, in both `key` and `restore-keys`. The patch must be in the prefix too: a restore-keys ending at X.Y just falls through to the next stale sibling, so purging one entry doesn't clear the wedge. Observed on trading#3262 (3.13.14 -> 3.13.15): all four pre-commit-hooks entries errored while the csharpier/gitleaks hooks passed, which reads as a diff problem rather than a cache one. Three cache entries shared the key and all three needed deleting before a re-run went green. Costs one cold rebuild per branch scope on a patch bump; a .pre-commit-config.yaml edit still reuses same-interpreter envs. --- .github/workflows/pre-commit-advisory.yml | 25 +++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pre-commit-advisory.yml b/.github/workflows/pre-commit-advisory.yml index 032fd30..b2b14fc 100644 --- a/.github/workflows/pre-commit-advisory.yml +++ b/.github/workflows/pre-commit-advisory.yml @@ -129,18 +129,39 @@ jobs: # Python is still needed for `language: python` hooks (ruff, …) — # go-pre-commit builds their venvs with the interpreter on PATH. - uses: actions/setup-python@v6 + id: setup-python with: python-version: ${{ inputs.python-version }} # Key prefix is go-pre-commit-* so envs built by the old Python # pre-commit are never restored into the Go implementation. + # + # Keyed on the *resolved* interpreter (steps.setup-python.outputs + # .python-version — a full X.Y.Z) rather than the `python-version` + # input, which callers give as a bare X.Y. The venvs go-pre-commit + # builds under ~/.cache/pre-commit/repo*/py_env-* hardlink the + # interpreter by full patch path (/opt/hostedtoolcache/Python/X.Y.Z), + # so when a runner-image refresh bumps the patch, a key pinned to + # X.Y still matches exactly and restores venvs whose interpreter no + # longer exists — every `language: python` hook then dies with + # `[ERROR] hook execution error: fork/exec .../bin/: no such + # file or directory`. Seen on trading#3262 (3.13.14 → 3.13.15), where + # all four pre-commit-hooks entries errored while the C#/gitleaks + # hooks passed, so it reads like a diff problem rather than a cache + # one. The patch must be in restore-keys too: a prefix ending at X.Y + # just falls through to the next stale sibling, so purging one entry + # (or even most) doesn't clear it. + # + # Cost: a patch bump now means one cold rebuild per branch scope + # instead of a silently broken restore. A .pre-commit-config.yaml + # edit still reuses same-interpreter envs via the prefix. - name: Cache pre-commit hook envs uses: actions/cache@v6 with: path: ~/.cache/pre-commit - key: go-pre-commit-${{ runner.os }}-py${{ inputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }} + key: go-pre-commit-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }} restore-keys: | - go-pre-commit-${{ runner.os }}-py${{ inputs.python-version }}- + go-pre-commit-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}- # go-pre-commit (github.com/blairham/go-pre-commit) — drop-in Go # reimplementation of pre-commit; no pip install. install-only: this