testsAndMisc/.github/workflows/pre-commit.yml
Krzysztof kuhy Rudnicki f4535b6bfb
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Allow Pre-commit checks to be run manually
The run that added the hook-environment cache could not benefit from it:
its key is a hash of .pre-commit-config.yaml, which that same commit
changed, so it logged "Cache not found" and then saved. Six hook
environments were still built from scratch -- including pylint's, which
pulls a dozen extra packages -- and that is most of the 126s hook step.

Without a dispatch there is no way to re-run the job on an unchanged tree
to see the cached number, so the saving could only be asserted, not
measured. Now it can be measured.

Confirmed in the same log that the previous commit landed:
"pytest with coverage enforcement....Skipped".

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

128 lines
5.9 KiB
YAML

name: Pre-commit checks
on:
push:
branches: [main]
pull_request:
branches: [main]
# Manual runs: this job's cost is dominated by whether the pre-commit hook
# environments are cached, and without a dispatch there is no way to
# re-run it on an unchanged tree to confirm a cache actually helps.
workflow_dispatch: {}
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