新增发布 readiness artifact SDK 校验 workflow
This commit is contained in:
@@ -41,7 +41,9 @@ continuation records are in `../text14.txt`.
|
||||
evidence driven and only reports `ready: true` when the caller supplies
|
||||
release gate evidence such as `project_release_gate=ok`. The project release
|
||||
gate writes the same report to `build/project-release-readiness.json` and
|
||||
validates it with `verify_project_release_readiness_artifact.sh`.
|
||||
validates it with `verify_project_release_readiness_artifact.sh`. External
|
||||
tools can load that artifact with `parseProjectReleaseReadinessArtifactJson()`
|
||||
and validate it with `createProjectReleaseReadinessArtifactValidation()`.
|
||||
- OPFS/session callers can use `createMachineSessionPersistenceSummary()` or
|
||||
`linuxCncIniPanelApi.getMachineSessionPersistenceSummary()` to inspect
|
||||
machine files, session snapshot, session readiness, session load, readonly
|
||||
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createMachineSessionSnapshotPayload,
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
createProjectReleaseReadinessReport,
|
||||
createSessionSnapshot,
|
||||
defaultMachinePaths,
|
||||
@@ -69,6 +70,7 @@ import {
|
||||
mountIniPanelShellWorkflowOverviewEmbeddingMountState,
|
||||
normalizeOpfsPath,
|
||||
parameterFilePath,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
planIniFileContextStaging,
|
||||
planSimConfigStaging,
|
||||
readMachineSessionReadiness,
|
||||
@@ -194,7 +196,10 @@ and `../PROJECT_COMPLETION_TRACKER.md`.
|
||||
The executable release gate uses the same helper to write
|
||||
`build/project-release-readiness.json` after all project checks pass, then
|
||||
validates that artifact with
|
||||
`tests/host/verify_project_release_readiness_artifact.sh`.
|
||||
`tests/host/verify_project_release_readiness_artifact.sh`. External tools can
|
||||
load that JSON with `parseProjectReleaseReadinessArtifactJson()` and validate
|
||||
it with `createProjectReleaseReadinessArtifactValidation()` without copying the
|
||||
host test assertions.
|
||||
|
||||
## OPFS/session persistence exports
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
|
||||
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
|
||||
export { createProjectReleaseReadinessReport } from "./project-release-readiness.js";
|
||||
export {
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
createProjectReleaseReadinessReport,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
} from "./project-release-readiness.js";
|
||||
export {
|
||||
analyzeIniRuntimeBoundaries,
|
||||
planIniFileContextStaging,
|
||||
|
||||
@@ -67,6 +67,9 @@ const BLOCKED_RUNTIME_FAMILIES = [
|
||||
"L4-PYTHON-REMAP",
|
||||
];
|
||||
|
||||
const PROJECT_RELEASE_READINESS_ARTIFACT_API = "project-release-readiness-report";
|
||||
const PROJECT_RELEASE_READINESS_ARTIFACT_VERSION = 1;
|
||||
|
||||
function outputContains(observedOutputs, expectedOutput) {
|
||||
return observedOutputs.some((output) => String(output).includes(expectedOutput));
|
||||
}
|
||||
@@ -81,6 +84,14 @@ function readGatePassed({ gate, gateResults, observedOutputs }) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function objectOrEmpty(value) {
|
||||
return value && typeof value === "object" ? value : {};
|
||||
}
|
||||
|
||||
function arrayOrEmpty(value) {
|
||||
return Array.isArray(value) ? value : [];
|
||||
}
|
||||
|
||||
export function createProjectReleaseReadinessReport({
|
||||
gateResults = {},
|
||||
observedOutputs = [],
|
||||
@@ -118,8 +129,8 @@ export function createProjectReleaseReadinessReport({
|
||||
];
|
||||
|
||||
return {
|
||||
apiName: "project-release-readiness-report",
|
||||
reportVersion: 1,
|
||||
apiName: PROJECT_RELEASE_READINESS_ARTIFACT_API,
|
||||
reportVersion: PROJECT_RELEASE_READINESS_ARTIFACT_VERSION,
|
||||
phase: ready ? "ready" : "waiting",
|
||||
ready,
|
||||
missing,
|
||||
@@ -152,3 +163,69 @@ export function createProjectReleaseReadinessReport({
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function parseProjectReleaseReadinessArtifactJson(text) {
|
||||
return JSON.parse(String(text));
|
||||
}
|
||||
|
||||
export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
|
||||
const artifactObject = objectOrEmpty(artifact);
|
||||
const releaseGate = objectOrEmpty(artifactObject.releaseGate);
|
||||
const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory);
|
||||
const gates = arrayOrEmpty(artifactObject.gates);
|
||||
const rows = arrayOrEmpty(artifactObject.rows);
|
||||
const gateCountReady = gates.length === REQUIRED_RELEASE_GATES.length;
|
||||
const gatesPassed = gateCountReady && gates.every(({ passed }) => passed === true);
|
||||
const missing = [
|
||||
...(artifactObject.apiName === PROJECT_RELEASE_READINESS_ARTIFACT_API ? [] : ["apiName"]),
|
||||
...(artifactObject.reportVersion === PROJECT_RELEASE_READINESS_ARTIFACT_VERSION ? [] : ["reportVersion"]),
|
||||
...(artifactObject.phase === "ready" ? [] : ["phase"]),
|
||||
...(artifactObject.ready === true ? [] : ["ready"]),
|
||||
...(Array.isArray(artifactObject.missing) && artifactObject.missing.length === 0 ? [] : ["missing"]),
|
||||
...(releaseGate.command === "wasm-port/tests/host/verify_project_release_gate.sh" ? [] : ["releaseGate.command"]),
|
||||
...(releaseGate.passed === true ? [] : ["releaseGate.passed"]),
|
||||
...(releaseGate.expectedOutput === "project_release_gate=ok" ? [] : ["releaseGate.expectedOutput"]),
|
||||
...(simConfigInventory.executed === 28 ? [] : ["simConfigInventory.executed"]),
|
||||
...(simConfigInventory.passed === 28 ? [] : ["simConfigInventory.passed"]),
|
||||
...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]),
|
||||
...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]),
|
||||
...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",")
|
||||
? []
|
||||
: ["blockedRuntimeFamilies"]),
|
||||
...(arrayOrEmpty(artifactObject.promotedBlockedFamilies).length === 0 ? [] : ["promotedBlockedFamilies"]),
|
||||
...(gateCountReady ? [] : ["gates.length"]),
|
||||
...(gatesPassed ? [] : ["gates.passed"]),
|
||||
...(rows.find(({ id, value }) => id === "project-release-gate" && value === "passed")
|
||||
? []
|
||||
: ["rows.project-release-gate"]),
|
||||
];
|
||||
|
||||
return {
|
||||
apiName: "project-release-readiness-artifact-validation",
|
||||
validationVersion: 1,
|
||||
phase: missing.length === 0 ? "ready" : "blocked",
|
||||
ready: missing.length === 0,
|
||||
missing,
|
||||
artifactApiName: artifactObject.apiName ?? null,
|
||||
artifactVersion: artifactObject.reportVersion ?? null,
|
||||
gateCount: gates.length,
|
||||
blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies),
|
||||
rows: [
|
||||
{
|
||||
id: "artifact",
|
||||
label: "Readiness artifact",
|
||||
value: artifactObject.apiName ?? "missing",
|
||||
},
|
||||
{
|
||||
id: "project-release-gate",
|
||||
label: "Project release gate",
|
||||
value: releaseGate.passed === true ? "passed" : "missing",
|
||||
},
|
||||
{
|
||||
id: "validation",
|
||||
label: "Artifact validation",
|
||||
value: missing.length === 0 ? "ready" : missing.join(", "),
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ for (const phrase of [
|
||||
"PROJECT_COMPLETION_TRACKER.md",
|
||||
"runtime/sdk/src/index.js",
|
||||
"createProjectReleaseReadinessReport()",
|
||||
"parseProjectReleaseReadinessArtifactJson()",
|
||||
"createProjectReleaseReadinessArtifactValidation()",
|
||||
"createIniPanelShellApiSurfaceInventory()",
|
||||
"createIniPanelShellSessionReadinessWorkflowReport()",
|
||||
"workflow overview embedding mount DOM",
|
||||
@@ -89,6 +91,8 @@ for (const phrase of [
|
||||
"OPFS/session persistence exports",
|
||||
"Project release readiness export",
|
||||
"createProjectReleaseReadinessReport()",
|
||||
"parseProjectReleaseReadinessArtifactJson()",
|
||||
"createProjectReleaseReadinessArtifactValidation()",
|
||||
"build/project-release-readiness.json",
|
||||
"INI panel shell handoff exports",
|
||||
"createIniPanelShellApiSurfaceInventory()",
|
||||
|
||||
@@ -3,33 +3,23 @@ import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
} from "../../runtime/sdk/src/index.js";
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const root = resolve(__dirname, "../..");
|
||||
const artifactPath = process.argv[2] ?? resolve(root, "build/project-release-readiness.json");
|
||||
const artifact = JSON.parse(readFileSync(artifactPath, "utf8"));
|
||||
const artifact = parseProjectReleaseReadinessArtifactJson(readFileSync(artifactPath, "utf8"));
|
||||
const validation = createProjectReleaseReadinessArtifactValidation(artifact);
|
||||
|
||||
assert.equal(artifact.apiName, "project-release-readiness-report");
|
||||
assert.equal(artifact.reportVersion, 1);
|
||||
assert.equal(artifact.phase, "ready");
|
||||
assert.equal(artifact.ready, true);
|
||||
assert.deepEqual(artifact.missing, []);
|
||||
assert.equal(artifact.releaseGate.command, "wasm-port/tests/host/verify_project_release_gate.sh");
|
||||
assert.equal(artifact.releaseGate.passed, true);
|
||||
assert.equal(artifact.releaseGate.expectedOutput, "project_release_gate=ok");
|
||||
assert.equal(artifact.simConfigInventory.executed, 28);
|
||||
assert.equal(artifact.simConfigInventory.passed, 28);
|
||||
assert.equal(artifact.simConfigInventory.skipped, 131);
|
||||
assert.equal(artifact.simConfigInventory.unexpectedFail, 0);
|
||||
assert.deepEqual(artifact.blockedRuntimeFamilies, [
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP",
|
||||
]);
|
||||
assert.deepEqual(artifact.promotedBlockedFamilies, []);
|
||||
assert.equal(artifact.gates.every(({ passed }) => passed === true), true);
|
||||
assert.equal(
|
||||
artifact.rows.find(({ id }) => id === "project-release-gate")?.value,
|
||||
"passed",
|
||||
);
|
||||
assert.equal(validation.apiName, "project-release-readiness-artifact-validation");
|
||||
assert.equal(validation.ready, true);
|
||||
assert.equal(validation.phase, "ready");
|
||||
assert.deepEqual(validation.missing, []);
|
||||
assert.equal(validation.artifactApiName, "project-release-readiness-report");
|
||||
assert.equal(validation.artifactVersion, 1);
|
||||
assert.equal(validation.gateCount, 9);
|
||||
|
||||
console.log("project_release_readiness_artifact_node_smoke=ok");
|
||||
|
||||
@@ -28,7 +28,9 @@ import {
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createProjectReleaseReadinessArtifactValidation,
|
||||
createProjectReleaseReadinessReport,
|
||||
parseProjectReleaseReadinessArtifactJson,
|
||||
createSessionSnapshot,
|
||||
defaultMachinePaths,
|
||||
gcodeFilenameFromProgramPath,
|
||||
@@ -73,7 +75,9 @@ const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md
|
||||
const requiredExports = [
|
||||
["createLinuxCncIniSdk", createLinuxCncIniSdk],
|
||||
["createLinuxCncInterpSdk", createLinuxCncInterpSdk],
|
||||
["createProjectReleaseReadinessArtifactValidation", createProjectReleaseReadinessArtifactValidation],
|
||||
["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport],
|
||||
["parseProjectReleaseReadinessArtifactJson", parseProjectReleaseReadinessArtifactJson],
|
||||
["planIniFileContextStaging", planIniFileContextStaging],
|
||||
["planSimConfigStaging", planSimConfigStaging],
|
||||
["analyzeIniRuntimeBoundaries", analyzeIniRuntimeBoundaries],
|
||||
@@ -170,6 +174,73 @@ const releaseReadinessReady = createProjectReleaseReadinessReport({
|
||||
});
|
||||
assert.equal(releaseReadinessReady.ready, true);
|
||||
assert.equal(releaseReadinessReady.releaseGate.passed, true);
|
||||
const releaseReadinessArtifact = parseProjectReleaseReadinessArtifactJson(JSON.stringify(createProjectReleaseReadinessReport({
|
||||
gateResults: {
|
||||
"diff-check": true,
|
||||
"vendor-sync": true,
|
||||
"standalone-cnc-semantics": true,
|
||||
"interp-wasm": true,
|
||||
"sim-config-inventory": true,
|
||||
"ini-panel-browser": true,
|
||||
"ui-node-smokes": true,
|
||||
"host-smokes": true,
|
||||
"project-release-gate": true,
|
||||
},
|
||||
})));
|
||||
assert.equal(releaseReadinessArtifact.apiName, "project-release-readiness-report");
|
||||
assert.deepEqual(createProjectReleaseReadinessArtifactValidation(releaseReadinessArtifact), {
|
||||
apiName: "project-release-readiness-artifact-validation",
|
||||
validationVersion: 1,
|
||||
phase: "ready",
|
||||
ready: true,
|
||||
missing: [],
|
||||
artifactApiName: "project-release-readiness-report",
|
||||
artifactVersion: 1,
|
||||
gateCount: 9,
|
||||
blockedRuntimeFamilies: [
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP",
|
||||
],
|
||||
rows: [
|
||||
{
|
||||
id: "artifact",
|
||||
label: "Readiness artifact",
|
||||
value: "project-release-readiness-report",
|
||||
},
|
||||
{
|
||||
id: "project-release-gate",
|
||||
label: "Project release gate",
|
||||
value: "passed",
|
||||
},
|
||||
{
|
||||
id: "validation",
|
||||
label: "Artifact validation",
|
||||
value: "ready",
|
||||
},
|
||||
],
|
||||
});
|
||||
assert.deepEqual(
|
||||
createProjectReleaseReadinessArtifactValidation({}).missing,
|
||||
[
|
||||
"apiName",
|
||||
"reportVersion",
|
||||
"phase",
|
||||
"ready",
|
||||
"missing",
|
||||
"releaseGate.command",
|
||||
"releaseGate.passed",
|
||||
"releaseGate.expectedOutput",
|
||||
"simConfigInventory.executed",
|
||||
"simConfigInventory.passed",
|
||||
"simConfigInventory.skipped",
|
||||
"simConfigInventory.unexpectedFail",
|
||||
"blockedRuntimeFamilies",
|
||||
"gates.length",
|
||||
"gates.passed",
|
||||
"rows.project-release-gate",
|
||||
],
|
||||
);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
|
||||
Reference in New Issue
Block a user