ci: gate merges on the test check, and verify the merge result
#229
Workflow file for this run
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 EVERY MERGE, and on demand. A push to `main` is what a merge looks like from Actions' side, | |
| # so every merged PR now re-runs lint, the suite, and the build-identity check against the tree | |
| # that actually resulted from the merge. | |
| # | |
| # That last phrase is the point, and it is why this is NOT the redundant run it was once called. | |
| # A `pull_request` run tests the PR head; a `push` run tests `main` after the merge landed. Those | |
| # differ whenever two independently-green branches conflict semantically -- each passes alone, the | |
| # merge of them does not. Only the post-merge run can see that. | |
| # | |
| # `pull_request` is here for a specific, load-bearing reason: it is what makes this job report a | |
| # status named `test` on a PR, and the `main` ruleset REQUIRES that context before a merge is | |
| # allowed (issue #236). The two are a matched pair and must be changed together -- removing this | |
| # trigger while the ruleset still requires `test` would leave every PR waiting forever on a check | |
| # that can never report, which is precisely the broken state #236 was filed to fix. That is not a | |
| # hypothetical: the ruleset previously required "Lint, Unit tests, Build" and "Integration Tests", | |
| # names no job here has ever produced. | |
| # | |
| # The context string is `test` because the job below is `test:` and declares no `name:`. Renaming | |
| # that job, or giving it a `name:`, silently changes the context and breaks the ruleset the same | |
| # way. If you rename it, update the ruleset in the same change. | |
| # | |
| # So the two triggers do different jobs: `pull_request` GATES the merge, `push` VERIFIES the result | |
| # of it. `release.yml` remains the gate that matters for money -- it re-runs `ruff` + `pytest` | |
| # itself before it will build an artifact, so a red `main` cannot become a wheel, a tag, or a | |
| # release asset even if both of these were removed. | |
| on: | |
| pull_request: | |
| push: | |
| branches: [main] | |
| workflow_dispatch: | |
| # `github.ref` is `refs/pull/<n>/merge` for PR runs, `refs/heads/main` for the post-merge push, and | |
| # `refs/heads/<branch>` for a dispatch -- distinct keys, so a PR run never cancels the post-merge | |
| # run. Pushes to the same PR do cancel each other, and back-to-back merges cancel the older `main` | |
| # run -- correct in both cases: only the newest result matters. | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| test: | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 15 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v6 | |
| with: | |
| enable-cache: true | |
| - name: Set up Python | |
| run: uv python install # version comes from .python-version; never pin it here twice | |
| - name: Sync dependencies | |
| run: uv sync --all-extras --dev | |
| - name: Lint | |
| run: uv run ruff check keel tests packages | |
| - name: Test | |
| run: uv run pytest -q | |
| # A build that cannot identify itself must not reach a release. This also catches an | |
| # import-time break in the CLI, which `pytest` alone would not surface as sharply. | |
| - name: Build identity | |
| run: uv run keel --version |