diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b2ec58e..874cd6f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -130,6 +130,7 @@ repos: # what else happened to be staged alongside. Both are a hard 10.00. - id: pylint name: pylint (source, 10.00) + alias: pylint-source exclude: ^screen_locker/tests/ args: - --rcfile=pyproject.toml @@ -189,6 +190,9 @@ repos: # The CI mirror, one step per hook so each call stays under the 60 s # action cap. Order matters: ci-venv (re)builds .ci-mirror-venv from # requirements.txt when it changed, so ci-pytest only reuses it. + # `pre-commit run --all-files` is split into four ci-precommit-* steps + # (each well under the cap); together they run every default-stage hook + # exactly once -- see run_precommit_group in scripts/ci_mirror.sh. - id: ci-venv name: CI mirror (clean requirements.txt venv) entry: bash scripts/ci_mirror.sh venv @@ -197,9 +201,33 @@ repos: always_run: true require_serial: true stages: [manual] - - id: ci-precommit - name: CI mirror (pre-commit --all-files) - entry: bash scripts/ci_mirror.sh precommit + - id: ci-precommit-pylint-source + name: "CI mirror (pre-commit --all-files: pylint source)" + entry: bash scripts/ci_mirror.sh precommit pylint-source + language: system + pass_filenames: false + always_run: true + require_serial: true + stages: [manual] + - id: ci-precommit-pylint-tests + name: "CI mirror (pre-commit --all-files: pylint tests)" + entry: bash scripts/ci_mirror.sh precommit pylint-tests + language: system + pass_filenames: false + always_run: true + require_serial: true + stages: [manual] + - id: ci-precommit-flutter-web + name: "CI mirror (pre-commit --all-files: flutter analyze + web lint/typecheck)" + entry: bash scripts/ci_mirror.sh precommit flutter-web + language: system + pass_filenames: false + always_run: true + require_serial: true + stages: [manual] + - id: ci-precommit-rest + name: "CI mirror (pre-commit --all-files: every other hook)" + entry: bash scripts/ci_mirror.sh precommit rest language: system pass_filenames: false always_run: true diff --git a/scripts/ci_mirror.sh b/scripts/ci_mirror.sh index 5c59375..4f93845 100755 --- a/scripts/ci_mirror.sh +++ b/scripts/ci_mirror.sh @@ -20,7 +20,7 @@ # per push under the resource cap, for work finish_auto.sh had just done. # # Usage: ci_mirror.sh [venv|precommit|pytest|all] [pytest args...] -# Each step is its own manual hook (ci-venv, ci-precommit, ci-pytest) so no +# Each step is its own manual hook (ci-venv, ci-precommit-*, ci-pytest) so no # single call outlives the 60 s action cap; `all` (the default) is the old # one-shot behaviour. pytest runs under xdist (-n auto --dist worksteal): # serial it was ~85 s, and pytest-cov merges the workers' coverage. @@ -79,9 +79,28 @@ ensure_venv() { log "clean venv ready" } -run_precommit_all_files() { - log "pre-commit run --all-files (mirrors the pre-commit workflow)" - pre-commit run --all-files || fail "pre-commit --all-files" +# Hooks that get their own group because they dominate the run time. `rest` +# skips them by id (SKIP matches the id, so `pylint` covers both pylint hooks). +readonly HEAVY_IDS="pylint,flutter-analyze,web-lint,web-typecheck" + +# One slice of `pre-commit run --all-files`, small enough for the 60 s action +# cap. The groups partition the default-stage hooks: pylint-source and +# pylint-tests (the aliases of the two `pylint` hooks), flutter-web, and rest +# (everything else). With no group, all of it runs in one go, as CI does. +run_precommit_group() { + local group="${1:-all}" + log "pre-commit run --all-files [$group] (mirrors the pre-commit workflow)" + case "$group" in + all) pre-commit run --all-files ;; + pylint-source | pylint-tests) pre-commit run "$group" --all-files ;; + flutter-web) + pre-commit run flutter-analyze --all-files \ + && pre-commit run web-lint --all-files \ + && pre-commit run web-typecheck --all-files + ;; + rest) SKIP="$HEAVY_IDS" pre-commit run --all-files ;; + *) fail "unknown pre-commit group '$group'" ;; + esac || fail "pre-commit --all-files [$group]" } run_pytest_clean_venv() { @@ -98,7 +117,7 @@ main() { esac case "$mode" in - precommit) run_precommit_all_files; return ;; + precommit) run_precommit_group "${1:-all}"; return ;; *) ;; esac require_file @@ -107,7 +126,7 @@ main() { venv) ;; pytest) run_pytest_clean_venv "$@" ;; *) - run_precommit_all_files + run_precommit_group all run_pytest_clean_venv "$@" log "all CI gates passed locally — safe to push" ;;