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..202b0293 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -63,3 +63,56 @@ 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 (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 + # 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);