govgate 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -20
- package/dist/index.js +23 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# govgate
|
|
2
2
|
|
|
3
|
-
Pipeline-step CLI for
|
|
3
|
+
Pipeline-step CLI for Governance OS: parses standard test output (JUnit XML),
|
|
4
4
|
maps tests to test cases, reports a run, and **gates the release** via its exit code.
|
|
5
5
|
No SDK in application code — only the pipeline YAML changes.
|
|
6
6
|
|
|
@@ -19,9 +19,9 @@ standard reporter.
|
|
|
19
19
|
|
|
20
20
|
| Flag | Default | Meaning |
|
|
21
21
|
|---|---|---|
|
|
22
|
-
| `--url <url>` | `
|
|
23
|
-
| `--api-key <key>` | `
|
|
24
|
-
| `--config <path>` |
|
|
22
|
+
| `--url <url>` | `GOVGATE_URL` | Tool deployment URL |
|
|
23
|
+
| `--api-key <key>` | `GOVGATE_API_KEY` | API key (env var strongly preferred) |
|
|
24
|
+
| `--config <path>` | `govgate/config.json` searched upward | Config file |
|
|
25
25
|
| `--suite <slug>` | config `suite` | Target suite |
|
|
26
26
|
| `--env <slug>` | config `defaultEnvironment` | Environment for the run |
|
|
27
27
|
| `--name <name>` | derived from CI context | Run name |
|
|
@@ -61,7 +61,7 @@ Resolution per test (results are unioned):
|
|
|
61
61
|
what .NET JUnit loggers emit), an underscore-delimited `Mt<n>` token maps the test:
|
|
62
62
|
`Mt5_DuplicateEmail_Rejected`, `Profiles_Mt12_NotFound`, `Delete_Resolves_MT28`.
|
|
63
63
|
Embedded substrings (`GMT5_…`, `Format2_…`) do not match.
|
|
64
|
-
3. **Config patterns** in
|
|
64
|
+
3. **Config patterns** in `govgate/config.json`:
|
|
65
65
|
|
|
66
66
|
```jsonc
|
|
67
67
|
{
|
|
@@ -83,10 +83,9 @@ any passing → `pass`; all skipped → the case is not posted (stays pending
|
|
|
83
83
|
test is a non-observation). Suite cases with no mapped tests stay pending and are
|
|
84
84
|
listed as "uncovered" in the summary.
|
|
85
85
|
|
|
86
|
-
Use the `map-tests`
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
validate it.
|
|
86
|
+
Use the `map-tests` Governance OS skill (served by your deployment at
|
|
87
|
+
`${GOVGATE_URL}/api/v1/skills`) to generate the mapping semi-automatically, and
|
|
88
|
+
`--dry-run` to validate it.
|
|
90
89
|
|
|
91
90
|
## .NET specifics (learned the hard way)
|
|
92
91
|
|
|
@@ -102,28 +101,25 @@ validate it.
|
|
|
102
101
|
- **Classic Release has no source checkout**: `dotnet publish` the test project into
|
|
103
102
|
the build artifact (`drop/tests/`) and run it with the ".NET Core" task using
|
|
104
103
|
Command `custom` + `vstest` — the `run` command does NOT execute arbitrary commands.
|
|
105
|
-
- The full ADO runbook (variable groups, stage scoping, exact task configs)
|
|
106
|
-
`
|
|
107
|
-
|
|
108
|
-
repo — or run the `onboard-repo` skill.
|
|
104
|
+
- The full ADO runbook (variable groups, stage scoping, exact task configs) is the
|
|
105
|
+
`onboard-repo` Governance OS skill (served at `${GOVGATE_URL}/api/v1/skills`) — run it,
|
|
106
|
+
or read its `reference.md`.
|
|
109
107
|
|
|
110
108
|
## Multi-job pipelines
|
|
111
109
|
|
|
112
110
|
```bash
|
|
113
111
|
# job A — creates the run, leaves it open
|
|
114
|
-
govgate report unit-results.xml --no-complete # emits
|
|
112
|
+
govgate report unit-results.xml --no-complete # emits GOVGATE_RUN_ID (GH output / ADO variable)
|
|
115
113
|
|
|
116
114
|
# job B — appends to the same run
|
|
117
|
-
govgate report e2e-results.xml --run-id $
|
|
115
|
+
govgate report e2e-results.xml --run-id $GOVGATE_RUN_ID --no-complete
|
|
118
116
|
|
|
119
117
|
# final job — completes + gates over everything
|
|
120
|
-
govgate complete --run-id $
|
|
118
|
+
govgate complete --run-id $GOVGATE_RUN_ID --fail-on fail --min-executed 80
|
|
121
119
|
```
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
[Maersk-Training/mt-testing-tool](https://github.com/Maersk-Training/mt-testing-tool)
|
|
126
|
-
repo for full pipelines.
|
|
121
|
+
Full pipeline examples for GitHub Actions and Azure DevOps ship with your Governance OS
|
|
122
|
+
deployment (served alongside the skills at `${GOVGATE_URL}/api/v1/skills`).
|
|
127
123
|
|
|
128
124
|
## Registry
|
|
129
125
|
|
package/dist/index.js
CHANGED
|
@@ -104,12 +104,12 @@ function detectCiContext(env = process.env) {
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
function emitRunIdVariable(runId, ctx, env = process.env) {
|
|
107
|
-
console.log(`
|
|
107
|
+
console.log(`GOVGATE_RUN_ID=${runId}`);
|
|
108
108
|
if (ctx.provider === "github" && env.GITHUB_OUTPUT) {
|
|
109
109
|
appendFileSync(env.GITHUB_OUTPUT, `run-id=${runId}
|
|
110
110
|
`);
|
|
111
111
|
} else if (ctx.provider === "azure-devops") {
|
|
112
|
-
console.log(`##vso[task.setvariable variable=
|
|
112
|
+
console.log(`##vso[task.setvariable variable=GOVGATE_RUN_ID;isOutput=true]${runId}`);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -126,7 +126,7 @@ function findConfigFile(startDir, explicitPath) {
|
|
|
126
126
|
}
|
|
127
127
|
let dir = resolve(startDir);
|
|
128
128
|
for (; ; ) {
|
|
129
|
-
const candidate = join(dir, ".
|
|
129
|
+
const candidate = join(dir, "govgate", "config.json");
|
|
130
130
|
if (existsSync(candidate)) return candidate;
|
|
131
131
|
const parent = dirname(dir);
|
|
132
132
|
if (parent === dir) return void 0;
|
|
@@ -169,26 +169,36 @@ function loadFileConfig(path) {
|
|
|
169
169
|
}
|
|
170
170
|
return out;
|
|
171
171
|
}
|
|
172
|
+
function loadFileMappings(flags, cwd = process.cwd()) {
|
|
173
|
+
const configPath = findConfigFile(cwd, flags.config);
|
|
174
|
+
const file = configPath ? loadFileConfig(configPath) : {};
|
|
175
|
+
return {
|
|
176
|
+
mappings: file.mappings ?? {},
|
|
177
|
+
suite: flags.suite ?? file.suite,
|
|
178
|
+
environment: flags.env ?? file.defaultEnvironment,
|
|
179
|
+
configPath
|
|
180
|
+
};
|
|
181
|
+
}
|
|
172
182
|
function resolveConfig(flags, cwd = process.cwd()) {
|
|
173
183
|
const configPath = findConfigFile(cwd, flags.config);
|
|
174
184
|
const file = configPath ? loadFileConfig(configPath) : {};
|
|
175
|
-
const url = flags.url ?? process.env.
|
|
176
|
-
const apiKey = flags.apiKey ?? process.env.
|
|
185
|
+
const url = flags.url ?? process.env.GOVGATE_URL;
|
|
186
|
+
const apiKey = flags.apiKey ?? process.env.GOVGATE_API_KEY;
|
|
177
187
|
const suite = flags.suite ?? file.suite;
|
|
178
188
|
const environment = flags.env ?? file.defaultEnvironment;
|
|
179
189
|
if (!url) {
|
|
180
190
|
throw new ConfigError(
|
|
181
|
-
"Tool URL missing. Set
|
|
191
|
+
"Tool URL missing. Set GOVGATE_URL or pass --url https://<your-deployment>"
|
|
182
192
|
);
|
|
183
193
|
}
|
|
184
194
|
if (!apiKey) {
|
|
185
195
|
throw new ConfigError(
|
|
186
|
-
"API key missing. Set
|
|
196
|
+
"API key missing. Set GOVGATE_API_KEY (create one in the tool: Admin -> API keys, scoped to this application)."
|
|
187
197
|
);
|
|
188
198
|
}
|
|
189
199
|
if (!suite) {
|
|
190
200
|
throw new ConfigError(
|
|
191
|
-
'Suite missing. Pass --suite <slug> or add { "suite": "<slug>" } to .
|
|
201
|
+
'Suite missing. Pass --suite <slug> or add { "suite": "<slug>" } to govgate/config.json in the repo root.'
|
|
192
202
|
);
|
|
193
203
|
}
|
|
194
204
|
return {
|
|
@@ -392,7 +402,7 @@ function printMappingTable(outcome) {
|
|
|
392
402
|
}
|
|
393
403
|
if (outcome.unmappedTests.length) {
|
|
394
404
|
console.log(`
|
|
395
|
-
Unmapped tests (${outcome.unmappedTests.length}) \u2014 add @mt-<case> tags or .
|
|
405
|
+
Unmapped tests (${outcome.unmappedTests.length}) \u2014 add @mt-<case> tags or govgate/config.json mappings:`);
|
|
396
406
|
for (const t of outcome.unmappedTests.slice(0, 20)) console.log(` - ${t.id}`);
|
|
397
407
|
if (outcome.unmappedTests.length > 20)
|
|
398
408
|
console.log(` \u2026 and ${outcome.unmappedTests.length - 20} more`);
|
|
@@ -476,8 +486,8 @@ async function completeAndGate(api, url, runId, gate) {
|
|
|
476
486
|
printRunUrl(url, runId);
|
|
477
487
|
if (!verdict.passed) process.exit(EXIT_GATE_FAILED);
|
|
478
488
|
}
|
|
479
|
-
var program = new Command().name("govgate").description("Report CI test results to
|
|
480
|
-
program.command("report").description("Parse test result files (JUnit XML), map tests to cases, and report a run").argument("<files...>", "result files or globs, e.g. test-results/*.xml").option("--url <url>", "tool URL (or
|
|
489
|
+
var program = new Command().name("govgate").description("Report CI test results to your testing tool and gate releases on the outcome.");
|
|
490
|
+
program.command("report").description("Parse test result files (JUnit XML), map tests to cases, and report a run").argument("<files...>", "result files or globs, e.g. test-results/*.xml").option("--url <url>", "tool URL (or GOVGATE_URL)").option("--api-key <key>", "API key (prefer GOVGATE_API_KEY)").option("--config <path>", "path to govgate/config.json (default: searched upward)").option("--suite <slug>", "suite slug (overrides config)").option("--env <slug>", "environment slug (overrides config)").option("--name <name>", "run name (default: derived from CI context)").option("--build-version <v>", "build version (default: short commit SHA from CI)").option("--external-url <url>", "link to the build/PR (default: from CI context)").option("--format <format>", "input format", "junit").option("--run-id <uuid>", "append results to an existing run instead of creating one").option("--no-complete", "leave the run in progress (multi-job pipelines); skips the gate").option("--fail-on <list>", "statuses that fail the gate: fail | fail,blocked", "fail").option("--min-executed <pct>", "minimum executed percentage required by the gate").option("--actor <label>", "tested-by label on results", "govgate").option("--dry-run", "parse and map only; post nothing").action(async (files, opts) => {
|
|
481
491
|
try {
|
|
482
492
|
const flags = {
|
|
483
493
|
url: opts.url,
|
|
@@ -488,7 +498,7 @@ program.command("report").description("Parse test result files (JUnit XML), map
|
|
|
488
498
|
};
|
|
489
499
|
const tests = await parseFiles(files, opts.format);
|
|
490
500
|
const config = opts.dryRun ? tryResolveConfig(flags) : resolveConfig(flags);
|
|
491
|
-
const mappings = config?.mappings ?? {};
|
|
501
|
+
const mappings = config?.mappings ?? (opts.dryRun ? loadFileMappings(flags).mappings : {});
|
|
492
502
|
const outcome = mapTestsToCases(tests, mappings);
|
|
493
503
|
console.log(
|
|
494
504
|
`Parsed ${tests.length} test(s) from ${files.join(", ")} \u2014 ${outcome.results.length} case(s) mapped, ${outcome.unmappedTests.length} unmapped test(s)`
|
|
@@ -554,7 +564,7 @@ function tryResolveConfig(flags) {
|
|
|
554
564
|
return void 0;
|
|
555
565
|
}
|
|
556
566
|
}
|
|
557
|
-
program.command("complete").description("Complete a run and evaluate the gate (final job of a multi-job pipeline)").requiredOption("--run-id <uuid>", "run to complete").option("--url <url>", "tool URL (or
|
|
567
|
+
program.command("complete").description("Complete a run and evaluate the gate (final job of a multi-job pipeline)").requiredOption("--run-id <uuid>", "run to complete").option("--url <url>", "tool URL (or GOVGATE_URL)").option("--api-key <key>", "API key (prefer GOVGATE_API_KEY)").option("--config <path>", "path to govgate/config.json").option("--suite <slug>", "suite slug (only used for config resolution)").option("--fail-on <list>", "statuses that fail the gate: fail | fail,blocked", "fail").option("--min-executed <pct>", "minimum executed percentage required by the gate").action(async (opts) => {
|
|
558
568
|
try {
|
|
559
569
|
const config = resolveConfig({
|
|
560
570
|
url: opts.url,
|
package/package.json
CHANGED