Add GLB stage manifests

This commit is contained in:
2026-08-04 09:45:51 +08:00
parent b7b5530430
commit 2832383f6e
17 changed files with 579 additions and 27 deletions

View File

@@ -5,6 +5,7 @@ const fs = require("fs");
const path = require("path");
const { readAreaConfig } = require("./lib/area-config");
const { digest: glbDigest } = require("./glb-digest");
const { fileRecord, readStageManifest, stageManifestPath } = require("./lib/stage-manifest");
const repoRoot = path.resolve(__dirname, "..");
const DEFAULT_CONFIG = path.join(repoRoot, "config", "areas", "nantaizi-lake-innovation-valley.json");
@@ -293,7 +294,106 @@ function metadataSummary(file, warnings) {
}
}
function collectWarnings(area, osm, artifacts, glb, metadata) {
function stageManifestStatus(area) {
const stages = [
{
stage: "cesium",
expected: fs.existsSync(area.outputs.glb),
inputs: {
blend: area.outputs.blend,
},
outputs: {
glb: area.outputs.glb,
metadata: area.outputs.metadata,
cesiumPreview: area.outputs.cesiumPreview,
},
},
{
stage: "compress",
expected: fs.existsSync(area.outputs.compressedGlb),
inputs: {
glb: area.outputs.glb,
metadata: area.outputs.metadata,
cesiumPreview: area.outputs.cesiumPreview,
},
outputs: {
compressedGlb: area.outputs.compressedGlb,
compressedMetadata: area.outputs.compressedMetadata,
compressedCesiumPreview: area.outputs.compressedCesiumPreview,
},
},
];
return stages.map((entry) => {
const file = stageManifestPath(area, entry.stage);
try {
const manifest = readStageManifest(area, entry.stage);
if (!manifest) {
return {
stage: entry.stage,
path: file,
expected: entry.expected,
exists: false,
valid: false,
fresh: false,
manifestWarnings: [],
issues: entry.expected ? ["manifest missing"] : [],
};
}
const issues = [
...manifestFileIssues(manifest.inputs || {}, entry.inputs, "input"),
...manifestFileIssues(manifest.outputs || {}, entry.outputs, "output"),
];
return {
stage: entry.stage,
path: file,
expected: entry.expected,
exists: true,
valid: true,
fresh: issues.length === 0,
finishedAt: manifest.finishedAt || null,
durationMs: manifest.durationMs ?? null,
summary: manifest.summary || null,
manifestWarnings: Array.isArray(manifest.warnings) ? manifest.warnings : [],
issues,
};
} catch (error) {
return {
stage: entry.stage,
path: file,
expected: entry.expected,
exists: fs.existsSync(file),
valid: false,
fresh: false,
manifestWarnings: [],
issues: [`manifest unreadable: ${error.message}`],
};
}
});
}
function manifestFileIssues(records, expectedFiles, label) {
const issues = [];
for (const [key, file] of Object.entries(expectedFiles)) {
const recorded = records[key];
if (!recorded) {
issues.push(`${label} ${key} not recorded`);
continue;
}
if (!fs.existsSync(file)) {
issues.push(`${label} ${key} file missing`);
continue;
}
const current = fileRecord(file);
if (recorded.bytes !== current.bytes) {
issues.push(`${label} ${key} bytes changed`);
} else if (recorded.sha256 && recorded.sha256 !== current.sha256) {
issues.push(`${label} ${key} sha256 changed`);
}
}
return issues;
}
function collectWarnings(area, osm, artifacts, manifests, glb, metadata) {
const warnings = [];
if (!osm.bounds) warnings.push("OSM has no valid <bounds>; scene extent may be wrong.");
if (osm.ways.missingNodeRefs) {
@@ -312,6 +412,18 @@ function collectWarnings(area, osm, artifacts, glb, metadata) {
warnings.push(`Artifact has wrong type: ${artifact.label} (${artifact.path}).`);
}
}
for (const manifest of manifests) {
if (manifest.expected && !manifest.exists) {
warnings.push(`Expected stage manifest missing: ${manifest.stage} (${manifest.path}).`);
} else if (manifest.exists && !manifest.valid) {
warnings.push(`Stage manifest invalid: ${manifest.stage} (${manifest.issues.join("; ")}).`);
} else if (manifest.exists && !manifest.fresh) {
warnings.push(`Stage manifest stale: ${manifest.stage} (${manifest.issues.join("; ")}).`);
}
for (const warning of manifest.manifestWarnings) {
warnings.push(`Stage manifest warning (${manifest.stage}): ${warning}.`);
}
}
if (glb) {
if (glb.fileBytes > BUDGETS.glbBytes) {
warnings.push(`GLB size ${mb(glb.fileBytes)} MB exceeds budget ${mb(BUDGETS.glbBytes)} MB.`);
@@ -332,7 +444,7 @@ function collectWarnings(area, osm, artifacts, glb, metadata) {
return warnings;
}
function printReport(area, configPath, osm, artifacts, glb, metadata, warnings) {
function printReport(area, configPath, osm, artifacts, manifests, glb, metadata, warnings) {
console.log("Area diagnostics");
console.log(`Area: ${area.id}`);
console.log(`Config: ${configPath}`);
@@ -381,6 +493,31 @@ function printReport(area, configPath, osm, artifacts, glb, metadata, warnings)
if (metadata) {
console.log(` metadata asset: ${metadata.asset || "missing"}, assets: ${metadata.assets}`);
}
console.log("");
console.log("Stage manifests");
for (const manifest of manifests) {
let state = "absent";
if (manifest.exists && !manifest.valid) state = "invalid";
else if (manifest.exists && !manifest.fresh) state = "stale";
else if (manifest.exists) state = "ok";
else if (manifest.expected) state = "missing";
const timing = manifest.finishedAt
? `, finished ${manifest.finishedAt}, ${manifest.durationMs ?? "?"} ms`
: "";
const issues = manifest.issues.length ? `, ${manifest.issues.join("; ")}` : "";
console.log(` ${state.padEnd(7)} ${manifest.stage}: ${manifest.path}${timing}${issues}`);
for (const warning of manifest.manifestWarnings) {
console.log(` warning: ${warning}`);
}
const glbSummary = manifest.summary?.glb || manifest.summary?.compressedGlb;
if (glbSummary?.counts) {
console.log(
` GLB: ${formatBytes(glbSummary.fileBytes)}, ${glbSummary.counts.nodes} nodes, ` +
`${glbSummary.counts.meshes} meshes, ${glbSummary.counts.images} images`,
);
}
}
if (glb) {
console.log("");
console.log("GLB digest");
@@ -425,14 +562,15 @@ function main() {
const area = readAreaConfig(configPath, { repoRoot });
const osm = parseOsm(fs.readFileSync(area.input, "utf8"));
const artifacts = artifactStatus(area);
const manifests = stageManifestStatus(area);
const metadataWarnings = [];
const metadata = metadataSummary(area.outputs.metadata, metadataWarnings);
const glb = fs.existsSync(area.outputs.glb) ? glbDigest(area.outputs.glb) : null;
const warnings = [
...metadataWarnings,
...collectWarnings(area, osm, artifacts, glb, metadata),
...collectWarnings(area, osm, artifacts, manifests, glb, metadata),
];
printReport(area, configPath, osm, artifacts, glb, metadata, warnings);
printReport(area, configPath, osm, artifacts, manifests, glb, metadata, warnings);
}
main();