Install uv without an action so the matrix jobs stop racing
CI / shell (shellcheck + syntax) (push) Failing after 1s
CI / python 3.11 (push) Failing after 1s
CI / python 3.12 (push) Failing after 1s
CI / python 3.13 (push) Failing after 0s

act caches each action as one shared git clone under /root/.cache/act/<hash>
and re-pulls it per job. The three python matrix jobs start within the same
second on the self-hosted runner, race on that directory, and the loser dies
with "lstat /root/.cache/act/<hash>/.npmrc: no such file or directory" before
any test runs — a red main with zero suite output and a different victim each
push (3.12, then 3.11).

The action was only fetching a binary; the matrix interpreter is selected per
command by uvx --python. A run: step has no action-cache entry and cannot
race, and keeps the jobs parallel — serialising the matrix would cost 3x the
wall clock and still leave actions/checkout shared across four jobs. The uv
version is pinned under the same rule as ruff.

The accompanying test parses uses: directives rather than the raw text, so the
comment can still name the action it avoids, and uses re instead of PyYAML
because CI runs uvx pytest, whose environment holds pytest and nothing else.
This commit is contained in:
2026-08-26 05:28:26 +00:00
parent 5f8d42f2cf
commit 02ba653127
3 changed files with 81 additions and 1 deletions
+18
View File
@@ -39,6 +39,24 @@ worse than no alert, because one day it carries a security fix.
### Fixed
- **CI turned `main` red on two of three pushes without running a single test.**
`act`, the engine behind the self-hosted Gitea runner, caches each *action* as
one shared git clone under `/root/.cache/act/<hash>` and re-pulls it per job.
The three matrix jobs start within the same second on one runner, so they race
on that directory and whichever loses dies with `lstat
/root/.cache/act/<hash>/.npmrc: no such file or directory` — before any suite
output exists, with a different victim each push (3.12 on one, 3.11 on the
next). A red gate that is usually noise is worse than no gate, because the one
time it means something nobody looks.
`uv` is now installed by a plain `run:` step instead of `astral-sh/setup-uv`.
A `run:` step has no action-cache entry and cannot race, and the action was
only ever fetching a binary — the matrix interpreter is chosen per command by
`uvx --python`, not by the action. Serialising the matrix was the alternative;
it costs 3x the wall clock and still leaves `actions/checkout` shared across
the four jobs. The uv version is pinned under the same rule as ruff: an
unpinned tool lets an upstream release turn `main` red with no change here.
- **The patch survived being applied and then silently stopped existing, because
something else remounted `/usr` four seconds later.** On a box running
TrueNAS 25.10.6 the boot of 2026-08-19 went: 16:41:56 `apply.sh` mounts its