diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8185a0a..6f3ae00 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,18 +1,26 @@ name: CI -# Unfiltered `push` as well as `pull_request` on purpose. The artifacts the build produces exist -# so a change can be tried on a Pi or a Mac without building it there, and that only works if -# every branch push produces them -- not just the ones that have a pull request open. The cost -# is that a PR raised from a branch in this repo builds twice. +# Every branch `push` as well as `pull_request` on purpose. The artifacts the build produces +# exist so a change can be tried on a Pi or a Mac without building it there, and that only works +# if every branch push produces them -- not just the ones that have a pull request open. The +# cost is that a PR raised from a branch in this repo builds twice. # # Except tags, which belong to release.yml. Both workflows call the same build, and the # `concurrency` group below is keyed by workflow name, so nothing would collapse the pair: a # `v*` push would otherwise run the matrix twice over and report two statuses for one tag. -# `tags-ignore` rather than a positive `branches` filter, so the sentence above stays "every -# branch push, except --" instead of a list a reader has to check is exhaustive. +# +# The `branches` filter is what excludes them, and the rule behind it cuts both ways. A `push` +# naming only branch filters does not fire for tags at all -- that is the whole of how a tag is +# left to release.yml -- and the mirror of that rule is a trap worth naming: a `push` naming only +# `tags` or `tags-ignore` does not fire for *branches*, so `tags-ignore: '**'` reads like +# "everything except tags" and means "nothing at all". +# +# `'**'` and not `'*'`, because only the doubled form matches a `/`, and the branches here carry +# two apiece. `'*'` would be this same bug wearing a better disguise: main would build and no +# task branch would. on: push: - tags-ignore: + branches: - '**' pull_request: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 91f7a73..db332a8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,8 +2,8 @@ name: Release # A human tags; this publishes what that tag builds. Nothing here creates a tag, and there is # no second trigger -- no branch push, no pull request, no schedule, no `workflow_dispatch` -- -# so the only way to reach `gh release create` is to have pushed a `v*` tag. `ci.yml` ignores -# tags for the same reason, leaving one workflow to answer for one tag. +# so the only way to reach `gh release create` is to have pushed a `v*` tag. `ci.yml` filters on +# branches and so never fires for a tag, leaving one workflow to answer for one tag. on: push: tags: diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index afdf57e..dca53f8 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1046,13 +1046,14 @@ operator to choose between `Type=simple` and `Type=forking` and write the unit t tee` returns 0 on a failed configure, and the assertion after it greps a truncated log — both callers green on a build that never configured. A fail-open gate is worse than no gate, which is the argument the shellcheck job at the top of `ci.yml` already makes. -- **`ci.yml` gained a `tags-ignore`, and that is the whole of its *behavioural* change** — - the rest of its diff is the matrix moving out to `build.yml`. Its `push` was +- **`ci.yml` gained a ref filter on its `push`, and that is the whole of its *behavioural* + change** — the rest of its diff is the matrix moving out to `build.yml`. Its `push` was unfiltered on purpose, and a `v*` push would otherwise have run the matrix twice over and reported two statuses for one tag — the `concurrency` group is keyed by workflow name, so - nothing collapses the pair. `tags-ignore` rather than a positive `branches: ['**']` filter, - so the existing "every branch push, on purpose" comment stays true with an exception named - rather than being rewritten as a list a reader has to check is exhaustive. + nothing collapses the pair. The filter is `branches: ['**']`, which is what excludes tags: a + `push` filtered on one kind of ref does not fire for the other kind at all. The inverse + spelling, `tags-ignore: ['**']`, reads like the same sentence but is that rule the other way + around, and stops branch pushes firing at all. - **Fail closed, in two places, because `needs:` only covers one of them.** `needs: build` catches a leg that went red. What it does not catch is a leg that went green *while publishing nothing* — `Upload` is `if: matrix.publish`, and `if-no-files-found: error` @@ -1374,9 +1375,9 @@ on a null-sink-only, mDNS-less binary. The three platform legs additionally run test and upload the binary they built, kept 14 days. The matrix itself lives in `.github/workflows/build.yml`, called by `ci.yml` and by item 10's -`release.yml` alike, so one definition answers for both paths; `ci.yml` carries a -`tags-ignore` so a tag push builds once rather than twice. See item 10 for why that split is -a reusable workflow rather than a composite action. +`release.yml` alike, so one definition answers for both paths; `ci.yml` filters its `push` to +`branches: ['**']`, which excludes tags, so a tag push builds once rather than twice. See item +10 for why that split is a reusable workflow rather than a composite action. The unit harness is item 1's and unchanged: GoogleTest via `FetchContent` pinned to a tag, wired to CTest with `gtest_discover_tests()`, defaulting ON only when this is the top-level