a control can say it is one, and the forked instrument is reconciled #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: ci | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| workflow_dispatch: | |
| jobs: | |
| test: | |
| name: tests (python ${{ matrix.python }} on ${{ matrix.os }}) | |
| runs-on: ${{ matrix.os }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-latest, macos-latest] | |
| python: ["3.10", "3.12", "3.13"] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: ${{ matrix.python }} | |
| - name: git identity for the adapter tests | |
| run: | | |
| git config --global user.email ci@example.com | |
| git config --global user.name ci | |
| - run: python3 tests/test_gates.py | |
| mutation: | |
| name: the suite must be able to fail | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: "3.12" | |
| - run: | | |
| git config --global user.email ci@example.com | |
| git config --global user.name ci | |
| # A green suite proves nothing unless it can go red. Break each gate on | |
| # purpose, one at a time; if the tests still pass, the tests are decorative | |
| # and this job fails instead. | |
| # | |
| # Every mutant must also APPLY. A mutation job whose search string has | |
| # drifted out of the source silently tests nothing, which is the same | |
| # failure the gates themselves exist to catch — so an unapplied mutant is | |
| # an error here, not a skip. | |
| - name: break each gate in turn, expect failures | |
| run: | | |
| python3 - <<'EOF' | |
| import pathlib, shutil, subprocess, sys | |
| MUTANTS = [ | |
| ("gates/research_depth_gate.py", | |
| " bad, lossy = offending_claims(text, os.path.dirname(os.path.abspath(path)))", | |
| " bad, lossy = [], [] # injected mutant"), | |
| ("gates/ownership_gate.py", | |
| " fails = check_text(text, excl)", | |
| " fails = [] # injected mutant"), | |
| ("gates/citation_attribution_gate.py", | |
| " all_fails += check_entry(key, rest, block)", | |
| " all_fails += [] # injected mutant"), | |
| ] | |
| for path, old, new in MUTANTS: | |
| p = pathlib.Path(path) | |
| orig = p.read_text() | |
| if old not in orig: | |
| print(f"::error::mutant for {path} did not apply — search string has drifted") | |
| sys.exit(1) | |
| p.write_text(orig.replace(old, new, 1)) | |
| rc = subprocess.run([sys.executable, "tests/test_gates.py"]).returncode | |
| p.write_text(orig) | |
| if rc == 0: | |
| print(f"::error::suite passed with {path} broken — the tests do not test") | |
| sys.exit(1) | |
| print(f"suite correctly detected the {path} mutant") | |
| EOF | |
| shellcheck: | |
| name: shellcheck | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - run: sudo apt-get update && sudo apt-get install -y shellcheck | |
| - run: shellcheck -s sh adapters/git/pre-commit adapters/git/install.sh |