testsAndMisc/.github/workflows/pre-commit.yml
Krzysztof kuhy Rudnicki 83c8c9deb3 Stop running the Python suite twice per push in CI
Per-step timings showed the real cost was not the install I had just
cached:

  Pre-commit checks   Run pre-commit hooks   152s
                      Install dependencies    55s

The log explains it -- "pytest with coverage enforcement....Passed"
inside the Pre-commit checks job. `pre-commit run --all-files` fires the
pytest-coverage hook, which runs every python_pkg suite with
--cov-fail-under=100: precisely what the Python tests workflow had
already done on the same commit. Two full 2174-test runs per push.

SKIP=pytest-coverage in that job only. It is the one hook skipped, and it
is skipped for being DUPLICATED rather than for being slow -- noted in
the workflow so it gets dropped if Python tests ever goes away.

Also checked and deliberately left alone:
session-autopsy-hook-mutations costs ~50s locally, but self-skips when
~/.claude/hooks is absent, which is always true on a runner. It was never
a CI cost.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
2026-08-23 16:30:27 +02:00

124 lines
5.7 KiB
YAML

name: Pre-commit checks
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
# See the note in python-tests.yml: caches the pip download cache,
# keyed on the real meta/ path rather than the root symlink.
cache: pip
cache-dependency-path: meta/requirements.txt
# zsh backs the zsh-syntax hook. apt does not let a patch release be
# selected, so this is pinned only to what the runner image provides;
# the version is echoed so a jump shows up in the log rather than as a
# mystery parse difference. Local machines compare on major.minor only.
- name: Install system dependencies
run: |
set -euo pipefail
sudo apt-get update && sudo apt-get install -y zsh librsvg2-bin
zsh --version
rsvg-convert --version
# Pin shellcheck instead of inheriting whatever ubuntu-latest ships.
#
# The runner image carried 0.9.0 while developer machines have 0.11.0
# from pacman, and the shellcheck hook is `language: system` -- so it
# used whichever binary happened to be on PATH. The two versions
# disagree (0.11.0 demoted SC2002 and several SC2015/SC2119 cases as
# noise), which meant a commit could pass every local check and still
# fail CI on findings the developer could not reproduce. That is exactly
# what kept this workflow red from b83ce5d onward.
#
# Keep SHELLCHECK_VERSION in step with the version developers install;
# the verification below fails loudly rather than silently drifting.
- name: Install shellcheck (pinned)
env:
SHELLCHECK_VERSION: v0.11.0
SHELLCHECK_SHA256: 8c3be12b05d5c177a04c29e3c78ce89ac86f1595681cab149b65b97c4e227198
run: |
set -euo pipefail
tarball="shellcheck-${SHELLCHECK_VERSION}.linux.x86_64.tar.xz"
curl -fsSL -o "$tarball" \
"https://github.com/koalaman/shellcheck/releases/download/${SHELLCHECK_VERSION}/${tarball}"
echo "${SHELLCHECK_SHA256} ${tarball}" | sha256sum --check --strict
tar -xJf "$tarball"
sudo install -m 0755 "shellcheck-${SHELLCHECK_VERSION}/shellcheck" /usr/local/bin/shellcheck
# Fail here, not 40 hooks later, if PATH still resolves to another copy.
installed="$(shellcheck --version | awk '/^version:/ { print $2 }')"
expected="${SHELLCHECK_VERSION#v}"
if [ "$installed" != "$expected" ]; then
echo "::error::shellcheck on PATH is ${installed}, expected ${expected}" >&2
command -v -a shellcheck >&2
exit 1
fi
echo "shellcheck ${installed} pinned and on PATH"
# prettier and shfmt do NOT run in this job -- prettier is a pre-push
# hook and shfmt runs from the local pre-commit git hook. They are
# pinned here anyway so this file stays the single place that records
# the expected version of every `language: system` tool, and so a future
# workflow that does run them inherits the pin instead of re-deriving
# it. Their real divergence risk is between developers: both REWRITE
# files, so two versions reformat the repo back and forth.
- name: Install prettier and shfmt (pinned)
env:
PRETTIER_VERSION: 3.9.5
SHFMT_VERSION: v3.13.1
SHFMT_SHA256: fb096c5d1ac6beabbdbaa2874d025badb03ee07929f0c9ff67563ce8c75398b1
run: |
set -euo pipefail
npm install --global --no-fund --no-audit "prettier@${PRETTIER_VERSION}"
curl -fsSL -o shfmt \
"https://github.com/mvdan/sh/releases/download/${SHFMT_VERSION}/shfmt_${SHFMT_VERSION}_linux_amd64"
echo "${SHFMT_SHA256} shfmt" | sha256sum --check --strict
sudo install -m 0755 shfmt /usr/local/bin/shfmt
# Assert what PATH actually resolves to, not what was just written:
# a copy earlier on PATH would otherwise win silently.
prettier_installed="$(prettier --version)"
shfmt_installed="$(shfmt --version)"
if [ "$prettier_installed" != "$PRETTIER_VERSION" ]; then
echo "::error::prettier on PATH is ${prettier_installed}, expected ${PRETTIER_VERSION}" >&2
exit 1
fi
if [ "$shfmt_installed" != "$SHFMT_VERSION" ]; then
echo "::error::shfmt on PATH is ${shfmt_installed}, expected ${SHFMT_VERSION}" >&2
exit 1
fi
echo "prettier ${prettier_installed} and shfmt ${shfmt_installed} pinned and on PATH"
# pre-commit builds a virtualenv per hook repo on first use, which is
# slower than the pip install itself. Keyed on the hook definitions, so
# it invalidates exactly when a hook's repo or rev changes.
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit-${{ runner.os }}-py3.12-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pre-commit
- name: Run pre-commit hooks
# SKIP=pytest-coverage: the Python tests workflow already runs the
# whole suite with the same 100% coverage gate, so running it again
# here doubled the cost of this job for no extra signal. It is the
# only hook skipped, and it is skipped because it is DUPLICATED, not
# because it is slow -- if that workflow is ever removed, drop this.
run: SKIP=pytest-coverage pre-commit run --all-files --show-diff-on-failure