screen-locker/scripts/ci_mirror.sh
Krzysztof kuhy Rudnicki 1475ab913b
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (flutter, flutter-analyze, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint,flutter-analyze) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / lock-popup-safety (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (0) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (1) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (2) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (3) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (4) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (5) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (6) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (7) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (8) (push) Waiting to run
Tests / flutter test (shard ${{ matrix.shard }}) (9) (push) Waiting to run
Tests / flutter-coverage (push) Blocked by required conditions
Tests / web-coverage (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shard ci-precommit into four gate steps
Each slice runs under 30 s; together they run all 35 default-stage
hooks once. CI (pre-commit.yml) is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WzxdWZ9CKZsNxtD5Jr9bYD
2026-11-10 17:45:58 +01:00

137 lines
5.0 KiB
Bash
Executable File

#!/bin/bash
# ============================================================================
# ci_mirror.sh — run the exact CI gates locally before a push can leave.
#
# Why: the installed pre-commit hook only checks *staged* files, and the local
# dev environment already has every dependency installed. CI does neither — it
# runs `pre-commit run --all-files` and installs a fresh environment from
# requirements.txt. That gap is why green-locally repos went red in CI
# (missing runtime deps, --all-files lint debt, order-dependent test flakes).
#
# This script reproduces both CI workflows on the developer machine:
# 1. a venv built ONLY from requirements.txt (mirrors the Tests workflow's
# `pip install -r requirements.txt`), rebuilt only when requirements.txt
# changes (hash-gated so day-to-day pushes stay fast);
# 2. `pre-commit run --all-files` (mirrors the pre-commit workflow);
# 3. `python -m pytest` inside that clean venv (mirrors Tests).
#
# Runs on demand (`scripts/check_gate_stamp.sh full`, pre-commit's manual
# stage) and in CI. It left the pre-push path on 2026-09-20: 10+ minutes
# 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
# 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.
# ============================================================================
set -euo pipefail
# Requirements file may be overridden (e.g. testsAndMisc uses meta/…).
REQUIREMENTS_FILE="${REQUIREMENTS_FILE:-requirements.txt}"
readonly REQUIREMENTS_FILE
ROOT="$(git rev-parse --show-toplevel)"
readonly ROOT
cd "$ROOT"
readonly VENV_DIR="$ROOT/.ci-mirror-venv"
readonly HASH_FILE="$VENV_DIR/.requirements.sha256"
readonly REQ_PATH="$ROOT/$REQUIREMENTS_FILE"
log() { printf 'ci-mirror: %s\n' "$1" >&2; }
fail() {
log "FAILED — $1"
log "CI would be red. Fix the above before pushing."
exit 1
}
require_file() {
if [[ ! -f "$REQ_PATH" ]]; then
fail "requirements file not found: $REQ_PATH"
fi
}
# Rebuild the venv only when requirements.txt changed since the last build.
ensure_venv() {
local current stored
current="$(sha256sum "$REQ_PATH" | cut -d' ' -f1)"
stored=""
if [[ -f "$HASH_FILE" ]]; then
stored="$(cat "$HASH_FILE")"
fi
if [[ -x "$VENV_DIR/bin/python" && "$current" == "$stored" ]]; then
log "venv up to date (requirements.txt unchanged)"
return
fi
log "requirements.txt changed — rebuilding clean venv (mirrors CI install)"
rm -rf "$VENV_DIR"
python3 -m venv "$VENV_DIR"
"$VENV_DIR/bin/python" -m pip install --quiet --upgrade pip \
|| fail "pip self-upgrade in the clean venv"
"$VENV_DIR/bin/python" -m pip install --quiet -r "$REQ_PATH" \
|| fail "pip install -r $REQUIREMENTS_FILE (a dep may be undeclared)"
printf '%s' "$current" > "$HASH_FILE"
log "clean venv ready"
}
# 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() {
log "pytest in the clean venv (mirrors the Tests workflow)"
"$VENV_DIR/bin/python" -m pytest -n auto --dist worksteal "$@" \
|| fail "pytest (clean requirements.txt venv)"
}
main() {
local mode="all"
case "${1:-}" in
venv | precommit | pytest | all) mode="$1"; shift ;;
*) ;;
esac
case "$mode" in
precommit) run_precommit_group "${1:-all}"; return ;;
*) ;;
esac
require_file
ensure_venv
case "$mode" in
venv) ;;
pytest) run_pytest_clean_venv "$@" ;;
*)
run_precommit_group all
run_pytest_clean_venv "$@"
log "all CI gates passed locally — safe to push"
;;
esac
}
main "$@"