fix: audit repository scope coverage #49

Merged
andres merged 2 commits from build/48-scope-coverage into main 2026-09-01 14:16:53 +00:00
4 changed files with 26 additions and 21 deletions
Showing only changes of commit 112f946802 - Show all commits

6
.github/labeler.yml vendored
View file

@ -4,13 +4,13 @@
- any-glob-to-any-file: ["src/**"]
"scope:packaging":
- changed-files:
- any-glob-to-any-file: ["scripts/**", ".forgejo/workflows/release.yml"]
- any-glob-to-any-file: ["scripts/**", ".forgejo/workflows/release.yml", "package.json", "package-lock.json", "CHANGELOG.md", "changelog.d/**"]
"scope:manifests":
- changed-files:
- any-glob-to-any-file: ["manifests/**"]
"scope:ci":
- changed-files:
- any-glob-to-any-file: [".forgejo/workflows/**"]
- any-glob-to-any-file: [".forgejo/workflows/**", ".github/**", ".ceremony/**"]
"scope:docs":
- changed-files:
- any-glob-to-any-file: ["README.md", "docs/**"]
- any-glob-to-any-file: ["*.md", "docs/**"]

6
.github/labels.conf vendored
View file

@ -1,7 +1,7 @@
panel=codex-bot-andresmgsl glm-bot-andresmgsl claude-bot-andresmgsl kimi-bot-andresmgsl
triage-actors=claude-bot-andresmgsl
scope:cli|C5DEF5|src/ — the command surface (cli.js, api.js, config.js)
scope:packaging|C5DEF5|scripts/ and the release workflow — deb build, registry publish, apt install path
scope:packaging|C5DEF5|scripts/, release workflow, package manifests, changelog, and fragments — release packaging and version surfaces
scope:manifests|C5DEF5|manifests/ — the fleet repo registry data
scope:ci|C5DEF5|.forgejo/workflows/ — the test and label gates
scope:docs|C5DEF5|README and docs/ — the prose contract
scope:ci|C5DEF5|.forgejo/workflows/, .github/, and .ceremony/ — the test, label, and governance gates
scope:docs|C5DEF5|root Markdown and docs/ — the prose contract

View file

@ -20,10 +20,10 @@ authoritative; this table is its human-readable restatement.
| Scope | Covers |
| --- | --- |
| `scope:cli` | `src/` — the command surface (`cli.js`, `api.js`, and `config.js`) |
| `scope:packaging` | `scripts/` and the release workflow — Debian package builds, registry publishing, and the APT install path |
| `scope:packaging` | `scripts/`, release workflow, package manifests, changelog, and fragments — release packaging and version surfaces |
| `scope:manifests` | `manifests/` — fleet repository registry data |
| `scope:ci` | `.forgejo/workflows/` — test and label gates |
| `scope:docs` | `README.md` and `docs/` — the prose contract |
| `scope:ci` | `.forgejo/workflows/`, `.github/`, and `.ceremony/` — the test, label, and governance gates |
| `scope:docs` | root Markdown and `docs/` — the prose contract |
These names and path descriptions restate the scope rows in
[`.github/labels.conf`](.github/labels.conf).

View file

@ -1,6 +1,7 @@
const { test } = require('node:test');
const assert = require('node:assert/strict');
const { spawn } = require('node:child_process');
const { execFileSync } = require('node:child_process');
const fs = require('node:fs');
const http = require('node:http');
const os = require('node:os');
@ -120,20 +121,24 @@ test('repository governance config resolves the current four-member panel and fi
});
});
test('repository scope mapping covers every configured scope with the ruled paths', () => {
test('repository scope mapping covers every tracked path except the ruled residue', () => {
const labeler = fs.readFileSync(REPOSITORY_LABELER, 'utf8');
const expected = {
'scope:cli': ['src/**'],
'scope:packaging': ['scripts/**', '.forgejo/workflows/release.yml'],
'scope:manifests': ['manifests/**'],
'scope:ci': ['.forgejo/workflows/**'],
'scope:docs': ['README.md', 'docs/**'],
};
const globs = [...labeler.matchAll(/any-glob-to-any-file:\s*(\[[^\n]+\])/g)]
.flatMap((match) => JSON.parse(match[1]));
const tracked = execFileSync('git', ['ls-files'], {
cwd: path.join(__dirname, '..'),
encoding: 'utf8',
}).trim().split('\n');
const allowed = new Set([
'.gitignore',
'assets/logo-mark.svg',
...tracked.filter((filename) => /^test\/[^/]+\.test\.js$/.test(filename)),
]);
const uncovered = tracked.filter((filename) => (
!allowed.has(filename) && !globs.some((glob) => path.matchesGlob(filename, glob))
));
for (const [label, globs] of Object.entries(expected)) {
assert.match(labeler, new RegExp(`^"${label}":`, 'm'), `${label} has no mapping`);
for (const glob of globs) assert.ok(labeler.includes(JSON.stringify(glob)), `${label} does not map ${glob}`);
}
assert.deepEqual(uncovered, []);
});
test('package lock versions match the package manifest', () => {