Files
nolimit-discounts/.github/workflows/compat.yml
T
kristianwindandClaude Opus 5 bd058566f5 Parse the workflows on Gitea too, and keep the compat matrix off its runner
Both found by the subscriptions session in its copy of this kit, and both
reproduce here. They share the shape everything else in ci/ has had: the
failure is invisible on GitHub, which is where anyone would be looking.

1. ci/workflows-in-sync.sh never parsed the workflow files on Gitea, and
   printed "Workflows: in sync." anyway. It skipped with a note when PyYAML was
   missing — and PyYAML is missing in exactly one place. Measured here rather
   than taken on report: `import yaml` on gitea/runner-images:ubuntu-latest
   raises ModuleNotFoundError. So the check was switched off on the single
   forge where an unparseable workflow produces no run at all, no failed run
   and no error to click on, which looks exactly like a runner that has not
   picked the job up.

   ci_python_yaml in ci/lib.sh now falls back to a container the way ci_php
   already did — alpine + py3-yaml, no network install, one apk call. There is
   no skip path left: workflows that cannot be parsed are a failure, not a
   note. Both routes verified to give byte-identical output.

2. compat.yml ran its five-cell matrix on Gitea for every PR touching ci/ or
   tests/. The `schedule` was gated to GitHub for exactly this reason and
   `pull_request` was forgotten. The subscriptions repo measured six jobs dying
   together with exit code 137 — SIGKILL, runner out of memory, two seconds in,
   without one error message in the log. Latent here only because this repo
   merges through GitHub and never opens a Gitea PR; one Gitea PR touching ci/
   would have found it.

New ci/check-workflows.py enforces both, called through ci_python_yaml. It
parses every workflow and fails when a Gitea job with more than one matrix cell
is not gated, and it is fatal when it parsed no file at all.

🔴 Its first version asked whether the `if:` CONTAINED the server_url test, and
passed the very condition it exists to catch: `!= 'schedule' || server_url ==
...` contains the gate while letting pull_request through. Substring matching is
adjacent to the signal rather than the signal. It now compares the whole
normalised condition against an allowlist, and was checked both ways in one run
— red against the unfixed compat.yml naming the job and the cell count, green
after.

Three self-test assertions added, 29 total: an unparseable workflow fails, an
ungated multi-cell matrix fails, and parsing zero files fails.

Two notes from building it, both the same trap this kit keeps setting. The
container path could not find check-workflows.py because ci_in_container tars
`git ls-files` and the file was untracked. And the ungated-matrix fixture used
| as a sed delimiter while the gate itself contains ||, so sed silently changed
nothing; the assertion reported "could not stage the fixture" instead of
passing, which is the only reason it was noticed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-17 00:37:30 +02:00

97 lines
3.8 KiB
YAML

name: Compatibility
# The question this answers: *will the shop still work after I press update?*
#
# It runs on a schedule rather than on a commit, because the thing that breaks
# a finished plugin is not a new commit — it is a new WordPress or a new
# WooCommerce, released on somebody else's timetable. A weekly run turns that
# into a red build here, days before it turns into a broken checkout there.
#
# `nightly` and `beta` are the early warning: they are what the next release
# will be. When one of those two cells goes red and the others stay green, the
# plugin has a few weeks to be fixed rather than a few hours.
on:
schedule:
# Monday morning, so a failure has a whole week in front of it.
- cron: '0 6 * * 1'
workflow_dispatch:
# Also whenever the tooling itself changes, because a compatibility run that
# silently stopped testing anything is worse than no run at all.
pull_request:
paths:
- 'ci/**'
- 'tests/**'
- '.github/workflows/compat.yml'
- '.gitea/workflows/compat.yml'
permissions:
contents: write
pull-requests: write
# No `concurrency:` block — see the note in ci.yml. Kept byte-identical in
# .github/workflows and .gitea/workflows; ci/workflows-in-sync.sh enforces it.
jobs:
matrix:
name: WP ${{ matrix.wp }} / WC ${{ matrix.wc }} / PHP ${{ matrix.php }}
# The schedule fires on both forges, and the second run would be the same
# work again on a single self-hosted runner for no extra information.
# GitHub's runners take the scheduled load; Gitea keeps workflow_dispatch,
# so the run is still one click away there.
#
# 🔴 This gates EVERY event except workflow_dispatch, not just the
# schedule. The earlier `!= 'schedule'` form let `pull_request` through,
# and this workflow triggers on any PR touching ci/ or tests/ — so five
# cells landed on the one self-hosted runner together. A sibling plugin
# measured six jobs dying at once with exit code 137: SIGKILL, out of
# memory, two seconds in, with not one error message in the log.
if: github.event_name == 'workflow_dispatch' || github.server_url == 'https://github.com'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
# Today's shop.
- { wp: latest, wc: latest, php: '8.3' }
# The next WordPress, before it ships.
- { wp: nightly, wc: latest, php: '8.3' }
# The next WooCommerce, before it ships. Falls back to the stable
# release when there is no open beta, so the cell is never skipped.
- { wp: latest, wc: beta, php: '8.3' }
# The floor the plugin header promises. A shop on an old host is
# exactly the shop that will not notice a fatal quickly.
- { wp: latest, wc: latest, php: '7.4' }
# And the newest PHP, which is where deprecations show up first.
- { wp: latest, wc: latest, php: '8.4' }
steps:
- uses: actions/checkout@v4
- name: Install into a throwaway shop and run the tests
env:
WP_VERSION: ${{ matrix.wp }}
WC_VERSION: ${{ matrix.wc }}
PHP_VERSION: ${{ matrix.php }}
run: ci/site-test.sh
headers:
name: Keep the compatibility headers honest
# Only after the current releases have actually been tested. Bumping
# "Tested up to" on a red build would be writing a claim we have just
# watched fail.
needs: matrix
# The PR is opened through the GitHub API, and the scheduled run that
# produces it is the GitHub one.
if: github.server_url == 'https://github.com' && github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Bump the headers and open a PR if they are behind
env:
GH_TOKEN: ${{ github.token }}
run: ci/bump-tested-up-to.sh