repo contract: CLAUDE.md commands, test_changed, CI gate
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 }}) (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) (push) Waiting to run
Tests / test (push) Waiting to run
Tests / web-coverage (push) Waiting to run
repo-contract / repo contract (push) Waiting to run

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Krzysztof Rudnicki 2026-10-04 17:18:00 +02:00
parent b0dfdd70c2
commit 2be88453d5
5 changed files with 117 additions and 0 deletions

25
.github/workflows/repo-contract.yml vendored Normal file
View File

@ -0,0 +1,25 @@
name: repo-contract
# The local pre-commit hook can be skipped (a fresh clone has no hooks), so the
# contract is enforced on push too. Mirrors md-naming.yml.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
repo-contract:
name: repo contract
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check out the shared gate
uses: actions/checkout@v4
with:
repository: kuhyx/utils
path: .utils
- name: Enforce the repo contract
run: bash .utils/scripts/check_repo_contract.sh

View File

@ -249,3 +249,10 @@ repos:
pass_filenames: false pass_filenames: false
always_run: true always_run: true
stages: [pre-push] stages: [pre-push]
- id: repo-contract
name: repo contract (CLAUDE.md Commands + test_changed)
entry: scripts/check_repo_contract.sh
language: system
pass_filenames: false
files: ^(CLAUDE\.md|AGENTS\.md|scripts/test_changed\.sh)$

View File

@ -14,3 +14,12 @@ After running tests fix all coverage gaps and issues, do not ignore unless speci
- cmd_done completion path can pick next game from snapshot-only hours; keep it aligned with HLTB cache/refresh before pick_next_game to avoid prologue-derived stale times (e.g., A Space 0.56h while cache has ~20h). - cmd_done completion path can pick next game from snapshot-only hours; keep it aligned with HLTB cache/refresh before pick_next_game to avoid prologue-derived stale times (e.g., A Space 0.56h while cache has ~20h).
- HLTB renames games (e.g., "Needy Streamer Overload" → "NEEDY GIRL OVERDOSE"). The old name lives in `game_alias`. Both `game_name` and `game_alias` must be checked when matching — fixed in `_pick_best_hltb_entry`. - HLTB renames games (e.g., "Needy Streamer Overload" → "NEEDY GIRL OVERDOSE"). The old name lives in `game_alias`. Both `game_name` and `game_alias` must be checked when matching — fixed in `_pick_best_hltb_entry`.
- **ALWAYS clear HLTB cache and re-run `run.sh` after changing the HLTB picking/matching algorithm.** Delete `~/.config/steam_backlog_enforcer/hltb_cache.json` (entire file, not just one entry) so all games get re-matched with the new logic. Then run `./run.sh` to verify correct results. Stale cache entries from the old algorithm will persist and hide bugs otherwise. - **ALWAYS clear HLTB cache and re-run `run.sh` after changing the HLTB picking/matching algorithm.** Delete `~/.config/steam_backlog_enforcer/hltb_cache.json` (entire file, not just one entry) so all games get re-matched with the new logic. Then run `./run.sh` to verify correct results. Stale cache entries from the old algorithm will persist and hide bugs otherwise.
## Commands
- run: `./run.sh`
- test: `python3 -m pytest -q`
- test-changed: `scripts/test_changed.sh`
- lint: `pre-commit run ruff --all-files`
- coverage: `python3 -m pytest -q`
- coverage-gaps: `coverage-gaps coverage.lcov`

26
scripts/check_repo_contract.sh Executable file
View File

@ -0,0 +1,26 @@
#!/bin/bash
# ============================================================================
# Fail if this repo breaks the shared repo contract.
#
# Thin delegate to the shared gate in ~/src/utils, which owns the contract
# text. Copy this file to scripts/check_repo_contract.sh in a repo; never copy
# the shared gate itself, or the shim would exec itself forever.
# ============================================================================
set -euo pipefail
readonly SHARED_GATE="${UTILS_ROOT:-$HOME/src/utils}/scripts/check_repo_contract.sh"
main() {
if [[ ! -x "$SHARED_GATE" ]]; then
echo "Error: shared repo-contract gate not found at $SHARED_GATE" >&2
echo " Clone github.com/kuhyx/utils to ~/src/utils, or set" >&2
echo " UTILS_ROOT to where it lives." >&2
exit 1
fi
exec bash "$SHARED_GATE" "$@"
}
main "$@"

50
scripts/test_changed.sh Executable file
View File

@ -0,0 +1,50 @@
#!/bin/bash
# ============================================================================
# Run only the tests related to files changed vs HEAD (staged, unstaged and
# untracked). Quiet: failures plus a one-line summary. Falls back to the full
# suite when a change cannot be mapped to tests. Generated by repo_contract.
# ============================================================================
set -euo pipefail
cd "$(git rev-parse --show-toplevel)"
CHANGED=()
while IFS= read -r f; do
[[ -n "$f" && -e "$f" ]] && CHANGED+=("$f")
done < <({ git diff --name-only HEAD 2>/dev/null || true; git ls-files --others --exclude-standard; } | sort -u)
pytest_run() {
if [[ -f uv.lock ]]; then uv run pytest "$@"
elif [[ -f poetry.lock ]]; then poetry run pytest "$@"
else python3 -m pytest "$@"; fi
}
if grep -qs testmon pyproject.toml requirements*.txt; then
pytest_run --testmon -q --tb=short; exit $?
fi
full=0
tests=()
for f in "${CHANGED[@]}"; do
case "$f" in
*/conftest.py | conftest.py | pyproject.toml | requirements*.txt | pytest.ini | setup.cfg) full=1 ;;
*.py)
base="$(basename "$f" .py)"
if [[ "$base" == test_* || "$base" == *_test ]]; then
tests+=("$f")
else
while IFS= read -r t; do tests+=("$t"); done \
< <(git ls-files "*test_${base}.py" "*${base}_test.py")
fi
;;
esac
done
if [[ $full -eq 0 && ${#CHANGED[@]} -eq 0 ]]; then echo "no changes vs HEAD: nothing to test"; exit 0; fi
if [[ $full -eq 0 && ${#tests[@]} -gt 0 ]]; then pytest_run -q --tb=short "${tests[@]}"; exit $?; fi
if [[ $full -eq 0 ]] && ! printf '%s\n' "${CHANGED[@]}" | grep -q '\.py$'; then
echo "no python changes: nothing to test"; exit 0
fi
echo "no mapped tests: running full suite"
pytest_run -q --tb=short