mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 20:07:16 +01:00
The single ci-mirror manual hook took ~100 s (pre-commit --all-files plus a serial clean-venv pytest, ~85 s), past the 60 s per-action cap. ci_mirror.sh now takes a subcommand (venv|precommit|pytest|all; default all = the old one-shot behaviour) and each step is its own manual hook, ordered so the venv rebuild runs first in its own call. The clean-venv pytest runs -n auto --dist worksteal; branch coverage stays at 100% (pytest-cov merges the workers). Measured (timeout 58): ci-venv 27.0 s cold / 0.1 s warm, ci-precommit 32.6 s, ci-pytest 12.6 s, flutter-test-coverage 46.5 s, web-coverage 1.7 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp
118 lines
4.0 KiB
Bash
Executable File
118 lines
4.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"
|
|
}
|
|
|
|
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"
|
|
}
|
|
|
|
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_all_files; return ;;
|
|
*) ;;
|
|
esac
|
|
require_file
|
|
ensure_venv
|
|
case "$mode" in
|
|
venv) ;;
|
|
pytest) run_pytest_clean_venv "$@" ;;
|
|
*)
|
|
run_precommit_all_files
|
|
run_pytest_clean_venv "$@"
|
|
log "all CI gates passed locally — safe to push"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|