chore(release): 0.3.15-unstable.20260831165924 #2369
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Code Quality | |
| on: | |
| push: | |
| # DEFAULT BRANCHES ONLY. `pull_request` below carries every other branch. | |
| # | |
| # This was an allow-list of branch prefixes, and that was a gate with a | |
| # SILENT hole: a branch matching nothing got no CI at all, and its last | |
| # visible status was whatever it inherited — indistinguishable, on every | |
| # dashboard, from a branch that passed. Two live examples, both found | |
| # 2026-08-14: `perf/**` was uncovered in openconnector, where a merge | |
| # carrying unresolved conflict markers and 84 failing tests was pushed and | |
| # nothing ran; and `feat/**` was uncovered in openregister, because the | |
| # list said `feature/**`. | |
| # | |
| # The comment that stood here said adding prefixes was not the durable fix, | |
| # and that the durable fix was to let the pull_request trigger gate it. | |
| # THIS IS THAT CHANGE. | |
| # | |
| # What forced it now: a push to a branch with an open PR ran the SAME 34 | |
| # jobs TWICE on the same commit. `concurrency` cannot dedupe them — the | |
| # group is suffixed by event name deliberately (.github#540: a | |
| # default-branch push carries jobs a PR run does not, and a dispatch must | |
| # not be cancellable by a standing release PR), so the two events sit in | |
| # different lanes BY DESIGN and both run to completion. Measured fleet-wide | |
| # 2026-08-25..27, 659 of 2,106 Code Quality runs were that duplicate — 31% | |
| # of the fleet's most expensive workflow, re-deciding a commit another run | |
| # was already deciding. The account ceiling is 60 concurrent jobs (Team | |
| # plan); the fleet was measured at 53 running with 1,528 jobs queued behind | |
| # them, the oldest run 7 hours old and not yet started. | |
| # | |
| # NO BRANCH LOSES ITS FLOOR. merge-hygiene.yml runs on `'**'` — every | |
| # branch anyone pushes, no prefix list to forget — and it is the check | |
| # `development` actually requires. That is the smoke alarm; this workflow | |
| # is the fire brigade and belongs on the PR. Of 668 feature-branch push | |
| # runs in that window, only NINE were on a branch with no PR run beside | |
| # them. | |
| # | |
| # The default branches STAY: their push runs are not duplicates, they are | |
| # the only carrier of Coverage Baseline Check, SBOM and Features Extract, | |
| # none of which run on a pull_request event. | |
| branches: | |
| - main | |
| - development | |
| pull_request: | |
| # Spelled out rather than left to the default so the set is reviewable. | |
| # `synchronize` — a push to an open PR — is the load-bearing one: without | |
| # it the suite runs once at PR-open and every later commit merges unchecked | |
| # under the first run's green tick. Cost is bounded by the `concurrency` | |
| # block below, which cancels the in-flight run for the same head ref. | |
| types: [opened, reopened, synchronize] | |
| branches: [main, beta, development] | |
| workflow_dispatch: | |
| # Deduplicating a `push` run against the `pull_request` run for the SAME head | |
| # ref is the point of this block, and for a feature branch it is exactly right: | |
| # two runs of identical jobs, one of them wasted. | |
| # | |
| # It is wrong for `main` and `development`, because the push run there is NOT a | |
| # duplicate — it carries the push-only "Coverage Baseline Check", which is the | |
| # whole push side of the coverage ratchet. And those two branches always have an | |
| # open PR whose `head_ref` IS the branch name: the standing "Release: merge | |
| # development into beta". `github.head_ref` on that PR run and `github.ref_name` | |
| # on the push run both render `development`, so both landed in the identical | |
| # group `quality-development`, and `cancel-in-progress` killed whichever started | |
| # first — always the push run, by ~9 seconds. | |
| # | |
| # Measured on the merge of #1151 (ddd74da4): run 31045325182, event `push`, | |
| # CANCELLED; run 31045337494, event `pull_request`, survived. Same pattern on | |
| # 3b9371f1, 75190bca and f2219e09 — four for four, so the push run on | |
| # `development` has never reached a verdict. "Coverage Baseline Check" therefore | |
| # reported `skipped` on the surviving run (correctly — it is push-only) while | |
| # never once executing on the run that could have run it. #1151 enabled a job | |
| # that could not reach a verdict: a dead gate of the permanently-pending shape, | |
| # which is invisible because a skipped job renders like a passing one. | |
| # | |
| # Suffixing only the default-branch push keeps feature-branch dedup untouched | |
| # (`quality-feature/x` for both events, exactly as before) and gives the two | |
| # default branches' push runs a lane of their own. | |
| concurrency: | |
| # SUFFIXED BY EVENT NAME, not just by `-push`. | |
| # | |
| # The previous expression gave a push on `development` its own lane | |
| # (`-push`) but left EVERYTHING ELSE sharing `quality-development` — and | |
| # that is not a quiet lane: `Sync to Beta` keeps a PR open whose head_ref | |
| # IS `development`, so its run computes the same group and is re-triggered | |
| # on every merge. | |
| # | |
| # A `workflow_dispatch` therefore shared a group with that PR and was | |
| # cancelled by it. Measured on shillinq 2026-08-21: dispatch 32487948678 | |
| # cancelled by pull_request run 32490160836 (head_branch `development`). | |
| # A run someone deliberately asked for could essentially never complete. | |
| # | |
| # That reaches past ad-hoc verification: the fleet gate-drift sweep | |
| # (.github#523) dispatches per app with `--ref development`, because | |
| # `schedule:` cannot choose a branch. Under the old group those runs are | |
| # cancelled and report neither pass nor fail — and a routine that produces | |
| # no verdict is indistinguishable from one that never ran. | |
| # | |
| # This is hermiq's form, already live there. Pull requests keep the bare | |
| # group (so a PR still supersedes its own earlier run); push, dispatch and | |
| # schedule each get their own lane. | |
| # | |
| # THE BRANCH RESTRICTION IS GONE, because it contradicted the sentence above. | |
| # | |
| # The suffix used to apply only when `ref_name` was `main` or `development`, | |
| # so on every OTHER branch push and pull_request computed the SAME group — | |
| # and `cancel-in-progress` made them kill each other. That became reachable | |
| # when the push allow-list widened on 2026-08-14 to include `feat/**`, | |
| # `fix/**`, `perf/**`, `refactor/**` and `chore/**`: those branches now get | |
| # both a push run and a pull_request run for one commit. | |
| # | |
| # `quality / Quality Report` is a `needs:`-gated aggregator and reports | |
| # FAILURE when its dependencies are CANCELLED, so the collision showed up as | |
| # a red gate on a PR that had never actually been evaluated — and re-running | |
| # collided the same way. See .github#563. | |
| # | |
| # A branch name is not a unique lane when two event types can each produce a | |
| # run for it, so the event is now always part of the key. | |
| group: quality-${{ github.head_ref || github.ref_name }}${{ github.event_name != 'pull_request' && format('-{0}', github.event_name) || '' }} | |
| # PUSH RUNS ARE NOT CANCELLED — and this has to be said HERE, not only in the | |
| # shared workflow. .github#597 set `cancel-in-progress` on quality.yml itself, | |
| # but a caller's own concurrency cancels the whole run before the called | |
| # workflow's setting can apply, so that fix reached only the apps that declare | |
| # no concurrency of their own. Measured 2026-08-28 over push runs on | |
| # `development` since #597: 0 of 11 cancelled where the caller was silent, 7 of | |
| # 13 (54%) cancelled where the caller still said `true`. | |
| # | |
| # An integration branch needs a verdict per commit: the run being cancelled is | |
| # the only thing that would have said whether what just landed is sound, and | |
| # its replacement is cancelled too. `pull_request` keeps cancelling, where | |
| # superseding really is correct. | |
| cancel-in-progress: ${{ github.event_name != 'push' }} | |
| # Permission CEILING for the called quality pipeline. GitHub statically | |
| # validates the called workflow's declared job permissions against this | |
| # grant — even for jobs that are disabled — so capping any of these at | |
| # `read` makes the whole call fail to START (zero jobs, no annotations). | |
| # Union of what the nested jobs need: journeydoc-capture (contents+actions | |
| # write), update-baseline / features-extract (contents write), the Quality | |
| # Report PR comment (issues / pull-requests write), and packages: read for | |
| # pulling org images. | |
| permissions: | |
| contents: write | |
| actions: write | |
| issues: write | |
| pull-requests: write | |
| packages: read | |
| jobs: | |
| quality: | |
| # Skip the duplicate run a branch-creation push fires alongside the PR event. | |
| if: github.event_name != 'push' || github.event.created != true | |
| uses: ConductionNL/.github/.github/workflows/quality.yml@main | |
| with: | |
| app-name: integriq | |
| # composer.json `config.platform.php` is pinned to 8.3, so phpcs/phpmd/etc | |
| # need the matching runtime or `vendor/composer/platform_check.php` aborts | |
| # with `Composer dependencies require a PHP version ">= 8.3.0"`. | |
| php-version: "8.3" | |
| # Set explicitly, because the shared workflow defaults to | |
| # `["stable31", "stable32"]` and stable31 CANNOT WORK here. integriq | |
| # declares <app>openregister</app> as a hard dependency, and openregister | |
| # declares min-version="32". On NC31 the E2E job logs | |
| # App "Open Register" cannot be installed because it is not compatible | |
| # with this version of the server. | |
| # then continues with a WARNING, so the run proceeds without its central | |
| # dependency and fails ~70s later on missing tables — which reads like a | |
| # migration fault rather than a missing app. Testing a version the app | |
| # cannot support produces red that says nothing. See issue #1172. | |
| # | |
| # WHY THIS IS NOW stable34 AND NOT `["stable32", "stable33"]`. This PR | |
| # moves the dev requirement to `nextcloud/ocp ^34`, so the stubs every | |
| # static-analysis leg resolves against are NC34's. Running the suite | |
| # against an older server would check the app under one set of API | |
| # signatures and analyse it under another, and the two disagreeing is | |
| # exactly the class of defect the analysers exist to find. | |
| # | |
| # ✅ THE NARROWING IS CLOSED, THE FIRST WAY. The previous revision of this | |
| # comment recorded a deliberate trade: info.xml declared three server | |
| # versions while the suite exercised one, leaving NC32 and NC33 unmeasured | |
| # rather than known-broken. It named its own closure condition — "restoring | |
| # the older refs alongside stable34 once ocp ^34 stubs are proven compatible | |
| # with them" — and that condition is now met: portaliq runs stable32, | |
| # stable33 and stable34 against `nextcloud/ocp ^34` and all six PHPUnit | |
| # legs pass (run 31599055849). So the refs come back rather than info.xml's | |
| # floor going up, and the two numbers agree again. | |
| nextcloud-test-refs: '["stable34", "stable32", "stable33"]' | |
| # `hydra-gates-require-full-coverage: false` is REMOVED here, exactly as | |
| # the comment it replaced instructed: "Remove this line in the change | |
| # that wires those producers up, so the coverage requirement arrives with | |
| # the coverage." | |
| # | |
| # WHAT THE FLAG ACTUALLY DID. It is not a gate and it does not decide any | |
| # finding. It answers one question: when a gate DID NOT RUN, is that a | |
| # failure? The gate package sorts every non-run into three categories — | |
| # `na` (subject matter absent: nothing is unverified), `structural` | |
| # (subject matter EXISTS, nothing produced the gate's input) and `wiring` | |
| # (the gate's own helper or tool is missing). Only `structural` and | |
| # `wiring` count against coverage. With the flag set to false, a gate | |
| # whose machinery had quietly stopped existing reported as green. | |
| # | |
| # That is not hypothetical in this fleet: a missing helper made gate-7 | |
| # report PASS over 11 real unguarded IDOR endpoints, and openregister | |
| # reported `success` while its own log read "EXCEPT GATES 4 33, WHICH DID | |
| # NOT RUN". A waiver here means integriq's green does not include | |
| # the coverage assertion at all. | |
| # | |
| # THE ORIGINAL DEFERRAL NO LONGER HOLDS, and not for the reason this PR | |
| # first gave. Re-measured on `development` @5ab1c9df, FULL TREE, with the | |
| # gate package cloned fresh from ConductionNL/.github@main (756fe89) — | |
| # not the local submodule, which was 38 commits behind and is therefore a | |
| # different program: | |
| # | |
| # [hydra-gates] COVERAGE: 63 of 64 declared gates reported a result | |
| # (1 not applicable to this repo/diff; 63 of 63 applicable gates ran). | |
| # | |
| # ZERO structural skips, ZERO wiring skips. The single not-applicable was | |
| # gate-33 axe-core, which declared itself so because this caller had not | |
| # set `enable-axe`. | |
| # | |
| # THAT IS NO LONGER TRUE: `enable-axe: true` is set below, and gate-33 | |
| # now reports a real verdict instead of `na`. CONFIRMED IN CI on the PR | |
| # that turned it on (#1211, job 93785212530): | |
| # | |
| # [gate-33] axe-core: PASS | |
| # [hydra-gates] gate-33 axe-core: report read — 0 violation(s) | |
| # present, 0 serious/critical. A PASS here is a PASS | |
| # over that number, not over silence. | |
| # | |
| # "report read" is the part that matters: the report survived the | |
| # provenance check rather than being rejected and deleted, which is the | |
| # failure mode where gate-33 goes back to skipping loudly. | |
| # | |
| # DO NOT READ THE COVERAGE LINE AS A CONSTANT. An earlier draft of this | |
| # comment predicted it would move from `63 of 64` to `64 of 64`. That | |
| # was wrong, and wrong in the way this fleet keeps getting caught by: | |
| # the count is a function of the DIFF, not of the repo. The same run | |
| # that produced the PASS above printed | |
| # | |
| # [hydra-gates] COVERAGE: 28 of 64 declared gates reported a result | |
| # (36 not applicable to this repo/diff; 28 of 28 applicable gates ran). | |
| # | |
| # because a two-file PR gives 36 gates no subject matter. The `63 of 64` | |
| # figure quoted higher up came from a FULL-SCOPE run. Compare like with | |
| # like or the number means nothing. What is genuinely comparable is that | |
| # gate-33 is now inside the applicable set in both scopes instead of | |
| # declaring itself `na`. | |
| # | |
| # ⚠️ AND THE SAME CAVEAT USED TO APPLY TO A GATE THAT PRINTS `PASS`. | |
| # gate-16 spec-coverage takes its base from `HYDRA_GATE_BASE_REF`, and | |
| # the runner supplied none on `--full`, so a full-scope run ON | |
| # `development` diffed the branch against itself, inspected nothing, and | |
| # printed PASS — a PASS that COUNTED toward "N of N applicable gates | |
| # ran" (ConductionNL/.github#361). **#364 has since MERGED**: a | |
| # full-repo run with no diff now reports | |
| # `NOT APPLICABLE — full-repo run computed NO diff … This is NOT a | |
| # pass.` So do not read a historical green gate-16 cell on this repo as | |
| # a measurement — re-measure with an explicit base. | |
| # | |
| # Measured here on `development` @7c1d9c6d with the canonical package | |
| # @81c8c97 (which still carried the old behaviour), changing ONLY the | |
| # base: | |
| # | |
| # base origin/development (= what CI's --full run uses) -> count=0 | |
| # base origin/beta -> count=0 | |
| # base origin/main -> count=30 | |
| # --mode report (whole tree, no diff at all) -> 30 | |
| # | |
| # A committed, untagged public method planted in lib/Service and a | |
| # matching Vue method moved every one of those numbers by exactly +2 and | |
| # named both plants, so the zeros above are real zeros and not a dead | |
| # gate. integriq's genuine gate-16 debt is 30 methods, all backend. | |
| # | |
| # Gate 4 was never starved by a producer this repo declines to switch | |
| # on; it declares itself not applicable. And gate-24 | |
| # integration-parity — named by the first version of this comment as the | |
| # real gap, which it then was — now PASSES: `scripts/check-integration- | |
| # parity.sh` landed on `development` after this PR was opened, so the | |
| # parity script arrived exactly as that comment said it should, ahead of | |
| # the flag being dropped. | |
| # | |
| # PROOF THIS SETTING CAN STILL FAIL, rather than being inert. On the same | |
| # tree, deleting `scripts/check-integration-parity.sh`: | |
| # | |
| # [gate-24] integration-parity: SKIPPED (structural) — ... this repo | |
| # DOES register integration leaves ... server↔JS leaf parity is | |
| # UNVERIFIED | |
| # [hydra-gates] COVERAGE: 62 of 63 applicable gates ran | |
| # | |
| # which is precisely the shape the runner exits 98 on, and which | |
| # quality.yml turns into `::error::hydra-gates passed every gate that | |
| # ran, but a gate whose SUBJECT MATTER EXISTS did not report`. So this | |
| # repo has live subject matter for the assertion; it is not being turned | |
| # on over nothing. | |
| # | |
| # NOTE ON WHAT THIS DOES *NOT* CHANGE. Exit 98 is reached only when no | |
| # gate FAILED — a run with failures is already red and exits with the | |
| # failure count. Removing this line therefore adds a verdict where there | |
| # was none; it cannot mask one. The same full-tree run still shows | |
| # gate-38 and gate-57 failing on inherited debt in files no PR is | |
| # touching; both are tracked separately and neither is in scope for a | |
| # diff-scoped CI run. | |
| enable-phpcs: true | |
| enable-psalm: true | |
| enable-phpstan: true | |
| enable-phpmetrics: true | |
| enable-frontend: true | |
| enable-eslint: true | |
| # Build the bundle in CI. Nothing here did until now, and the gap hid two | |
| # real defects for weeks: @nextcloud/dialogs pinned at a vue@2.7 release | |
| # inside a Vue 3 app, and fifty NcSelect/NcTextArea listeners still on the | |
| # v8 `@input` contract that v9 does not emit. Both compile, lint and pass | |
| # unit tests — only an actual build (and, for the listeners, using the | |
| # control) shows them. | |
| # | |
| # A broken bundle is also silent in production: Nextcloud serves whatever | |
| # js/ was last committed, so the app keeps working from a stale build | |
| # while main cannot be rebuilt at all. | |
| # `check:specs` and `test:l10n` are added to this list below rather than | |
| # here; see the "Frontend Check legs" block further down. The value lives | |
| # in exactly one place — a second `frontend-checks:` key in the same | |
| # `with:` block would be accepted last-one-wins by every YAML parser | |
| # involved and would read as configured while being decided elsewhere. | |
| # Integration + E2E run against a live Nextcloud server with OR | |
| # checked out as an additional app (collections + journeys exercise | |
| # /index.php/apps/openregister/api/objects/integriq/*). | |
| enable-newman: true | |
| newman-collection-path: "tests/postman" | |
| newman-environment-path: "tests/postman/integriq.postman_environment.json" | |
| # WHY THIS SEED EXISTS. The collection's "01 — Fixture setup (via OR)" | |
| # folder POSTs to /apps/openregister/api/objects/integriq/<schema>; | |
| # since the chain-C cutover every integriq entity is an OpenRegister | |
| # object. Without a seed step the `openconnector` register does not | |
| # exist, all 8 fixture POSTs return 404, `{{fxSourceUuid}}` is never set, | |
| # and every downstream request then 404s on the literal placeholder — | |
| # 51 of 107 assertions (measured, run 30816264169). SEED_SCOPE=register | |
| # reuses the Playwright job's tests/e2e/ci-seed.sh and stops after the | |
| # register import, skipping the SPA warm-up and bundle gate that this | |
| # job (which never runs `npm run build`) must not execute. | |
| # | |
| # This was held back until ConductionNL/.github#132 landed: the shared | |
| # workflow used to start this job's `php -S` SINGLE-WORKER, and journey | |
| # J2 points a Source at this very instance, so the app called back into | |
| # the server while the one worker was still inside the outer request — | |
| # a deadlock that hung the job until timeout-minutes (run 30821823343). | |
| # #132 sets PHP_CLI_SERVER_WORKERS=8 on this job's server, which is what | |
| # makes the self-referential fixtures serveable. Merged 2026-08-03. | |
| newman-seed-command: 'SEED_SCOPE=register bash apps/integriq/tests/e2e/ci-seed.sh' | |
| additional-apps: '[{"repo":"ConductionNL/openregister","app":"openregister","ref":"development"}]' | |
| # ── E2E browser tests ──────────────────────────────────────────────── | |
| # `playwright-test-path` does double duty in the shared workflow: | |
| # 1. it is the directory the "Validate Playwright tests exist" step | |
| # counts *.spec.ts in; | |
| # 2. it is the FIRST place the run step looks for a config — | |
| # `${playwright-test-path}/playwright.config.ts`, falling back to | |
| # the repo root only if that file is absent. | |
| # It used to say `tests/e2e/regression`, which holds no config, so lookup | |
| # (2) fell through to the ROOT config. The run step passes no | |
| # `--project`, so all four root projects ran at once — 243 tests in 1.6h, | |
| # including `visual` (pixel baselines the root config itself documents as | |
| # un-matchable on a CI Linux runner) and `docs-capture` (screenshot | |
| # re-shoots that have their own dedicated job). `tests/e2e` makes lookup | |
| # (1) hit tests/e2e/playwright.config.ts, which declares exactly one | |
| # project over spec-coverage/ + regression/ + workflows/. | |
| enable-playwright: true | |
| playwright-test-path: tests/e2e | |
| # integriq's sources/mappings/synchronizations/jobs are OpenRegister | |
| # OBJECTS — there is no `oc_openconnector_*` table for them — so with no | |
| # `openconnector` register the SPA has nothing to resolve and the suite | |
| # reports it as a wall of selector timeouts. `occ app:enable` is not a | |
| # reliable provisioning path for it: the InitializeRegister repair step | |
| # swallows its own failures as warnings and occ still exits 0. The script | |
| # provisions explicitly and fails loudly when the register or its schemas | |
| # are still absent. cwd for this step is the Nextcloud server root. | |
| playwright-seed-command: 'bash apps/integriq/tests/e2e/ci-seed.sh' | |
| # ── PHPUnit ────────────────────────────────────────────────────────── | |
| # `enable-phpunit` defaults to FALSE in the shared workflow and this | |
| # caller never set it, so the job reported `skipped` — with its matrix | |
| # placeholders still literal (`PHPUnit (PHP ${{ matrix.php-version }}, | |
| # NC ${{ matrix.nextcloud-ref }})`), because GitHub evaluates a matrix | |
| # job's `if:` before expanding the matrix. A disabled PHPUnit therefore | |
| # looks exactly like a repo with no PHP tests. This repo has phpunit.xml | |
| # and a tests/ tree, and a `coverage-report` artifact from 2026-08-03 | |
| # proves the suite does run: 20141 of 33162 statements covered. | |
| # This is also the prerequisite for the coverage ratchet — the | |
| # "Coverage Baseline Check" job `needs: phpunit` and downloads its | |
| # artifact, so enabling the guard without this would be a dead gate of a | |
| # different shape. | |
| enable-phpunit: true | |
| # ── Frontend Check legs ────────────────────────────────────────────── | |
| # `frontend-checks` was already non-empty (`["build"]`), so the job did | |
| # run here — but only the build. The validator family ran nowhere. | |
| # `check:specs` is the aggregate (json-strict + manifest + register) and | |
| # is listed as ONE leg rather than three, because each leg is a fresh job | |
| # with its own checkout + `npm ci`. | |
| # Measured on this tree before enabling: `check:specs` PASSES and | |
| # `test:l10n` PASSES. | |
| # `test:l10n:parity` is deliberately NOT added: measured on this tree it | |
| # is short 337+ translations across the required locales. That is a | |
| # translation backlog, and a permanently-red leg is one that gets | |
| # switched off again. | |
| # | |
| # `format` (prettier --check) is listed because the shared workflow has NO | |
| # prettier job of its own — `quality.yml` runs eslint and stylelint and | |
| # mentions prettier ZERO times. This repo already carries | |
| # `@nextcloud/prettier-config` and a `format` script, so without this leg | |
| # `npm run format` never runs outside a developer's editor and the tree | |
| # drifts straight back out of format between merges — the same inert- | |
| # formatter failure mode that made the old `.prettierrc` worth deleting. | |
| # Centralising the config never stopped drift; the gate does. | |
| # Measured on this tree before enabling: PASSES, 176 of 222 tracked | |
| # frontend files in scope (l10n/ and docs/ excluded via .prettierignore / | |
| # .gitignore, which prettier 3 also reads). | |
| # `check:schema-l10n` is a RATCHET, not a gate. Every string inside a form | |
| # comes from the schema and is a key in THIS app's catalogue; an absent key | |
| # renders the English source inside an otherwise translated form, silently. | |
| # The fleet had 30,459 such strings, so this records the current count and | |
| # fails only when it GROWS — burning it down stays an ordinary PR. | |
| frontend-checks: '["build", "check:specs", "test:l10n", "format", "check:schema-l10n", "check:l10n-js"]' | |
| # ── Hydra mechanical gates ─────────────────────────────────────────── | |
| # `enable-hydra-gates` defaults to FALSE, so this tier has never executed | |
| # here — the job reported `skipped`, which the Quality Report renders | |
| # identically to a pass. | |
| enable-hydra-gates: true | |
| # No `hydra-gates-ref` here on purpose. The shared workflow defaults it | |
| # to @main, and this workflow is itself consumed at @main, so the two | |
| # sides move together and a gate fix reaches this repo without a commit | |
| # in this repo. A pin is a silent expiry date: 22 repos sat on v1.0.1 and | |
| # 16 gates were dead fleet-wide while every one reported PASS (.github#159), | |
| # and a default flipped at @main later reached those old runners and made | |
| # them red on gates they had no subject matter for (.github#173). | |
| # To hold this repo still for a specific reason, set the input explicitly | |
| # and say why — it is still honoured. To roll back for everyone, revert on | |
| # ConductionNL/.github main. | |
| # | |
| # THIRD CAUSE, and the one that is failing this repo RIGHT NOW | |
| # (.github#177): quality.yml@main began executing three gate helpers BY | |
| # NAME — check_spec_anchors.py, check_form_labels.py and | |
| # check_license_triangle.py — which exist in NO tag before v1.5.0. | |
| # Verified by DIRECTORY LISTING of each tag, not by per-file lookups: | |
| # those answered "present" uniformly across v1.0.0..v1.5.0, and the | |
| # uniformity across independent inputs was the tell that the instrument | |
| # was wrong. So the Hydra Gates job here fails at "Verify the pinned gates | |
| # package satisfies this workflow", before a single gate runs, with the | |
| # workflow's own words: "This is NOT a code-quality finding about your | |
| # repository." Removing the pin is the repair. | |
| # | |
| # Unpinning also picks up v1.5.1's push scoping (.github#179): on a push to | |
| # `development`, `origin/development` IS `HEAD`, so the diff was empty by | |
| # construction — <= v1.4.0 passed over it (permanently green) and v1.5.0 | |
| # refused with exit 99 (permanently red). The scope is now | |
| # `github.event.before...HEAD`, what the push actually changed. | |
| # ── axe-core (gate-33) ─────────────────────────────────────────────── | |
| # `enable-axe` used to be deliberately unset here, on the grounds that a | |
| # vanilla Nextcloud 34 reports serious/critical violations on core's OWN | |
| # routes that DOM scoping does not remove. That reasoning is now out of | |
| # date in the part that mattered: `axe-include-selector` defaults to | |
| # `#content-vue, #content`, which scopes the analysis to the app's own | |
| # rendered DOM, and the runner proves on every run that the scope is a | |
| # SCOPE and not a MUTE (it injects a violation inside the container and | |
| # asserts it is reported, and one outside and asserts it is not). | |
| # | |
| # So this is turned on against a MEASUREMENT, not a hope. Run locally | |
| # with the canonical runner (ConductionNL/.github@main b8c7ead, | |
| # @axe-core/playwright 4.12.1) against a dedicated Nextcloud 34 with | |
| # openregister + integriq installed and the register seeded: | |
| # | |
| # /index.php/apps/integriq/ HTTP 200 passes=19 violations=0 | |
| # /index.php/apps/integriq/#/sources HTTP 200 passes=17 violations=0 | |
| # | |
| # with `axe self-test OK` and `axe scope control OK` on both. Zero | |
| # violations at any impact, so zero serious/critical — gate-33 has | |
| # nothing to fail on today. | |
| enable-axe: true | |
| # ONE ROUTE, AND THAT IS A RUNNER DEFECT, NOT A CHOICE — see | |
| # ConductionNL/.github#351. | |
| # | |
| # `axe-run.cjs` treats a null `page.goto()` response as HTTP 0 and dies. | |
| # A fragment-only navigation returns null because no document is fetched, | |
| # so for a hash-routed SPA — which this app is (`createWebHashHistory()`) | |
| # — every route after the first one shares a document with it and is | |
| # reported as `returned HTTP 0. … Set the axe-routes input to routes this | |
| # app actually serves.` The route is served fine; only its POSITION in | |
| # the list decides. Proven both ways on the same instance: `#/sources` | |
| # listed FIRST analysed cleanly at HTTP 200; the SAME route listed after | |
| # the app root killed the run with exit 2 and wrote no report at all. | |
| # | |
| # That is the trap in this input: the error blames the caller, so the | |
| # obvious response is to delete routes until it goes green, which | |
| # silently narrows accessibility coverage to the landing page and reads | |
| # as a config fix. Recording it here so the next person widens the list | |
| # by fixing #351 rather than by guessing. | |
| # | |
| # Until #351 lands this is the app root only. It is a real verdict on a | |
| # real page — gate-33 has reported SKIPPED in this repo since it was | |
| # written — but it is one page, not the app. | |
| axe-routes: "/index.php/apps/integriq/" | |
| # ── Coverage ratchet ───────────────────────────────────────────────── | |
| # `enable-coverage-guard` defaults to FALSE in the shared workflow, so | |
| # BOTH coverage jobs — "Coverage Baseline Protection" (PR) and "Coverage | |
| # Baseline Check" (push) — have only ever reported `skipped` here. A | |
| # skipped job renders in the Quality Report exactly like a passing one, | |
| # so the ratchet read as present while checking nothing. | |
| # | |
| # Turning it on needs two files this repo did not have, both now added | |
| # alongside this line: | |
| # * `scripts/coverage-guard.php` — copied BYTE-IDENTICAL from | |
| # openregister (the same copy softwarecatalog took), so there is one | |
| # implementation of the ratchet across the fleet rather than five. | |
| # * `.coverage-baseline` — a bare number. It must EQUAL what CI | |
| # measures on development, not approximate it: too high and the | |
| # "Guard coverage baseline" step inside PHPUnit exits 1; too low and | |
| # the push-side Coverage Baseline Check recomputes a higher value, | |
| # finds `git diff` non-empty, and fails demanding the new number be | |
| # committed. There is no safe margin in either direction. | |
| # The committed value is the one CI printed, recorded as measured. | |
| # | |
| # This was held back until `enable-phpunit` above had produced a | |
| # `coverage-report` artifact from this repo, since the guard reads the | |
| # project-level metrics out of that job's clover.xml. | |
| enable-coverage-guard: true |