mirror of
https://github.com/kuhyx/WUT_Computer_Science.git
synced 2026-11-10 16:48:45 +01:00
repo contract: CLAUDE.md commands, test_changed, CI gate; Makefile cover hook, ignore coverage output
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7739d33532
commit
dadc2b3e6f
25
.github/workflows/repo-contract.yml
vendored
Normal file
25
.github/workflows/repo-contract.yml
vendored
Normal 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
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -489,3 +489,7 @@ gates/runnability-ci.tsv
|
||||
|
||||
# Per-course virtualenvs created by ./run.sh run.
|
||||
.venv/
|
||||
|
||||
# test coverage output
|
||||
.coverage
|
||||
coverage.xml
|
||||
|
||||
@ -67,6 +67,12 @@ repos:
|
||||
|
||||
- repo: local
|
||||
hooks:
|
||||
- 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)$
|
||||
# Coursework deliverables are allowed under the two course trees;
|
||||
# compiled output is allowed nowhere. See .binary-allowlist.
|
||||
- id: no-binaries
|
||||
|
||||
@ -155,3 +155,12 @@ applies.
|
||||
| `.binary-allowlist` | Path-scoped: deliverables allowed under the two course trees, compiled output allowed nowhere. |
|
||||
| `DOCS-runnability.md` | Per-course run status, recorded separately for `local` and `ci`. |
|
||||
| `TODO-*.md` | Open decisions. Each carries the removal marker and is deleted when the work lands. |
|
||||
|
||||
## Commands
|
||||
|
||||
- run: n/a: coursework archive; per course use `./run.sh run <dir>`
|
||||
- test: `scripts/test_suites.sh`
|
||||
- test-changed: `scripts/test_changed.sh`
|
||||
- lint: `ruff check .`
|
||||
- coverage: `COVERAGE=1 scripts/test_suites.sh`
|
||||
- coverage-gaps: `coverage-gaps coverage.xml`
|
||||
|
||||
5
Makefile
Normal file
5
Makefile
Normal file
@ -0,0 +1,5 @@
|
||||
# finish gate hook: the root-level pytest cannot collect the ECRYPT sieve suite (relative CSVs), so
|
||||
# the shared gate calls this target instead (see scripts/test_suites.sh).
|
||||
.PHONY: cover
|
||||
cover:
|
||||
COVERAGE=1 scripts/test_suites.sh
|
||||
26
scripts/check_repo_contract.sh
Executable file
26
scripts/check_repo_contract.sh
Executable 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 "$@"
|
||||
32
scripts/test_changed.sh
Executable file
32
scripts/test_changed.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# Run only the pytest suites whose directory has changes vs HEAD (staged,
|
||||
# unstaged and untracked). Quiet: failures plus a one-line summary per suite.
|
||||
# Changes to shared config (pyproject.toml, this repo's test scripts) run all
|
||||
# suites; changes outside any suite run nothing.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(git rev-parse --show-toplevel)"
|
||||
readonly ROOT
|
||||
readonly SUITES=(Programming/ECOTE Programming/ECRYPT_PROJECT/sieve)
|
||||
|
||||
changed="$(cd "$ROOT" && { git diff --name-only HEAD 2>/dev/null || true; git ls-files --others --exclude-standard; } | sort -u)"
|
||||
selected=()
|
||||
all=0
|
||||
for s in "${SUITES[@]}"; do
|
||||
grep -q "^${s}/" <<<"$changed" && selected+=("$s")
|
||||
done
|
||||
grep -Eq '^(pyproject\.toml|scripts/test_suites\.sh|scripts/test_changed\.sh)$' <<<"$changed" && all=1
|
||||
|
||||
if [[ $all -eq 1 ]]; then
|
||||
echo "shared test config changed: running all suites"
|
||||
exec "$ROOT/scripts/test_suites.sh"
|
||||
fi
|
||||
if [[ ${#selected[@]} -eq 0 ]]; then
|
||||
echo "no changes in a pytest suite: nothing to test"
|
||||
exit 0
|
||||
fi
|
||||
SUITE_DIRS="${selected[*]}" exec "$ROOT/scripts/test_suites.sh"
|
||||
40
scripts/test_suites.sh
Executable file
40
scripts/test_suites.sh
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
# ============================================================================
|
||||
# Run the pytest suites that exist in this archive, each from its own directory
|
||||
# (ECRYPT sieve reads its reference CSVs by relative path, so a repo-root
|
||||
# `pytest` cannot collect it). Extra args are passed to every pytest run.
|
||||
# Usage: scripts/test_suites.sh [pytest args]
|
||||
# COVERAGE=1 scripts/test_suites.sh # writes coverage.xml at repo root
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(git rev-parse --show-toplevel)"
|
||||
readonly ROOT
|
||||
readonly SUITES=(Programming/ECOTE Programming/ECRYPT_PROJECT/sieve)
|
||||
|
||||
run_suites() {
|
||||
local -a suites=("$@") extra=()
|
||||
[[ ${#suites[@]} -eq 0 ]] && suites=("${SUITES[@]}")
|
||||
if [[ "${COVERAGE:-0}" == 1 ]]; then
|
||||
export COVERAGE_FILE="$ROOT/.coverage"
|
||||
rm -f "$COVERAGE_FILE" "$ROOT/coverage.xml"
|
||||
fi
|
||||
local rc=0 s
|
||||
for s in "${suites[@]}"; do
|
||||
if [[ "${COVERAGE:-0}" == 1 ]]; then
|
||||
extra=(--cov="$ROOT/$s" --cov-branch --cov-append --cov-config="$ROOT/pyproject.toml")
|
||||
fi
|
||||
(cd "$ROOT/$s" && python3 -m pytest -q --tb=short "${extra[@]}" "${PYTEST_ARGS[@]}") || rc=$?
|
||||
done
|
||||
if [[ "${COVERAGE:-0}" == 1 ]]; then
|
||||
(cd "$ROOT" && python3 -m coverage xml -o coverage.xml --rcfile=pyproject.toml >/dev/null)
|
||||
fi
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
PYTEST_ARGS=("$@")
|
||||
[[ -n "${SUITE_DIRS:-}" ]] && read -r -a SELECTED <<<"$SUITE_DIRS" || SELECTED=()
|
||||
run_suites "${SELECTED[@]}"
|
||||
exit $?
|
||||
Loading…
Reference in New Issue
Block a user