新增项目发布 readiness SDK report
This commit is contained in:
@@ -34,6 +34,12 @@ continuation records are in `../text14.txt`.
|
||||
`createIniPanelShellSessionReadinessWorkflowReport()` when they need
|
||||
machine-readable session readiness phase and missing reasons. The workflow
|
||||
overview exposes the API surface inventory as a read-only summary row.
|
||||
- CI dashboards and external SDK callers can use
|
||||
`createProjectReleaseReadinessReport()` for a machine-readable release
|
||||
readiness report covering required gate commands, expected smoke outputs, the
|
||||
sim-config inventory baseline, and blocked runtime families. The helper is
|
||||
evidence driven and only reports `ready: true` when the caller supplies
|
||||
release gate evidence such as `project_release_gate=ok`.
|
||||
- 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,
|
||||
createProjectReleaseReadinessReport,
|
||||
createSessionSnapshot,
|
||||
defaultMachinePaths,
|
||||
gcodeFilenameFromProgramPath,
|
||||
@@ -176,6 +177,20 @@ path but keeps the runner loop going after LinuxCNC reports an error, matching
|
||||
upstream `rs274 -n 0` regression tests such as `tests/interp/oword-unwind`.
|
||||
It does not implement or reinterpret LinuxCNC error semantics.
|
||||
|
||||
## Project release readiness export
|
||||
|
||||
`createProjectReleaseReadinessReport()` returns a machine-readable release
|
||||
readiness report for CI dashboards and external SDK callers. The report lists
|
||||
the required project release gate command, expected smoke outputs, the current
|
||||
sim-config inventory baseline, and blocked runtime families. It is evidence
|
||||
driven: the default report is `ready: false` until the caller supplies observed
|
||||
gate output such as `project_release_gate=ok` or explicit gate results.
|
||||
|
||||
This helper does not execute shell commands and does not inspect or implement
|
||||
CNC behavior. It only packages project-level release evidence and blocked
|
||||
runtime policy that are already documented in `docs/project-release-handoff.md`
|
||||
and `../PROJECT_COMPLETION_TRACKER.md`.
|
||||
|
||||
## OPFS/session persistence exports
|
||||
|
||||
The SDK entrypoint re-exports the OPFS/session persistence helpers used by the
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export { createLinuxCncIniSdk } from "./linuxcnc-ini.js";
|
||||
export { createLinuxCncInterpSdk } from "./linuxcnc-interp.js";
|
||||
export { createProjectReleaseReadinessReport } from "./project-release-readiness.js";
|
||||
export {
|
||||
analyzeIniRuntimeBoundaries,
|
||||
planIniFileContextStaging,
|
||||
|
||||
154
wasm-port/runtime/sdk/src/project-release-readiness.js
Normal file
154
wasm-port/runtime/sdk/src/project-release-readiness.js
Normal file
@@ -0,0 +1,154 @@
|
||||
const REQUIRED_RELEASE_GATES = [
|
||||
{
|
||||
id: "diff-check",
|
||||
label: "Git diff check",
|
||||
command: "git diff --check",
|
||||
},
|
||||
{
|
||||
id: "vendor-sync",
|
||||
label: "Vendor sync guard",
|
||||
command: "wasm-port/tools/verify_vendor_sync.sh",
|
||||
expectedOutput: "vendor sync up to date",
|
||||
},
|
||||
{
|
||||
id: "standalone-cnc-semantics",
|
||||
label: "Standalone CNC semantics guard",
|
||||
command: "wasm-port/tools/verify_no_standalone_cnc_semantics.sh",
|
||||
expectedOutput: "standalone CNC semantics guard complete",
|
||||
},
|
||||
{
|
||||
id: "interp-wasm",
|
||||
label: "Interpreter WASM Node smoke",
|
||||
command: "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_interp_wasm.sh",
|
||||
expectedOutput: "interp_wasm_node_smoke=ok",
|
||||
},
|
||||
{
|
||||
id: "sim-config-inventory",
|
||||
label: "Sim config inventory WASM Node smoke",
|
||||
command: "SKIP_INTERP_BUILD=1 wasm-port/tests/wasm/node/verify_sim_configs_inventory_wasm.sh",
|
||||
expectedOutput: "sim_configs_wasm_node_inventory_unexpected_fail=0",
|
||||
},
|
||||
{
|
||||
id: "ini-panel-browser",
|
||||
label: "INI panel browser smoke",
|
||||
command: "SKIP_INI_BUILD=1 SKIP_INTERP_BUILD=1 wasm-port/tests/browser/verify_ini_panel_browser.sh",
|
||||
expectedOutput: "browser_ini_shell_integration_workflow_smoke=ok",
|
||||
},
|
||||
{
|
||||
id: "ui-node-smokes",
|
||||
label: "UI Node smokes",
|
||||
command: "wasm-port/tests/ui/node/verify_ui_node_smokes.sh",
|
||||
expectedOutput: "ui_node_smokes=ok",
|
||||
},
|
||||
{
|
||||
id: "host-smokes",
|
||||
label: "Host aggregate smoke",
|
||||
command: "wasm-port/tests/host/verify_host_smokes.sh",
|
||||
expectedOutput: "host_wasm_opfs_browser_smokes=ok",
|
||||
},
|
||||
{
|
||||
id: "project-release-gate",
|
||||
label: "Project release gate",
|
||||
command: "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
expectedOutput: "project_release_gate=ok",
|
||||
},
|
||||
];
|
||||
|
||||
const DEFAULT_SIM_CONFIG_INVENTORY_BASELINE = {
|
||||
executed: 28,
|
||||
passed: 28,
|
||||
skipped: 131,
|
||||
unexpectedFail: 0,
|
||||
};
|
||||
|
||||
const BLOCKED_RUNTIME_FAMILIES = [
|
||||
"L4-USER-M-PROCESS",
|
||||
"L4-TOOL-DB",
|
||||
"L4-PYTHON-REMAP",
|
||||
];
|
||||
|
||||
function outputContains(observedOutputs, expectedOutput) {
|
||||
return observedOutputs.some((output) => String(output).includes(expectedOutput));
|
||||
}
|
||||
|
||||
function readGatePassed({ gate, gateResults, observedOutputs }) {
|
||||
if (gateResults?.[gate.id] === true) {
|
||||
return true;
|
||||
}
|
||||
if (gate.expectedOutput && outputContains(observedOutputs, gate.expectedOutput)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function createProjectReleaseReadinessReport({
|
||||
gateResults = {},
|
||||
observedOutputs = [],
|
||||
simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES,
|
||||
promotedRuntimeFamilies = [],
|
||||
} = {}) {
|
||||
const outputs = Array.isArray(observedOutputs) ? observedOutputs : [];
|
||||
const gateRows = REQUIRED_RELEASE_GATES.map((gate) => {
|
||||
const passed = readGatePassed({ gate, gateResults, observedOutputs: outputs });
|
||||
return {
|
||||
id: gate.id,
|
||||
label: gate.label,
|
||||
command: gate.command,
|
||||
expectedOutput: gate.expectedOutput ?? null,
|
||||
status: passed ? "passed" : "unknown",
|
||||
passed,
|
||||
};
|
||||
});
|
||||
const releaseGatePassed = gateRows.find(({ id }) => id === "project-release-gate")?.passed === true;
|
||||
const inventory = {
|
||||
...DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
|
||||
...(simConfigInventory && typeof simConfigInventory === "object" ? simConfigInventory : {}),
|
||||
};
|
||||
const blockedFamilies = [...blockedRuntimeFamilies];
|
||||
const promotedFamilies = [...promotedRuntimeFamilies];
|
||||
const promotedBlockedFamilies = promotedFamilies.filter((family) => blockedFamilies.includes(family));
|
||||
const inventoryReady = inventory.unexpectedFail === 0;
|
||||
const blockedRuntimeReady = promotedBlockedFamilies.length === 0;
|
||||
const ready = releaseGatePassed && inventoryReady && blockedRuntimeReady;
|
||||
const missing = [
|
||||
...(releaseGatePassed ? [] : ["project-release-gate"]),
|
||||
...(inventoryReady ? [] : ["sim-config-inventory"]),
|
||||
...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]),
|
||||
];
|
||||
|
||||
return {
|
||||
apiName: "project-release-readiness-report",
|
||||
reportVersion: 1,
|
||||
phase: ready ? "ready" : "waiting",
|
||||
ready,
|
||||
missing,
|
||||
releaseGate: {
|
||||
command: "wasm-port/tests/host/verify_project_release_gate.sh",
|
||||
passed: releaseGatePassed,
|
||||
expectedOutput: "project_release_gate=ok",
|
||||
},
|
||||
simConfigInventory: inventory,
|
||||
blockedRuntimeFamilies: blockedFamilies,
|
||||
promotedRuntimeFamilies: promotedFamilies,
|
||||
promotedBlockedFamilies,
|
||||
gates: gateRows,
|
||||
rows: [
|
||||
{
|
||||
id: "project-release-gate",
|
||||
label: "Project release gate",
|
||||
value: releaseGatePassed ? "passed" : "unknown",
|
||||
},
|
||||
{
|
||||
id: "sim-config-inventory",
|
||||
label: "Sim config inventory",
|
||||
value: `unexpected_fail=${inventory.unexpectedFail}`,
|
||||
},
|
||||
{
|
||||
id: "blocked-runtime-families",
|
||||
label: "Blocked runtime families",
|
||||
value: blockedRuntimeReady ? blockedFamilies.join(", ") : `promoted: ${promotedBlockedFamilies.join(", ")}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
@@ -31,6 +31,7 @@ for (const phrase of [
|
||||
"docs/drift-report.md",
|
||||
"PROJECT_COMPLETION_TRACKER.md",
|
||||
"runtime/sdk/src/index.js",
|
||||
"createProjectReleaseReadinessReport()",
|
||||
"createIniPanelShellApiSurfaceInventory()",
|
||||
"createIniPanelShellSessionReadinessWorkflowReport()",
|
||||
"workflow overview embedding mount DOM",
|
||||
@@ -81,6 +82,8 @@ for (const phrase of [
|
||||
|
||||
for (const phrase of [
|
||||
"OPFS/session persistence exports",
|
||||
"Project release readiness export",
|
||||
"createProjectReleaseReadinessReport()",
|
||||
"INI panel shell handoff exports",
|
||||
"createIniPanelShellApiSurfaceInventory()",
|
||||
"createIniPanelShellSessionReadinessWorkflowReport()",
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
createMachineSessionPersistenceDisplayViewModel,
|
||||
createMachineSessionPersistenceRenderState,
|
||||
createMachineSessionPersistenceSummary,
|
||||
createProjectReleaseReadinessReport,
|
||||
createSessionSnapshot,
|
||||
defaultMachinePaths,
|
||||
gcodeFilenameFromProgramPath,
|
||||
@@ -72,6 +73,7 @@ const trackerText = readFileSync(resolve(root, "../PROJECT_COMPLETION_TRACKER.md
|
||||
const requiredExports = [
|
||||
["createLinuxCncIniSdk", createLinuxCncIniSdk],
|
||||
["createLinuxCncInterpSdk", createLinuxCncInterpSdk],
|
||||
["createProjectReleaseReadinessReport", createProjectReleaseReadinessReport],
|
||||
["planIniFileContextStaging", planIniFileContextStaging],
|
||||
["planSimConfigStaging", planSimConfigStaging],
|
||||
["analyzeIniRuntimeBoundaries", analyzeIniRuntimeBoundaries],
|
||||
@@ -153,6 +155,29 @@ assert.equal(sessionSnapshotPath("sdk-session"), "linuxcnc/sessions/sdk-session/
|
||||
assert.equal(normalizeOpfsPath("linuxcnc/machines/sdk-mill/tool.tbl"), "linuxcnc/machines/sdk-mill/tool.tbl");
|
||||
assert.equal(gcodeFilenameFromProgramPath("linuxcnc/gcode/demo.ngc"), "demo.ngc");
|
||||
|
||||
const releaseReadinessWaiting = createProjectReleaseReadinessReport();
|
||||
assert.equal(releaseReadinessWaiting.apiName, "project-release-readiness-report");
|
||||
assert.equal(releaseReadinessWaiting.ready, false);
|
||||
assert.deepEqual(releaseReadinessWaiting.missing, ["project-release-gate"]);
|
||||
assert.equal(releaseReadinessWaiting.simConfigInventory.unexpectedFail, 0);
|
||||
assert.deepEqual(releaseReadinessWaiting.promotedBlockedFamilies, []);
|
||||
assert.equal(
|
||||
releaseReadinessWaiting.rows.find(({ id }) => id === "blocked-runtime-families")?.value,
|
||||
"L4-USER-M-PROCESS, L4-TOOL-DB, L4-PYTHON-REMAP",
|
||||
);
|
||||
const releaseReadinessReady = createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
});
|
||||
assert.equal(releaseReadinessReady.ready, true);
|
||||
assert.equal(releaseReadinessReady.releaseGate.passed, true);
|
||||
assert.equal(
|
||||
createProjectReleaseReadinessReport({
|
||||
observedOutputs: ["project_release_gate=ok"],
|
||||
promotedRuntimeFamilies: ["L4-TOOL-DB"],
|
||||
}).missing.includes("blocked-runtime-families"),
|
||||
true,
|
||||
);
|
||||
|
||||
assert.deepEqual(getIniPanelEntryManifest(), INI_PANEL_ENTRIES);
|
||||
assert.deepEqual(getVisibleIniPanelEntries(), INI_PANEL_ENTRIES);
|
||||
assert.deepEqual(getIniPanelEntryByHref("./control-page.html"), {
|
||||
|
||||
Reference in New Issue
Block a user