govgate 0.4.0 → 0.5.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.
Files changed (3) hide show
  1. package/README.md +19 -1
  2. package/dist/index.js +44 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -23,7 +23,8 @@ standard reporter.
23
23
  | `--api-key <key>` | `GOVGATE_API_KEY` | API key (env var strongly preferred) |
24
24
  | `--config <path>` | `govgate/config.json` searched upward | Config file |
25
25
  | `--suite <slug>` | config `suite` | Target suite |
26
- | `--env <slug>` | config `defaultEnvironment` | Environment for the run |
26
+ | `--env <slug>` | inferred, else config `defaultEnvironment` | Environment for the run (overrides inference) |
27
+ | `--base-url <url>` | `API_BASEURL` | Live app base URL; matched against the declared environments' `baseUrl`s to infer the environment |
27
28
  | `--name <name>` | derived from CI context | Run name |
28
29
  | `--build-version <v>` | short commit SHA from CI | Build version |
29
30
  | `--external-url <url>` | CI build URL | "Build ↗" link on the run page |
@@ -49,6 +50,23 @@ automatically before creating a run when the block is present, so a brand-new st
49
50
  can never fail on an unknown environment — the explicit command exists for
50
51
  onboarding-time provisioning and for verifying the catalog without reporting.
51
52
 
53
+ ## Environment inference (v0.5.0+)
54
+
55
+ When `--env` is absent, `report` resolves the environment **from the deployment
56
+ under test**: the base URL (from `--base-url` or the `API_BASEURL` env var — the
57
+ same variable your smoke tests already use) is matched against the declared
58
+ environments' `baseUrl`s, and the matching slug wins. Falls back to
59
+ `defaultEnvironment` when nothing matches. Matching ignores scheme, casing, and
60
+ trailing slashes.
61
+
62
+ This makes one identical report step correct in every stage — no per-stage
63
+ `--env` variable to configure or mis-copy. The chosen slug is always printed to
64
+ the CI log.
65
+
66
+ ```
67
+ npx govgate report "test-results/*.xml" --fail-on fail # env inferred from API_BASEURL
68
+ ```
69
+
52
70
  ## Exit codes
53
71
 
54
72
  | Code | Meaning |
package/dist/index.js CHANGED
@@ -272,6 +272,28 @@ function parseEnvironments(path, raw) {
272
272
  }
273
273
  return out;
274
274
  }
275
+ function normalizeBaseUrl(raw) {
276
+ const trimmed = raw.trim();
277
+ if (trimmed === "") return void 0;
278
+ const withScheme = /^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
279
+ let url;
280
+ try {
281
+ url = new URL(withScheme);
282
+ } catch {
283
+ return void 0;
284
+ }
285
+ const path = url.pathname.replace(/\/+$/, "");
286
+ return `${url.host}${path}`.toLowerCase();
287
+ }
288
+ function inferEnvironment(environments, baseUrl) {
289
+ if (!baseUrl || !environments?.length) return void 0;
290
+ const target = normalizeBaseUrl(baseUrl);
291
+ if (!target) return void 0;
292
+ for (const env of environments) {
293
+ if (env.baseUrl && normalizeBaseUrl(env.baseUrl) === target) return env.slug;
294
+ }
295
+ return void 0;
296
+ }
275
297
  function loadFileMappings(flags, cwd = process.cwd()) {
276
298
  const configPath = findConfigFile(cwd, flags.config);
277
299
  const file = configPath ? loadFileConfig(configPath) : {};
@@ -288,7 +310,22 @@ function resolveConfig(flags, cwd = process.cwd()) {
288
310
  const url = flags.url ?? process.env.GOVGATE_URL;
289
311
  const apiKey = flags.apiKey ?? process.env.GOVGATE_API_KEY;
290
312
  const suite = flags.suite ?? file.suite;
291
- const environment = flags.env ?? file.defaultEnvironment;
313
+ let environment = flags.env;
314
+ if (!environment) {
315
+ const liveBaseUrl = flags.baseUrl ?? process.env.API_BASEURL;
316
+ const inferred = inferEnvironment(file.environments, liveBaseUrl);
317
+ if (inferred) {
318
+ console.error(`govgate: environment '${inferred}' inferred from base URL ${liveBaseUrl}`);
319
+ environment = inferred;
320
+ } else {
321
+ if (liveBaseUrl && file.environments?.some((e) => e.baseUrl)) {
322
+ console.error(
323
+ `govgate: base URL ${liveBaseUrl} matches no declared environment baseUrl` + (file.defaultEnvironment ? `; falling back to defaultEnvironment '${file.defaultEnvironment}'` : " and no defaultEnvironment is set")
324
+ );
325
+ }
326
+ environment = file.defaultEnvironment;
327
+ }
328
+ }
292
329
  if (!url) {
293
330
  throw new ConfigError(
294
331
  "Tool URL missing. Set GOVGATE_URL or pass --url https://<your-deployment>"
@@ -602,14 +639,18 @@ async function completeAndGate(api, url, runId, gate) {
602
639
  if (!verdict.passed) process.exit(EXIT_GATE_FAILED);
603
640
  }
604
641
  var program = new Command().name("govgate").description("Report CI test results to your testing tool and gate releases on the outcome.");
605
- 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) => {
642
+ 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 and inference)").option(
643
+ "--base-url <url>",
644
+ "base URL of the app under test; matched against the declared environments' baseUrls to infer the environment (default: API_BASEURL)"
645
+ ).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) => {
606
646
  try {
607
647
  const flags = {
608
648
  url: opts.url,
609
649
  apiKey: opts.apiKey,
610
650
  suite: opts.suite,
611
651
  env: opts.env,
612
- config: opts.config
652
+ config: opts.config,
653
+ baseUrl: opts.baseUrl
613
654
  };
614
655
  const tests = await parseFiles(files, opts.format);
615
656
  const config = opts.dryRun ? tryResolveConfig(flags) : resolveConfig(flags);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "govgate",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Governance gate for releases: report CI test results (JUnit XML) to your testing tool and gate promotion on the outcome.",
5
5
  "license": "MIT",
6
6
  "author": "HatStack",