From fc5b5c53c72bee829791ac97cc7007b7c7e1896c Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Thu, 1 Oct 2026 00:41:57 -0400 Subject: [PATCH] docs(contributing): correct releasing.rst's cascade guard (#958) The page generalised the sibling workflows' guard to the three publishing jobs: "The three jobs here do the same for each of their own direct dependencies: `!cancelled() && needs..result != 'failure'`". No job in create-release.yml uses that spelling. The only two occurrences in the file are comments explaining why it is not used (:119, :124-128). What the three actually use, for `github` and -- for `conda` -- `tag`: !cancelled() && (needs..result == 'success' || needs..result == 'skipped') The two differ only when a predecessor's own result is `cancelled`: `!= 'failure'` admits it, the equality pair does not. With `tag` cancelled and the run itself not, `!= 'failure'` would send `conda` into `actions/checkout` with `ref: conda-+0` for a tag never pushed, trading a clean skip for a checkout failure. So the documented guard would have reintroduced what the real one prevents. `version_check` is now named as the exception rather than folded into "each of their own direct dependencies", which it is: all three require it to have strictly succeeded, with no `|| 'skipped'`, because it produces the evidence the gates read. The first half of the paragraph is untouched and correct -- the three sibling workflows do use `!= 'failure'` for `needs.unit-tests.result`. tests/project/test_release_gates.py:624 already asserts `!= 'failure'` must not appear in these conditions, so the behaviour was pinned and only the prose disagreed. 34 tests, 23 subtests, re-derived as 34 under plain unittest. No test pins this page's prose. --- docs/source/contributing/releasing.rst | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/source/contributing/releasing.rst b/docs/source/contributing/releasing.rst index 8712e1b7a..bad9896e7 100644 --- a/docs/source/contributing/releasing.rst +++ b/docs/source/contributing/releasing.rst @@ -170,9 +170,21 @@ this run, it *skipped*. ``cron-vendor.yml``, ``deploy-pages.yml`` and ``cron-conda.yml`` each open with ``!cancelled() && needs.unit-tests.result != 'failure'`` so that a skipped gate does not cascade-skip them, while an outright failure or a -genuine run cancellation still does. The three jobs here do the same for each -of their own direct dependencies: ``!cancelled() && needs..result != -'failure'``. +genuine run cancellation still does. The three jobs here are stricter about +their *publishing* predecessors -- ``github``, and for ``conda`` also ``tag``: +``!cancelled() && (needs..result == 'success' || needs..result == +'skipped')``, not ``!= 'failure'``. The two spellings differ only when a +predecessor's own result is ``cancelled``, which ``!= 'failure'`` still admits +and the equality pair does not. Concretely: if ``tag`` is cancelled while the +run itself is not, ``!= 'failure'`` would let ``conda`` proceed into its +``actions/checkout`` with ``ref: conda-+0``, a tag ``tag`` never +pushed, trading a clean skip for a checkout failure; the equality pair skips +``conda`` outright instead. + +``version_check`` is the exception, and deliberately so: all three require +``needs.version_check.result == 'success'`` with no ``|| 'skipped'``. It +produces the evidence the gates read, so a skipped or cancelled +``version_check`` leaves them nothing to decide on. The pipeline, and why one approval is enough ----------------------------------------------