Merge pull request #125 from pikasTech/fix/dev-report-port-contract

fix: enforce dev report port lifecycle
This commit is contained in:
Lyon
2026-05-22 20:00:44 +08:00
committed by GitHub
6 changed files with 387 additions and 5 deletions
+29
View File
@@ -25,6 +25,11 @@ Each committed DEV gate report must carry `reportLifecycle`:
- Active reports may preserve a legacy public endpoint only inside an explicit
`runtimeSmoke.legacyPublicEndpoint` object with `status: "deprecated"` and
`activeGreenEligible: false`.
- Active reports must not present `:6666` or `:6667` as a current public DEV
endpoint in endpoint, route, health, ingress, API, frontend, or browser
evidence. Internal k3s service/listen ports may still be `6667` when the
surrounding field names them as cluster-local service ports rather than
public endpoints.
- M3 DEV-LIVE remains narrower than report freshness: it requires
`res_boxsimu_1:DO1 -> hwlab-patch-panel -> res_boxsimu_2:DI1` in real DEV
plus operation, audit, and evidence identifiers. SOURCE, LOCAL, DRY-RUN,
@@ -40,6 +45,9 @@ Each committed DEV gate report must carry `reportLifecycle`:
- `reports/dev-gate/dev-gate-report.example.json`: machine-readable example.
- `reports/dev-gate/dev-deploy-report.json`: DEV deploy apply/preflight result.
- `scripts/validate-dev-gate-report.mjs`: local validator for the report shape.
- `scripts/report-lifecycle.mjs`: lightweight lifecycle helper for validating
one report or marking a replaced report historical/deprecated after endpoint
changes.
## Required Fields
@@ -105,3 +113,24 @@ node scripts/validate-dev-gate-report.mjs
The validator scans `reports/dev-gate/*.json` by default. It only checks the
contract shape and the frozen report metadata; it does not run any DEV or PROD
deployment.
## Invalidate/Rebuild Workflow
When the public DEV endpoint mapping changes, do not rewrite historical
evidence strings. Preserve the old facts and mark any report that can no longer
represent the active gate as historical:
```sh
node --check scripts/report-lifecycle.mjs
node scripts/report-lifecycle.mjs invalidate reports/dev-gate/<old-report>.json \
--deprecated-endpoint http://74.48.78.17:6667 \
--reason "Public DEV endpoint moved to frontend :16666 and API/edge :16667" \
--out reports/dev-gate/historical/<old-report>-legacy-6667.json
node scripts/validate-dev-gate-report.mjs reports/dev-gate/historical/<old-report>-legacy-6667.json
```
Then regenerate the active report through its owning script so the active file
uses `http://74.48.78.17:16666` for browser/frontend evidence and
`http://74.48.78.17:16667` for API/edge/live evidence. Keep PR notes explicit:
which legacy reports were moved or marked historical, which active reports were
regenerated, and which current reports still need real DEV reruns.
+42 -2
View File
@@ -4,15 +4,55 @@ export const DEPRECATED_DEV_PUBLIC_ENDPOINT = "http://74.48.78.17:6667";
export const DEPRECATED_RESERVED_PROD_ENDPOINT = "http://74.48.78.17:6666";
export const REPORT_LIFECYCLE_VERSION = "v1";
export const deprecatedPublicPorts = Object.freeze([6666, 6667]);
export const deprecatedPublicEndpoints = Object.freeze([
DEPRECATED_DEV_PUBLIC_ENDPOINT,
DEPRECATED_RESERVED_PROD_ENDPOINT
]);
const deprecatedPublicEndpointPattern = /http:\/\/74\.48\.78\.17:666[67]\b/gu;
const deprecatedPublicEndpointPattern = /\bhttps?:\/\/74\.48\.78\.17:(666[67])(?=[/?#\s"'`),\]}]|$)/giu;
const deprecatedPublicHostPortPattern = /(?<!\/\/)\b74\.48\.78\.17:(666[67])(?=[/?#\s"'`),\]}]|$)/giu;
const endpointContextWords = [
"active",
"api",
"browser",
"current",
"deprecated",
"edge",
"endpoint",
"frontend",
"health",
"historical",
"ingress",
"legacy",
"public",
"route"
].join("|");
const deprecatedPublicPortContextPattern = new RegExp(
`\\b(?:${endpointContextWords})\\b[^.\\n]{0,80}(?<![A-Za-z0-9.-]):(666[67])\\b|(?<![A-Za-z0-9.-]):(666[67])\\b[^.\\n]{0,80}\\b(?:${endpointContextWords})\\b`,
"giu"
);
function endpointForPort(port) {
if (Number(port) === 6666) return DEPRECATED_RESERVED_PROD_ENDPOINT;
if (Number(port) === 6667) return DEPRECATED_DEV_PUBLIC_ENDPOINT;
throw new Error(`unsupported deprecated public port ${port}`);
}
function addMatches(matches, pattern, text) {
pattern.lastIndex = 0;
for (const match of String(text).matchAll(pattern)) {
const port = match[1] ?? match[2];
matches.add(endpointForPort(port));
}
}
export function findDeprecatedPublicEndpoints(text) {
return [...new Set(String(text).match(deprecatedPublicEndpointPattern) ?? [])].sort();
const matches = new Set();
addMatches(matches, deprecatedPublicEndpointPattern, text);
addMatches(matches, deprecatedPublicHostPortPattern, text);
addMatches(matches, deprecatedPublicPortContextPattern, text);
return [...matches].sort();
}
export function isDeprecatedEndpointAllowedInActiveReport(report, endpoint) {
+4 -2
View File
@@ -5,13 +5,14 @@
"type": "module",
"scripts": {
"validate": "node scripts/validate-contract.mjs && node scripts/deploy-contract-plan.mjs --check && node scripts/deploy-desired-state-plan.mjs --check",
"check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node --test scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/server.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh",
"check": "node --check internal/protocol/index.mjs && node --check internal/agent/index.mjs && node --check internal/agent/runtime.mjs && node --check internal/audit/index.mjs && node --check internal/db/runtime-store.mjs && node --check internal/mvp-gate/summary.mjs && node --check internal/cloud/db-contract.mjs && node --check internal/cloud/json-rpc.mjs && node --check internal/cloud/server.mjs && node --check internal/sim/model.mjs && node --check internal/sim/http.mjs && node --check internal/sim/l2-runtime.mjs && node --check internal/patchpanel/model.mjs && node --check internal/patchpanel/runtime.mjs && node --check cmd/hwlab-cloud-api/main.mjs && node --check cmd/hwlab-agent-mgr/main.mjs && node --check cmd/hwlab-agent-worker/main.mjs && node --check cmd/hwlab-box-simu/main.mjs && node --check cmd/hwlab-gateway-simu/main.mjs && node --check scripts/cloud-api-runtime-smoke.mjs && node --check scripts/dev-edge-health-smoke.mjs && node --check scripts/src/dev-edge-health-smoke-lib.mjs && node --check scripts/validate-contract.mjs && node --check scripts/deploy-desired-state-plan.mjs && node --check scripts/src/deploy-desired-state-plan.mjs && node --check scripts/report-lifecycle.mjs && node --check scripts/report-lifecycle.test.mjs && node --check scripts/validate-dev-gate-report.mjs && node --check scripts/validate-dev-m3-cardinality.mjs && node --check scripts/validate-artifact-catalog.mjs && node --check scripts/refresh-artifact-catalog.mjs && node --check scripts/dev-artifact-publish.mjs && node --check scripts/src/dev-artifact-services.mjs && node --check scripts/src/registry-capabilities.mjs && node --check scripts/preflight-dev-base-image.mjs && node --check scripts/src/dev-base-image-preflight.mjs && node --check scripts/dev-evidence-blocker-aggregator.mjs && node --check scripts/src/dev-evidence-blocker-aggregator.mjs && node --check scripts/d601-k3s-readonly-observability.mjs && node --check scripts/src/d601-k3s-readonly-observability.mjs && node --check scripts/l2-runtime-contract-smoke.mjs && node --check scripts/patch-panel-runtime-smoke.mjs && node --check scripts/export-web-gate-summary.mjs && node --check scripts/l6-cli-web-smoke.mjs && node --check tools/hwlab-cli/bin/hwlab-cli.mjs && node --check tools/hwlab-cli/lib/cli.mjs && node --check web/hwlab-cloud-web/app.mjs && node --check web/hwlab-cloud-web/gate-summary.mjs && node --check web/hwlab-cloud-web/scripts/check.mjs && node --check web/hwlab-cloud-web/scripts/build.mjs && node scripts/validate-contract.mjs && node scripts/validate-dev-gate-report.mjs && node scripts/report-lifecycle.test.mjs && node scripts/validate-dev-m3-cardinality.mjs && node scripts/validate-artifact-catalog.mjs && node scripts/deploy-desired-state-plan.mjs --check && node scripts/dev-evidence-blocker-aggregator.mjs --check && node scripts/l2-runtime-contract-smoke.mjs && node scripts/l6-cli-web-smoke.mjs && node web/hwlab-cloud-web/scripts/check.mjs && node --test scripts/deploy-desired-state-plan.test.mjs scripts/src/dev-deploy-apply.test.mjs internal/agent/index.test.mjs internal/audit/index.test.mjs internal/db/schema.test.mjs internal/cloud/json-rpc.test.mjs internal/cloud/server.test.mjs internal/patchpanel/model.test.mjs internal/patchpanel/runtime.test.mjs && node scripts/cloud-api-runtime-smoke.mjs && sh -n scripts/bootstrap-skills.sh scripts/worker-entrypoint.sh",
"dev-base-image:preflight": "node scripts/preflight-dev-base-image.mjs",
"cloud-api:smoke": "node scripts/cloud-api-runtime-smoke.mjs",
"m1:smoke": "node scripts/m1-contract-smoke.mjs",
"l2:smoke": "node scripts/l2-runtime-contract-smoke.mjs",
"dev:evidence": "node scripts/dev-evidence-blocker-aggregator.mjs --pretty",
"dev:evidence:check": "node scripts/dev-evidence-blocker-aggregator.mjs --check",
"report:lifecycle": "node scripts/report-lifecycle.mjs",
"dev-edge:smoke": "node scripts/dev-edge-health-smoke.mjs",
"deploy:contract:plan": "node scripts/deploy-contract-plan.mjs --pretty",
"deploy:contract:check": "node scripts/deploy-contract-plan.mjs --check",
@@ -30,6 +31,7 @@
"dev-artifact:publish": "node scripts/dev-artifact-publish.mjs --publish",
"docs:validate:m3-rollout": "node scripts/validate-m3-rollout-runbook.mjs",
"d601:k3s:readonly": "node scripts/d601-k3s-readonly-observability.mjs",
"runner:issue-visibility:preflight": "node scripts/runner-issue-visibility-preflight.mjs"
"runner:issue-visibility:preflight": "node scripts/runner-issue-visibility-preflight.mjs",
"report:lifecycle:test": "node scripts/report-lifecycle.test.mjs"
}
}
+235
View File
@@ -0,0 +1,235 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
ACTIVE_DEV_BROWSER_ENDPOINT,
ACTIVE_DEV_PUBLIC_ENDPOINT,
REPORT_LIFECYCLE_VERSION,
deprecatedPublicEndpoints,
findForbiddenActiveDeprecatedEndpoints,
historicalReportLifecycle
} from "../internal/dev-report-lifecycle.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const defaultDeprecatedEndpoint = "http://74.48.78.17:6667";
const statusFields = ["status", "gateStatus", "conclusion"];
function usage() {
return [
"Usage:",
" node scripts/report-lifecycle.mjs validate <report.json>",
" node scripts/report-lifecycle.mjs invalidate <report.json> --reason <text> [--deprecated-endpoint <url>] [--replaced-by <path>] [--out <path>]",
"",
"Notes:",
" validate checks the active/historical endpoint lifecycle contract for one report.",
" invalidate marks one report historical/deprecated without changing evidence payloads.",
" --out defaults to in-place; use a historical/ path when keeping an old active filename reserved for regenerated evidence."
].join("\n");
}
function parseArgs(argv) {
if (argv.length === 0 || argv.includes("--help")) {
return { help: true };
}
const [command, reportPath, ...rest] = argv;
const args = {
command,
reportPath,
deprecatedEndpoint: defaultDeprecatedEndpoint,
reason: null,
replacedBy: null,
out: null
};
for (let index = 0; index < rest.length; index += 1) {
const arg = rest[index];
if (arg === "--deprecated-endpoint") {
args.deprecatedEndpoint = rest[++index];
} else if (arg === "--reason") {
args.reason = rest[++index];
} else if (arg === "--replaced-by") {
args.replacedBy = rest[++index];
} else if (arg === "--out") {
args.out = rest[++index];
} else if (arg === "--help") {
args.help = true;
} else {
throw new Error(`unknown argument: ${arg}`);
}
}
if (args.help || !args.command) return args;
if (!["validate", "invalidate"].includes(args.command)) {
throw new Error(`unknown command: ${args.command}`);
}
assert.ok(args.reportPath, "report path is required");
if (args.command === "invalidate") {
assert.ok(args.reason, "--reason is required for invalidate");
assert.ok(
deprecatedPublicEndpoints.includes(args.deprecatedEndpoint),
`--deprecated-endpoint must be one of ${deprecatedPublicEndpoints.join(", ")}`
);
}
return args;
}
function repoPath(inputPath) {
const absolutePath = path.resolve(process.cwd(), inputPath);
assert.ok(
absolutePath === repoRoot || absolutePath.startsWith(`${repoRoot}${path.sep}`),
`${inputPath} must stay inside the repository`
);
return absolutePath;
}
function relativeRepoPath(inputPath) {
return path.relative(repoRoot, repoPath(inputPath)).split(path.sep).join("/");
}
function assertObject(value, label) {
assert.ok(value && typeof value === "object" && !Array.isArray(value), `${label} must be an object`);
}
function assertString(value, label) {
assert.equal(typeof value, "string", `${label} must be a string`);
assert.ok(value.trim().length > 0, `${label} must not be empty`);
}
function validateLifecycle(report, raw, label) {
assertObject(report.reportLifecycle, `${label}.reportLifecycle`);
const lifecycle = report.reportLifecycle;
assert.equal(lifecycle.version, REPORT_LIFECYCLE_VERSION, `${label}.reportLifecycle.version`);
assert.ok(["active", "historical"].includes(lifecycle.state), `${label}.reportLifecycle.state`);
assert.equal(lifecycle.activeEndpoint, ACTIVE_DEV_PUBLIC_ENDPOINT, `${label}.reportLifecycle.activeEndpoint`);
assert.equal(lifecycle.activeBrowserEndpoint, ACTIVE_DEV_BROWSER_ENDPOINT, `${label}.reportLifecycle.activeBrowserEndpoint`);
assertString(lifecycle.summary, `${label}.reportLifecycle.summary`);
if (lifecycle.state === "active") {
assert.equal(lifecycle.deprecatedEndpoint, null, `${label}.reportLifecycle.deprecatedEndpoint`);
assert.deepEqual(
findForbiddenActiveDeprecatedEndpoints(report, raw),
[],
`${label} active report must not present :6666/:6667 as current public endpoint evidence`
);
return "active";
}
assert.ok(
deprecatedPublicEndpoints.includes(lifecycle.deprecatedEndpoint),
`${label}.reportLifecycle.deprecatedEndpoint must name a deprecated public endpoint`
);
assert.ok(
lifecycle.summary.toLowerCase().includes("historical") ||
lifecycle.summary.toLowerCase().includes("deprecated"),
`${label}.reportLifecycle.summary must explain the historical/deprecated state`
);
return "historical";
}
function primaryStatus(report) {
return report.status ?? report.gateStatus ?? report.conclusion ?? null;
}
function markNonGreen(report) {
let hasTopLevelStatus = false;
for (const field of statusFields) {
if (Object.hasOwn(report, field)) {
hasTopLevelStatus = true;
if (["pass", "ready", "green", "published"].includes(report[field])) {
report[field] = "blocked";
}
}
}
if (!hasTopLevelStatus) {
report.status = "blocked";
}
}
function appendInvalidationBlocker(report, reason, deprecatedEndpoint) {
if (!Array.isArray(report.blockers)) {
report.blockers = [];
}
const scope = "deprecated-public-endpoint";
const exists = report.blockers.some((blocker) => blocker?.scope === scope);
if (!exists) {
report.blockers.push({
type: "environment_blocker",
scope,
status: "open",
summary: `${reason} Deprecated endpoint ${deprecatedEndpoint} cannot be used as active DEV evidence.`
});
}
}
async function readReport(reportPath) {
const absolutePath = repoPath(reportPath);
const raw = await readFile(absolutePath, "utf8");
return { absolutePath, raw, report: JSON.parse(raw) };
}
async function validateCommand(args) {
const relativePath = relativeRepoPath(args.reportPath);
const { raw, report } = await readReport(args.reportPath);
const state = validateLifecycle(report, raw, relativePath);
process.stdout.write(`${JSON.stringify({
ok: true,
path: relativePath,
state,
primaryStatus: primaryStatus(report)
})}\n`);
}
async function invalidateCommand(args) {
const inputRelativePath = relativeRepoPath(args.reportPath);
const { report } = await readReport(args.reportPath);
assertObject(report, inputRelativePath);
report.reportLifecycle = historicalReportLifecycle({
deprecatedEndpoint: args.deprecatedEndpoint,
summary: `Historical/deprecated after DEV public endpoint change: ${args.reason}`
});
if (args.replacedBy) {
report.reportLifecycle.replacedBy = relativeRepoPath(args.replacedBy);
}
markNonGreen(report);
appendInvalidationBlocker(report, args.reason, args.deprecatedEndpoint);
const outPath = args.out ? repoPath(args.out) : repoPath(args.reportPath);
await mkdir(path.dirname(outPath), { recursive: true });
const nextRaw = `${JSON.stringify(report, null, 2)}\n`;
validateLifecycle(report, nextRaw, inputRelativePath);
await writeFile(outPath, nextRaw);
process.stdout.write(`${JSON.stringify({
ok: true,
input: inputRelativePath,
output: path.relative(repoRoot, outPath).split(path.sep).join("/"),
state: "historical",
deprecatedEndpoint: args.deprecatedEndpoint,
replacedBy: report.reportLifecycle.replacedBy ?? null
})}\n`);
}
async function main() {
const args = parseArgs(process.argv.slice(2));
if (args.help || !args.command) {
process.stdout.write(`${usage()}\n`);
return;
}
if (args.command === "validate") {
await validateCommand(args);
return;
}
if (args.command === "invalidate") {
await invalidateCommand(args);
}
}
try {
await main();
} catch (error) {
process.stderr.write(`[report-lifecycle] ${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env node
import assert from "node:assert/strict";
import {
DEPRECATED_DEV_PUBLIC_ENDPOINT,
DEPRECATED_RESERVED_PROD_ENDPOINT,
findDeprecatedPublicEndpoints,
findForbiddenActiveDeprecatedEndpoints
} from "../internal/dev-report-lifecycle.mjs";
const activeReport = {
reportLifecycle: {
state: "active"
}
};
const activeReportWithLegacyException = {
reportLifecycle: {
state: "active"
},
runtimeSmoke: {
legacyPublicEndpoint: {
endpoint: DEPRECATED_DEV_PUBLIC_ENDPOINT,
status: "deprecated",
activeGreenEligible: false
}
}
};
assert.deepEqual(
findDeprecatedPublicEndpoints("current public endpoint http://74.48.78.17:6667/health"),
[DEPRECATED_DEV_PUBLIC_ENDPOINT]
);
assert.deepEqual(
findDeprecatedPublicEndpoints("old public :6666 evidence"),
[DEPRECATED_RESERVED_PROD_ENDPOINT]
);
assert.deepEqual(
findDeprecatedPublicEndpoints("http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667 is an internal k3s service URL"),
[]
);
assert.deepEqual(
findDeprecatedPublicEndpoints('"port": 6667, "note": "Internal k3s service port only"'),
[]
);
assert.deepEqual(
findForbiddenActiveDeprecatedEndpoints(activeReport, "current public :6667 endpoint"),
[DEPRECATED_DEV_PUBLIC_ENDPOINT]
);
assert.deepEqual(
findForbiddenActiveDeprecatedEndpoints(activeReportWithLegacyException, "legacy public :6667 endpoint is deprecated"),
[]
);
assert.deepEqual(
findForbiddenActiveDeprecatedEndpoints({ reportLifecycle: { state: "historical" } }, "current public :6667 endpoint"),
[]
);
console.log("validated report lifecycle endpoint scanner");
+18 -1
View File
@@ -4,6 +4,11 @@ import { readFile } from "node:fs/promises";
import path from "node:path";
import { fileURLToPath } from "node:url";
import {
findForbiddenActiveDeprecatedEndpoints,
REPORT_LIFECYCLE_VERSION
} from "../internal/dev-report-lifecycle.mjs";
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const reportPath = path.join(repoRoot, "reports/dev-gate/dev-m2-deploy-smoke-active.json");
@@ -26,10 +31,22 @@ function assertTimestamp(value, label) {
}
async function main() {
const report = JSON.parse(await readFile(reportPath, "utf8"));
const raw = await readFile(reportPath, "utf8");
const report = JSON.parse(raw);
assertObject(report, "report");
assert.equal(report.issue, "pikasTech/HWLAB#23");
assert.equal(report.taskId, "m2-dev-deploy-smoke");
assertObject(report.reportLifecycle, "reportLifecycle");
assert.equal(report.reportLifecycle.version, REPORT_LIFECYCLE_VERSION);
assert.equal(report.reportLifecycle.state, "active");
assert.equal(report.reportLifecycle.activeEndpoint, "http://74.48.78.17:16667");
assert.equal(report.reportLifecycle.activeBrowserEndpoint, "http://74.48.78.17:16666");
assert.equal(report.reportLifecycle.deprecatedEndpoint, null);
assert.deepEqual(
findForbiddenActiveDeprecatedEndpoints(report, raw),
[],
"active M2 report must not present deprecated public ports as current endpoint evidence"
);
assert.equal(report.status, "pass");
assert.equal(report.endpoint, "http://74.48.78.17:16667");
assert.equal(report.frontendEndpoint, "http://74.48.78.17:16666");