Add full stage manifests

This commit is contained in:
2026-08-04 11:37:20 +08:00
parent 0b7e69770c
commit 5874cf9379
9 changed files with 516 additions and 21 deletions

View File

@@ -3,6 +3,12 @@
const fs = require("fs");
const path = require("path");
const { readAreaConfig } = require("./area-config");
const {
SCENE_LAYERS,
SCENE_FILE,
SCENE_STYLE_FILE,
layerFile,
} = require("./scene-layers");
const { digest: glbDigest } = require("../glb-digest");
const { BUDGETS, fileRecord, readStageManifest, stageManifestPath } = require("./stage-manifest");
@@ -16,7 +22,7 @@ function analyzeArea(configPath, options = {}) {
const area = readAreaConfig(resolvedConfig, { repoRoot });
const osm = parseOsm(fs.readFileSync(area.input, "utf8"));
const artifacts = artifactStatus(area);
const manifests = stageManifestStatus(area);
const manifests = stageManifestStatus(area, resolvedConfig);
const metadataWarnings = [];
const metadata = metadataSummary(area.outputs.metadata, metadataWarnings);
const glb = fs.existsSync(area.outputs.glb) ? glbDigest(area.outputs.glb) : null;
@@ -296,8 +302,58 @@ function metadataSummary(file, warnings) {
}
}
function stageManifestStatus(area) {
function stageManifestStatus(area, configPath = null) {
const reimportManifest = stageManifestPath(area, "reimport");
const hasReimportManifest = fs.existsSync(reimportManifest);
const derivedConfig = path.join(area.outputs.pipelineDir, "osm2streets-qgis.config.json");
const stages = [
{
stage: "intermediates",
expected: (
fs.existsSync(area.outputs.geojsonDir) ||
fs.existsSync(area.outputs.gpkg) ||
fs.existsSync(area.outputs.qgisProject)
) && !hasReimportManifest,
inputs: {
...(configPath ? { config: configPath } : {}),
osm: area.input,
},
outputs: {
derivedConfig,
geojsonDir: area.outputs.geojsonDir,
...sceneGeojsonFiles(area),
gpkg: area.outputs.gpkg,
qgisProject: area.outputs.qgisProject,
qgisPreview: optionalExpectedFile(area.outputs.qgisPreview),
},
},
{
stage: "reimport",
expected: hasReimportManifest,
inputs: {
...(configPath ? { config: configPath } : {}),
derivedConfig,
gpkg: area.outputs.gpkg,
},
outputs: {
geojsonDir: area.outputs.geojsonDir,
...sceneGeojsonFiles(area),
},
},
{
stage: "blender",
expected: fs.existsSync(area.outputs.blend),
inputs: {
...(configPath ? { config: configPath } : {}),
osm: area.input,
geojsonDir: area.outputs.geojsonDir,
...sceneGeojsonFiles(area),
},
outputs: {
blend: area.outputs.blend,
render: area.outputs.render,
},
},
{
stage: "cesium",
expected: fs.existsSync(area.outputs.glb),
@@ -307,7 +363,23 @@ function stageManifestStatus(area) {
outputs: {
glb: area.outputs.glb,
metadata: area.outputs.metadata,
},
},
{
stage: "preview",
expected: fs.existsSync(area.outputs.cesiumPreview),
inputs: {
...(configPath ? { config: configPath } : {}),
osm: area.input,
glb: area.outputs.glb,
metadata: area.outputs.metadata,
previewCss: path.join(path.resolve(__dirname, ".."), "lib", "cesium-preview.css"),
previewJs: path.join(path.resolve(__dirname, ".."), "lib", "cesium-preview.js"),
},
outputs: {
cesiumPreview: area.outputs.cesiumPreview,
vehicleRoute: area.outputs.vehicleRoute,
vehicleModel: area.outputs.vehicleModel,
},
},
{
@@ -375,12 +447,19 @@ function stageManifestStatus(area) {
function manifestFileIssues(records, expectedFiles, label) {
const issues = [];
for (const [key, file] of Object.entries(expectedFiles)) {
for (const [key, expected] of Object.entries(expectedFiles)) {
const { file, required } = normalizeExpectedFile(expected);
const recorded = records[key];
if (!required && !recorded && !fs.existsSync(file)) {
continue;
}
if (!recorded) {
issues.push(`${label} ${key} not recorded`);
continue;
}
if (!required && recorded === null && !fs.existsSync(file)) {
continue;
}
if (!fs.existsSync(file)) {
issues.push(`${label} ${key} file missing`);
continue;
@@ -395,6 +474,33 @@ function manifestFileIssues(records, expectedFiles, label) {
return issues;
}
function normalizeExpectedFile(expected) {
if (expected && typeof expected === "object" && expected.path) {
return {
file: expected.path,
required: expected.required !== false,
};
}
return {
file: expected,
required: true,
};
}
function optionalExpectedFile(file) {
return { path: file, required: false };
}
function sceneGeojsonFiles(area) {
const files = {};
for (const layer of SCENE_LAYERS) {
files[layer.id] = path.join(area.outputs.geojsonDir, layerFile(layer));
}
files[SCENE_FILE] = path.join(area.outputs.geojsonDir, SCENE_FILE);
files[SCENE_STYLE_FILE] = path.join(area.outputs.geojsonDir, SCENE_STYLE_FILE);
return files;
}
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.");