feat: enforce zero-warning reusable CI (#22) #47
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
| # Canonical single CI workflow for every JorisJonkers-dev repository. | |
| # | |
| # Org convention: ONE pipeline per repo, and it terminates in a single | |
| # aggregating job named "Pipeline Complete". That job name is the ONLY | |
| # required status check in the org/repo ruleset -- every other job feeds | |
| # into it via `needs:`. Adding or renaming an upstream job never requires | |
| # touching branch protection: as long as it is in `needs:` below, it gates | |
| # merges through the aggregator. | |
| name: CI | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
| permissions: | |
| contents: read | |
| jobs: | |
| actionlint: | |
| name: Actionlint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install actionlint | |
| env: | |
| ACTIONLINT_VERSION: 1.7.12 | |
| run: | | |
| curl -sSfL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash -o /tmp/download-actionlint.bash | |
| bash /tmp/download-actionlint.bash "$ACTIONLINT_VERSION" /tmp | |
| sudo install /tmp/actionlint /usr/local/bin/actionlint | |
| - name: Lint workflows | |
| run: actionlint -ignore 'property "job_workflow_sha" is not defined' .github/workflows/*.yml | |
| yaml-validity: | |
| name: YAML Validity | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Install YAML parser | |
| run: | | |
| python3 -m venv .venv-yaml | |
| .venv-yaml/bin/python -m pip install PyYAML==6.0.2 | |
| - name: Validate YAML files | |
| run: | | |
| .venv-yaml/bin/python - <<'PY' | |
| from pathlib import Path | |
| import sys | |
| import yaml | |
| files = sorted( | |
| path for path in Path(".").rglob("*") | |
| if path.suffix in {".yml", ".yaml"} and ".git" not in path.parts | |
| ) | |
| failed = False | |
| for path in files: | |
| try: | |
| with path.open("r", encoding="utf-8") as handle: | |
| yaml.safe_load(handle) | |
| except Exception as exc: | |
| failed = True | |
| print(f"::error file={path}::{exc}") | |
| if failed: | |
| sys.exit(1) | |
| print(f"Validated {len(files)} YAML files.") | |
| PY | |
| python-tests: | |
| name: Python Tests | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: actions/setup-python@v6 | |
| with: | |
| python-version: '3.13' | |
| - name: Install coverage | |
| run: | | |
| python3 -m venv .venv-tests | |
| .venv-tests/bin/python -m pip install coverage==7.10.7 | |
| - name: Run migration guard tests with coverage | |
| run: | | |
| .venv-tests/bin/python -m coverage run --source=scripts.check_migrations -m unittest discover -s tests | |
| .venv-tests/bin/python -m coverage report --fail-under=80 | |
| # --------------------------------------------------------------------------- | |
| # Terminal aggregator. This is the single required check in the ruleset. | |
| # It runs even when an upstream job fails/cancels (if: always()) and then | |
| # explicitly fails unless every dependency succeeded -- so a skipped or | |
| # failed gating job can never be mistaken for a green pipeline. | |
| # --------------------------------------------------------------------------- | |
| pipeline-complete: | |
| name: Pipeline Complete | |
| if: always() | |
| needs: [actionlint, yaml-validity, python-tests] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Verify all gating jobs succeeded | |
| uses: re-actors/alls-green@release/v1 | |
| with: | |
| jobs: ${{ toJSON(needs) }} |