diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33f4a44..02ce4f9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,6 +4,13 @@ on: push: branches: [main] pull_request: + # Mondays, 06:00 UTC. Every other trigger here is caused by someone pushing, + # so without this the suite is only ever run against the dependency versions + # `uv.lock` pins -- see the `upstream-drift` job for what that hides. Note + # GitHub disables a scheduled workflow after 60 days with no repository + # activity, so a long quiet spell stops these runs rather than failing them. + schedule: + - cron: "0 6 * * 1" workflow_dispatch: permissions: @@ -167,6 +174,85 @@ jobs: # `addopts` in pyproject.toml supplies `-m 'not live'`. run: uv run pytest + upstream-drift: + # `uv.lock` is what every other job resolves against, so nothing in this + # workflow would notice a new `langgraph` minor breaking the runtime until a + # user on a fresh `pip install grapharc` hit it. Issue #103 is the standing + # form of that complaint: the extras are unbounded and the lockfile hides + # what the next major would do. This job re-resolves every range from + # scratch and runs the suite against the newest versions the constraints in + # pyproject.toml actually allow. + # + # Scheduled and manual only, deliberately. A pull request has to be judged + # against the lockfile it ships; if this ran on PRs, an upstream release on + # the morning of a review would turn someone else's branch red for a reason + # that branch did not cause. + name: upstream drift (unlocked deps) + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + # `issues: write` is for the reporting step below; a job's permissions + # replace the workflow's rather than adding to them, so `contents: read` + # is repeated here. + permissions: + contents: read + issues: write + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + with: + python-version: "3.12" + enable-cache: true + - name: Re-resolve every dependency to the newest allowed version + run: uv lock --upgrade + - name: Report what moved + # Printed whether or not the suite then passes: a green run against + # moved dependencies is the useful half of this job, because it is the + # evidence that a range can be widened or a pin dropped. + run: git --no-pager diff --stat -- uv.lock + - name: Sync and test against the re-resolved versions + run: | + uv sync --all-extras --group dev + uv run pytest + - name: Say so where someone will see it + # A scheduled run reports to nobody. This repository has already paid + # for that: `pages.yml` failed on two consecutive pushes and the + # failures sat unnoticed for the better part of two months, while the + # published site served a version six releases behind. A weekly job in + # a repository that goes quiet for weeks at a time is the same shape, + # so the failure comes to the issue tracker instead of the Actions tab. + # + # One issue, reused: an unattended weekly job that opens a fresh issue + # every Monday is a second way of being ignored. Matched on title + # rather than a label, so this needs no label to exist first. + if: failure() + env: + GH_TOKEN: ${{ github.token }} + TITLE: "upstream drift: the suite fails against re-resolved dependencies" + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + run: | + body=$(cat <