From b8a3621753fe5a19a7e19f9886275452e6cccc96 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 4 Aug 2026 16:46:18 +0200 Subject: [PATCH 1/2] ci(quality): enable the Code Quality gates this repo was silently skipping A skipped job and a passing job are indistinguishable in the Quality Report. Every gate turned on here reported 'skipped' in every run. Each newly-enabled leg was measured against this tree BEFORE being enabled; the results are in the PR description. Legs that were measured failing are enabled anyway - the defects are pre-existing, and the only thing that changed is that CI can now see them. Journeydoc Capture and enable-axe are deliberately NOT enabled. --- .coverage-baseline | 1 + .github/workflows/code-quality.yml | 46 +++++++++++++++++++++++ scripts/coverage-guard.php | 59 ++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 .coverage-baseline create mode 100755 scripts/coverage-guard.php diff --git a/.coverage-baseline b/.coverage-baseline new file mode 100644 index 00000000..13e0537c --- /dev/null +++ b/.coverage-baseline @@ -0,0 +1 @@ +13.01 diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index dae84286..4e547517 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -63,3 +63,49 @@ jobs: # schemas, or the app's own register→schema mapping still aren't there. # cwd for this step is the Nextcloud server root. playwright-seed-command: 'bash apps/softwarecatalog/tests/e2e/ci-seed.sh' + + # ── Integration tests ──────────────────────────────────────────────── + # Was explicitly `false`. `newman-collection-path` already points at + # `tests`, and tests/magic-mapper-import.postman_collection.json sits at + # exactly that level — which matters, because the validator counts + # collections RECURSIVELY (`find`) while the run step globs them FLAT + # (`for collection in *.postman_collection.json` after `cd`). A path that + # only satisfies the validator would pass validation and then run nothing. + # This one satisfies both. + enable-newman: true + + # ── Frontend Check legs ────────────────────────────────────────────── + # `frontend-checks` defaults to `[]`, and an empty list means the shared + # workflow emits NO "Frontend Check" job at all — so these three + # validators ran nowhere while the run still looked complete. + # Measured on this tree before enabling: ALL THREE PASS. They are enabled + # to keep them passing, not because they are currently broken. + # `test:l10n:parity` is deliberately NOT added: measured on this tree it + # is short 404+ translations across the required locales. That is a + # translation backlog, and a permanently-red leg is one that gets switched + # off again. + # `test` / `test:unit` are NOT listed: "Frontend Tests (unit)" runs them. + frontend-checks: '["check:manifest", "check:vue-demi", "test:l10n"]' + + # ── Coverage ratchet ───────────────────────────────────────────────── + # `enable-coverage-guard` defaults to FALSE, which is why both + # "Coverage Baseline Protection" and "Coverage Baseline Check" have only + # ever reported `skipped`. It needs two inputs this repo did not have, + # both added in this commit: `scripts/coverage-guard.php` (byte-identical + # to openregister's) and `.coverage-baseline` = 13.01, this repo's own + # measured coverage (4077 of 31330 statements) read from clover.xml in the + # `coverage-report` artifact of run 30911570224. That is the lowest figure + # in the fleet by a wide margin and is recorded here as measured, not + # rounded up: the ratchet's job is to stop it falling further, and a + # baseline set above the truth would fail on the first honest run. + enable-coverage-guard: true + + # ── 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. Pinned to v1.0.1 so a change to the gate package + # cannot move this repo's verdict without a commit here. + # `enable-axe` deliberately NOT set: a vanilla Nextcloud 34 already carries + # serious/critical violations from core's own UI. + enable-hydra-gates: true + hydra-gates-ref: v1.0.1 diff --git a/scripts/coverage-guard.php b/scripts/coverage-guard.php new file mode 100755 index 00000000..a600aba7 --- /dev/null +++ b/scripts/coverage-guard.php @@ -0,0 +1,59 @@ +#!/usr/bin/env php + [--update-baseline] + * + * Exit codes: + * 0 — coverage is equal to or higher than baseline + * 1 — coverage dropped (PR should be blocked) + * 2 — missing files or invalid input + */ + +$baselineFile = __DIR__ . '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/../.coverage-baseline'; +$cloverFile = $argv[1] ?? 'coverage/clover.xml'; +$updateBaseline = in_array('--update-baseline', $argv, true); + +if (!file_exists($cloverFile)) { + fwrite(STDERR, "Error: Clover file not found: $cloverFile\n"); + exit(2); +} + +if (!file_exists($baselineFile)) { + fwrite(STDERR, "Error: Baseline file not found: $baselineFile\n"); + exit(2); +} + +$xml = simplexml_load_file($cloverFile); +if ($xml === false) { + fwrite(STDERR, "Error: Could not parse $cloverFile\n"); + exit(2); +} + +$metrics = $xml->project->metrics; +$statements = (int)$metrics['statements']; +$covered = (int)$metrics['coveredstatements']; +$current = $statements > 0 ? round(($covered / $statements) * 100, 2) : 0.0; + +$baseline = (float)trim(file_get_contents($baselineFile)); + +echo "Coverage baseline: {$baseline}%\n"; +echo "Coverage current: {$current}%\n"; + +if ($current < $baseline) { + echo "FAIL: Coverage dropped by " . round($baseline - $current, 2) . "%\n"; + exit(1); +} + +if ($current > $baseline) { + echo "Coverage improved by " . round($current - $baseline, 2) . "%\n"; + if ($updateBaseline) { + file_put_contents($baselineFile, number_format($current, 2) . "\n"); + echo "Baseline updated to {$current}%\n"; + } +} else { + echo "Coverage unchanged.\n"; +} + +exit(0); From efc68e31f73c0a0747865b6cc9ab84b4cbca10be Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Tue, 4 Aug 2026 16:49:55 +0200 Subject: [PATCH 2/2] ci(quality): remove duplicate with: keys; respect documented newman opt-outs A second key in the same with: block is accepted last-one-wins by every YAML parser involved, so the file reads as configured while the value is decided somewhere else in it. Where enable-newman: false already carried a specific, still-live reason (OR React/Async await + SQLite REGEXP; ZGW API incomplete; collections hard-coding non-CI URLs and unseeded registers), it is left off and the reason is quoted rather than overridden. Where it was a bare line with no comment, the stale line is removed instead of shadowed. --- .github/workflows/code-quality.yml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 4e547517..202b0293 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -64,15 +64,22 @@ jobs: # cwd for this step is the Nextcloud server root. playwright-seed-command: 'bash apps/softwarecatalog/tests/e2e/ci-seed.sh' - # ── Integration tests ──────────────────────────────────────────────── - # Was explicitly `false`. `newman-collection-path` already points at - # `tests`, and tests/magic-mapper-import.postman_collection.json sits at - # exactly that level — which matters, because the validator counts - # collections RECURSIVELY (`find`) while the run step globs them FLAT - # (`for collection in *.postman_collection.json` after `cd`). A path that - # only satisfies the validator would pass validation and then run nothing. - # This one satisfies both. - enable-newman: true + # Integration Tests (Newman) stays OFF here, deliberately. The + # `enable-newman: false` further up is not a default nobody chose: it + # records that the collection's requests hard-code + # `/var/www/html/custom_apps/openregister/...` paths that do not exist on + # a CI runner (Nextcloud lives under `server/apps/`, no rewrite), and that + # it assumes a seeded `voorzieningen` register / `module` schema. Turning + # it on would produce a guaranteed red whose cause is already written + # down. Flipped back on in the commit that rewrites the collection to the + # CI base_url and adds the seed step. + # + # Noted while checking this, because it is the kind of thing that reads as + # working when it is not: the validator counts collections RECURSIVELY + # (`find`) while the run step globs them FLAT (`for collection in + # *.postman_collection.json` after `cd`). A `newman-collection-path` that + # only satisfies the validator passes validation and then runs nothing. + # The configured `tests` path does satisfy both here. # ── Frontend Check legs ────────────────────────────────────────────── # `frontend-checks` defaults to `[]`, and an empty list means the shared