Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 51 additions & 6 deletions .github/workflows/cron-conda.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@ on:
# publishes to Anaconda, and letting two runs upload the same version
# concurrently is worse than dropping a superseded duplicate.
#
# A `workflow_run` is keyed on the commit `Unit Tests` verified instead (#1643).
# Each `Unit Tests` completion raises a run here, and one whose verdict is for an
# older commit only runs `verdict` and skips -- but in a shared group it would
# still displace a pending run whose verdict matches, since "any existing pending
# job or workflow in the same concurrency group will be canceled" (GitHub,
# "Control the concurrency of workflows and jobs").
#
# Note this group is not what serialises the repository writes -- the
# `conda-update` job below carries its own `repository-maintenance` group, shared
# with cron-vendor, and that is what actually stops two jobs committing at once.
concurrency:
group: conda-update-${{ github.ref }}
group: conda-update-${{ github.event.workflow_run.head_sha || github.ref }}
cancel-in-progress: false

jobs:
Expand All @@ -44,18 +51,54 @@ jobs:
with:
gate-only: true

# #1643: `github.sha`, which every checkout here takes, is main's tip when the
# `workflow_run` was raised -- not the commit the completed `Unit Tests` run
# verified, and in a merge wave the two differ. This job compares them with
# util/verdict_covers_checkout.py, whose docstring gives the rule, and the job
# below acts on a `workflow_run` only when `verified` is 'true'. Its `if:` is
# that job's `workflow_run` clause, so a run raised by a pull request's or a
# failed `Unit Tests` run still concludes `skipped`, as before.
verdict:
name: Verdict covers this commit
if: >-
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
outputs:
verified: ${{ steps.verdict.outputs.verified }}
steps:
# The default ref, as the job below uses, so both see the same `github.sha`.
# Twenty commits leaves room for the bot commits the rule accepts; a verdict
# further back is not found, and so not accepted.
- uses: actions/checkout@v7
with:
fetch-depth: 20
persist-credentials: false

- name: Compare the verified commit with this checkout
id: verdict
env:
VERDICT_SHA: ${{ github.event.workflow_run.head_sha }}
run: python3 util/verdict_covers_checkout.py

conda-update:
name: Update requirements.txt
runs-on: ubuntu-latest
needs: [ unit-tests ]
needs: [ unit-tests, verdict ]
# `needs` cannot itself be conditional, so a skipped gate (`workflow_run`)
# has to be accepted explicitly -- by default this job would inherit the
# skip. `!cancelled()` rather than `always()` so that cancelling the run
# still cancels the update; a gate that actually failed still blocks.
#
# The `workflow_run` clause is the real gate for a push to main now: it
# reads the verdict `Unit Tests` already reached for this exact commit
# instead of re-running it. `.event == 'push'` and `.head_branch == 'main'`
# reads the verdict `Unit Tests` already reached instead of re-running it,
# and `verdict` says whether that verdict covers the commit checked out
# below (#1643). `.event == 'push'` and `.head_branch == 'main'`
# both matter -- `workflow_run` fires for *every* completion of that
# workflow, including one triggered by a pull request, and without this
# check a green PR would upload a conda distribution built from that PR's
Expand All @@ -65,7 +108,8 @@ jobs:
(github.event_name != 'workflow_run' || (
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
github.event.workflow_run.head_branch == 'main' &&
needs.verdict.outputs.verified == 'true'
))
concurrency:
group: repository-maintenance
Expand All @@ -90,7 +134,8 @@ jobs:
# default-branch commit removes the pattern outright rather than
# relying on CodeQL tracing through the guard. The push below rebases
# onto main's actual tip before writing regardless, so pinning to the
# exact tested sha would have bought nothing here either.
# exact tested sha would have bought nothing here either. Whether this
# commit is one that verdict covers is `verdict`'s question (#1643).
#
# The push below re-points `origin` at an SSH remote and authenticates
# with the loaded deploy key, not with this checkout's credential --
Expand Down
57 changes: 51 additions & 6 deletions .github/workflows/cron-vendor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,18 @@ permissions: {}
# picks the registries up again, whereas a dropped validation run means a commit
# nobody ever checked.
#
# A `workflow_run` is keyed on the commit `Unit Tests` verified instead (#1643).
# Each `Unit Tests` completion raises a run here, and one whose verdict is for an
# older commit only runs `verdict` and skips -- but in a shared group it would
# still displace a pending run whose verdict matches, since "any existing pending
# job or workflow in the same concurrency group will be canceled" (GitHub,
# "Control the concurrency of workflows and jobs").
#
# Note this group is not what serialises the repository writes -- the
# `vendor-update` job below carries its own `repository-maintenance` group, shared
# with cron-conda, and that is what actually stops two jobs committing at once.
concurrency:
group: vendor-update-${{ github.ref }}
group: vendor-update-${{ github.event.workflow_run.head_sha || github.ref }}
cancel-in-progress: false

jobs:
Expand All @@ -50,17 +57,53 @@ jobs:
with:
gate-only: true

# #1643: `github.sha`, which every checkout here takes, is main's tip when the
# `workflow_run` was raised -- not the commit the completed `Unit Tests` run
# verified, and in a merge wave the two differ. This job compares them with
# util/verdict_covers_checkout.py, whose docstring gives the rule, and the job
# below acts on a `workflow_run` only when `verified` is 'true'. Its `if:` is
# that job's `workflow_run` clause, so a run raised by a pull request's or a
# failed `Unit Tests` run still concludes `skipped`, as before.
verdict:
name: Verdict covers this commit
if: >-
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
outputs:
verified: ${{ steps.verdict.outputs.verified }}
steps:
# The default ref, as the job below uses, so both see the same `github.sha`.
# Twenty commits leaves room for the bot commits the rule accepts; a verdict
# further back is not found, and so not accepted.
- uses: actions/checkout@v7
with:
fetch-depth: 20
persist-credentials: false

- name: Compare the verified commit with this checkout
id: verdict
env:
VERDICT_SHA: ${{ github.event.workflow_run.head_sha }}
run: python3 util/verdict_covers_checkout.py

vendor-update:
runs-on: macos-latest
needs: [ unit-tests ]
needs: [ unit-tests, verdict ]
# `needs` cannot itself be conditional, so a skipped gate (`workflow_run`)
# has to be accepted explicitly -- by default this job would inherit the
# skip. `!cancelled()` rather than `always()` so that cancelling the run
# still cancels the update; a gate that actually failed still blocks.
#
# The `workflow_run` clause is the real gate for a push to main now: it
# reads the verdict `Unit Tests` already reached for this exact commit
# instead of re-running it. `.event == 'push'` and `.head_branch == 'main'`
# reads the verdict `Unit Tests` already reached instead of re-running it,
# and `verdict` says whether that verdict covers the commit checked out
# below (#1643). `.event == 'push'` and `.head_branch == 'main'`
# both matter -- `workflow_run` fires for *every* completion of that
# workflow, including one triggered by a pull request, and without this
# check a green PR would push a vendor-registry commit from that PR's tree.
Expand All @@ -69,7 +112,8 @@ jobs:
(github.event_name != 'workflow_run' || (
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
github.event.workflow_run.head_branch == 'main' &&
needs.verdict.outputs.verified == 'true'
))
concurrency:
group: repository-maintenance
Expand All @@ -94,7 +138,8 @@ jobs:
# default-branch commit removes the pattern outright rather than
# relying on CodeQL tracing through the guard. The push below rebases
# onto main's actual tip before writing regardless, so pinning to the
# exact tested sha would have bought nothing here either.
# exact tested sha would have bought nothing here either. Whether this
# commit is one that verdict covers is `verdict`'s question (#1643).
# The push below re-points `origin` at an SSH remote and authenticates
# with the loaded deploy key, not with this checkout's credential --
# so there is nothing here for a persisted token to do, and one less
Expand Down
57 changes: 52 additions & 5 deletions .github/workflows/deploy-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,16 @@ permissions:
# the newer one publishes, and deploying the older commit's docs after the newer
# ones would actively be wrong. GitHub Pages also permits only one live
# deployment, so serialising here is a requirement rather than a saving.
#
# A `workflow_run` is keyed on the commit `Unit Tests` verified instead (#1643).
# One whose verdict is for an older commit only runs `verdict` and skips, and in
# the shared group it would still displace a pending run whose verdict matches,
# since "any existing pending job or workflow in the same concurrency group will
# be canceled" (GitHub, "Control the concurrency of workflows and jobs"). The
# coalescing and serialising above then happen in `deploy-pages`' own `ci-`
# group, which only a run whose verdict covers its checkout reaches.
concurrency:
group: pages-${{ github.ref }}
group: pages-${{ github.event.workflow_run.head_sha || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
Expand All @@ -48,17 +56,53 @@ jobs:
with:
gate-only: true

# #1643: `github.sha`, which every checkout here takes, is main's tip when the
# `workflow_run` was raised -- not the commit the completed `Unit Tests` run
# verified, and in a merge wave the two differ. This job compares them with
# util/verdict_covers_checkout.py, whose docstring gives the rule, and the job
# below acts on a `workflow_run` only when `verified` is 'true'. Its `if:` is
# that job's `workflow_run` clause, so a run raised by a pull request's or a
# failed `Unit Tests` run still concludes `skipped`, as before.
verdict:
name: Verdict covers this commit
if: >-
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
outputs:
verified: ${{ steps.verdict.outputs.verified }}
steps:
# The default ref, as the job below uses, so both see the same `github.sha`.
# Twenty commits leaves room for the bot commits the rule accepts; a verdict
# further back is not found, and so not accepted.
- uses: actions/checkout@v7
with:
fetch-depth: 20
persist-credentials: false

- name: Compare the verified commit with this checkout
id: verdict
env:
VERDICT_SHA: ${{ github.event.workflow_run.head_sha }}
run: python3 util/verdict_covers_checkout.py

deploy-pages:
needs: [ unit-tests ]
needs: [ unit-tests, verdict ]
# `needs` cannot itself be conditional, so a skipped gate (pull requests,
# and now `workflow_run`) has to be accepted explicitly -- by default this
# job would inherit the skip. `!cancelled()` rather than `always()` so that
# cancelling the run still cancels the docs build; a gate that actually
# failed still blocks.
#
# The `workflow_run` clause is the real gate for a push to main now: it
# reads the verdict `Unit Tests` already reached for this exact commit
# instead of re-running it. `.event == 'push'` and `.head_branch == 'main'`
# reads the verdict `Unit Tests` already reached instead of re-running it,
# and `verdict` says whether that verdict covers the commit checked out
# below (#1643). `.event == 'push'` and `.head_branch == 'main'`
# both matter -- `workflow_run` fires for *every* completion of that
# workflow, including one triggered by a pull request, and without this
# check a green PR would publish docs from that PR's tree.
Expand All @@ -67,7 +111,8 @@ jobs:
(github.event_name != 'workflow_run' || (
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.event == 'push' &&
github.event.workflow_run.head_branch == 'main'
github.event.workflow_run.head_branch == 'main' &&
needs.verdict.outputs.verified == 'true'
))
concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession.
runs-on: macos-latest
Expand All @@ -86,6 +131,8 @@ jobs:
# relying on CodeQL tracing through the guard. `main`'s tip is also
# the *right* commit for docs, which should reflect current main,
# not a slightly older one -- so this is not a compromise here.
# Whether this commit is one that verdict covers is `verdict`'s
# question (#1643).
persist-credentials: false

- uses: actions/setup-python@v7
Expand Down
25 changes: 20 additions & 5 deletions docs/source/contributing/workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,10 @@ itself (dotted, above) **only when its own trigger is not** ``workflow_run``
(their gate jobs' ``if:`` guards this explicitly) -- i.e. only on the
Saturday ``schedule`` path, where no completed Unit Tests run exists yet to
read a verdict from. On the ``workflow_run`` path (thick arrows), each reads
the verdict Unit Tests already reached for that same commit instead of
re-running the gate a second time.
the verdict Unit Tests already reached instead of re-running the gate a second
time. Vendor Update, Conda Update and GitHub Pages act on it only once their
``verdict`` job, described under the edge list below, says it is a verdict on
the commit they build.

``workflow_run`` Edges
----------------------
Expand All @@ -224,6 +226,19 @@ and ``.head_branch`` (or, for Create Release and Coverage Comment, ``.conclusion
doing anything with an outward effect; none of the filtering happens in the
``on:`` block.

Those checks say nothing about *which* commit was verified. A ``workflow_run``
checks out ``github.sha``, ``main``'s tip when the event is raised, not
``github.event.workflow_run.head_sha``, the commit the completed run tested, and
in a merge wave the two differ: Conda Update 38325136710 bumped 2e941bbb7 under
the verdict for 9083163f5 (`#1643 <https://github.com/JarryShaw/PyPCAPKit/issues/1643>`__).
So Vendor Update, Conda Update and GitHub Pages each run a ``verdict`` job first,
and act only when the tip is the verified commit, or that commit plus bot commits
to ``conda/build`` and ``conda/requirements.txt`` alone; otherwise they skip, and
the tip's own Unit Tests run raises them again. The rule and its evidence are in
``util/verdict_covers_checkout.py``. It covers the checkout only: Conda Update's
push rebases onto ``main`` again, and Conda Tag tags whatever ``git pull`` brings
in, so a commit that lands after the event can still be tagged.

Reusable-Workflow Calls (``uses:``)
-----------------------------------

Expand All @@ -234,11 +249,11 @@ repository target the same reusable workflow, with the same input:

* ``.github/workflows/create-release.yml:374`` -- job ``unit-tests`` calls
``./.github/workflows/unit-tests.yml`` with ``gate-only: true``.
* ``.github/workflows/cron-conda.yml:43`` -- job ``unit-tests`` calls
* ``.github/workflows/cron-conda.yml:50`` -- job ``unit-tests`` calls
``./.github/workflows/unit-tests.yml`` with ``gate-only: true``.
* ``.github/workflows/cron-vendor.yml:49`` -- job ``unit-tests`` calls
* ``.github/workflows/cron-vendor.yml:56`` -- job ``unit-tests`` calls
``./.github/workflows/unit-tests.yml`` with ``gate-only: true``.
* ``.github/workflows/deploy-pages.yml:47`` -- job ``unit-tests`` calls
* ``.github/workflows/deploy-pages.yml:55`` -- job ``unit-tests`` calls
``./.github/workflows/unit-tests.yml`` with ``gate-only: true``.

``gate-only: true`` selects **two** of ``unit-tests.yml``'s **ten** jobs,
Expand Down
Loading
Loading