Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7140e99e1 | ||
|
|
76b4f93709 | ||
|
|
bc6fa3d517 | ||
|
|
f4fabb66e5 | ||
|
|
4a0fb1bfae |
@@ -228,7 +228,13 @@ function readCsv(tracker: SourceTracker, path: string, label: string): CsvRow[]
|
||||
|
||||
function itemDirectoryFor(collection: CollectionConfig, catalogPath: string, row: CsvRow): { directory: string; metadataPath: string } {
|
||||
if (collection.id === "font_panel") {
|
||||
const resourceDir = requireString(row.resource_dir, "font resource_dir");
|
||||
const configuredResourceDir = requireString(row.resource_dir, "font resource_dir");
|
||||
const normalizedResourceDir = configuredResourceDir.replaceAll("\\", "/");
|
||||
const relocationMarker = "/resources/font_packages/";
|
||||
const markerIndex = normalizedResourceDir.lastIndexOf(relocationMarker);
|
||||
const resourceDir = isAbsolute(configuredResourceDir) && !inside(configuredResourceDir, collection.root.path) && markerIndex >= 0
|
||||
? relativeReference(normalizedResourceDir.slice(markerIndex + 1), "font resource_dir relocation")
|
||||
: configuredResourceDir;
|
||||
const directory = resolveSourcePath(resourceDir, collection.root.path, collection.root.path, "font resource_dir");
|
||||
return { directory, metadataPath: resolveSourcePath("metadata.json", directory, collection.root.path, "font metadata") };
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
|
||||
import { compileAssetArchive, compileStaticStickerCatalog } from "../packages/asset-compiler/dist/index.js";
|
||||
import { createP0aColorCardRenderPlans } from "../packages/asset-renderer/dist/index.js";
|
||||
@@ -18,18 +18,38 @@ import {
|
||||
const runDirectory = resolve(process.env.DADA_WP5_03_RUN_DIRECTORY ?? "artifacts/tdd/wp5-03-local");
|
||||
const whiteDirectory = resolve(process.env.DADA_WP5_03_WHITE_EVIDENCE_DIR ?? join(runDirectory, "cases", "TDD-WP5-WHITE-001-p0a-allowlist"));
|
||||
const colorDirectory = resolve(process.env.DADA_WP5_03_COLOR_EVIDENCE_DIR ?? join(runDirectory, "cases", "TDD-WP5-COL-001-four-layouts"));
|
||||
const handoffManifest = resolve(process.env.DADA_COMPLEX_ASSET_MANIFEST ?? join(homedir(), "Desktop", "sticker_web_handoff", "sticker_web_catalog_manifest.json"));
|
||||
const stickerRoot = resolve(process.env.DADA_STATIC_STICKER_ROOT ?? join(homedir(), "Desktop", "贴纸素材"));
|
||||
const replicationRoot = resolve(process.env.DADA_REPLICATION_ASSET_ROOT ?? join(homedir(), "Desktop", "sticker_web_replication_assets"));
|
||||
const handoffManifest = resolve(process.env.DADA_COMPLEX_ASSET_MANIFEST ?? join(replicationRoot, "sticker_web_handoff", "sticker_web_catalog_manifest.json"));
|
||||
const stickerRoot = resolve(process.env.DADA_STATIC_STICKER_ROOT ?? join(replicationRoot, "sticker_normal"));
|
||||
if (!existsSync(handoffManifest)) throw new Error("normalized complex asset handoff is unavailable");
|
||||
if (!existsSync(stickerRoot)) throw new Error("static sticker source is unavailable");
|
||||
|
||||
const complexDirectory = resolve(runDirectory, "inputs", "complex");
|
||||
const staticDirectory = resolve(runDirectory, "inputs", "static");
|
||||
const normalizedHandoffDirectory = resolve(runDirectory, "inputs", "normalized-handoff");
|
||||
mkdirSync(whiteDirectory, { recursive: true });
|
||||
mkdirSync(colorDirectory, { recursive: true });
|
||||
|
||||
const sourceHandoff = JSON.parse(readFileSync(handoffManifest, "utf8"));
|
||||
const normalizedHandoff = {
|
||||
...sourceHandoff,
|
||||
web_handoff: "STICKER_WEB_REPLICATION_HANDOFF.md",
|
||||
validation: "sticker_archive_validation_20260722.json",
|
||||
collections: sourceHandoff.collections
|
||||
.filter((collection) => collection.id !== "normal_stickers")
|
||||
.map((collection) => ({
|
||||
...collection,
|
||||
root: resolve(dirname(handoffManifest), collection.root),
|
||||
})),
|
||||
};
|
||||
const normalizedHandoffPath = resolve(normalizedHandoffDirectory, "sticker_web_catalog_manifest.normalized.json");
|
||||
mkdirSync(normalizedHandoffDirectory, { recursive: true });
|
||||
copyFileSync(resolve(dirname(handoffManifest), sourceHandoff.web_handoff), resolve(normalizedHandoffDirectory, normalizedHandoff.web_handoff));
|
||||
copyFileSync(resolve(dirname(handoffManifest), sourceHandoff.validation), resolve(normalizedHandoffDirectory, normalizedHandoff.validation));
|
||||
writeFileSync(normalizedHandoffPath, `${JSON.stringify(normalizedHandoff, null, 2)}\n`);
|
||||
|
||||
const complex = compileAssetArchive({
|
||||
manifestPath: handoffManifest,
|
||||
manifestPath: normalizedHandoffPath,
|
||||
outputDirectory: complexDirectory,
|
||||
releaseVersion: P0A_COMPLEX_RELEASE_VERSION,
|
||||
});
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { homedir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
const phaseIndex = process.argv.indexOf("--phase");
|
||||
const phase = phaseIndex >= 0 ? process.argv[phaseIndex + 1] : "green";
|
||||
@@ -10,6 +11,7 @@ if (!new Set(["red", "green"]).has(phase)) throw new Error(`Unsupported phase: $
|
||||
const runId = process.env.DADA_TDD_RUN_ID ?? `wp6-04-${phase}-${new Date().toISOString().replace(/[^0-9]/g, "")}`;
|
||||
const runDirectory = resolve("artifacts", "tdd", runId);
|
||||
const caseDirectory = resolve(runDirectory, "cases", "TDD-WP6-AUD-001-sensitive-operations");
|
||||
const replicationRoot = resolve(process.env.DADA_REPLICATION_ASSET_ROOT ?? join(homedir(), "Desktop", "sticker_web_replication_assets"));
|
||||
if (existsSync(runDirectory)) throw new Error(`Evidence run already exists: ${runId}`);
|
||||
mkdirSync(caseDirectory, { recursive: true });
|
||||
|
||||
@@ -17,6 +19,9 @@ const environment = {
|
||||
...process.env,
|
||||
DADA_EVIDENCE_DIR_WP6_AUD: caseDirectory,
|
||||
DADA_PLAYWRIGHT_OUTPUT_DIR: resolve(runDirectory, "playwright-output"),
|
||||
DADA_STATIC_STICKER_ROOT: process.env.DADA_STATIC_STICKER_ROOT ?? join(replicationRoot, "sticker_normal"),
|
||||
DADA_DYNAMIC_ASSET_ROOT: process.env.DADA_DYNAMIC_ASSET_ROOT ?? join(replicationRoot, "sticker_interactive", "单模板归档", "templates"),
|
||||
DADA_TEXT_ASSET_ROOT: process.env.DADA_TEXT_ASSET_ROOT ?? join(replicationRoot, "sticker_text"),
|
||||
};
|
||||
const commands = phase === "red"
|
||||
? [
|
||||
@@ -25,8 +30,8 @@ const commands = phase === "red"
|
||||
["e2e-red", ".\\node_modules\\.bin\\playwright.CMD test tests/e2e/wp6-04-audit.spec.ts --config playwright.config.ts"],
|
||||
]
|
||||
: [
|
||||
["integration", "pnpm.cmd test:integration"],
|
||||
["api", "pnpm.cmd test:api"],
|
||||
["integration", "pnpm.cmd exec vitest run tests/integration --testTimeout=20000"],
|
||||
["api", "pnpm.cmd check:openapi && pnpm.cmd exec vitest run tests/api --testTimeout=20000"],
|
||||
["worker", "pnpm.cmd test:worker"],
|
||||
["e2e", "pnpm.cmd test:e2e"],
|
||||
["tdd-trace", "pnpm.cmd validate:tdd-trace"],
|
||||
|
||||
@@ -190,6 +190,27 @@ describe("TDD-WP5-MAN-001 readonly asset compiler", () => {
|
||||
expect(repeated.report.derived_files).toEqual({ created: 0, reused: 4, total: 4 });
|
||||
});
|
||||
|
||||
it("safely relocates legacy absolute font package paths after an archive move", () => {
|
||||
const fixture = createFixture();
|
||||
const catalogPath = join(fixture.sourceRoot, "fonts", "reports", "font_panel_catalog.csv");
|
||||
const metadata = JSON.parse(readFileSync(join(fixture.sourceRoot, "fonts", "resources", "font_packages", "FONT001_Test", "metadata.json"), "utf8")) as { local_sha256: string };
|
||||
csv(catalogPath, [{
|
||||
candidate_id: "FONT001",
|
||||
display_name: "Test Font",
|
||||
font_family: "Dada Test",
|
||||
local_sha256: metadata.local_sha256,
|
||||
panel_order: "1",
|
||||
resource_dir: "C:/Users/legacy/Desktop/sticker_text/fonts/resources/font_packages/FONT001_Test",
|
||||
resource_status: "verified_extracted",
|
||||
}]);
|
||||
|
||||
expect(() => compileAssetArchive({
|
||||
manifestPath: fixture.manifestPath,
|
||||
outputDirectory: fixture.outputRoot,
|
||||
releaseVersion: "fixture-v1",
|
||||
})).not.toThrow();
|
||||
});
|
||||
|
||||
it("rejects evidence collections, traversal and output inside a source root", () => {
|
||||
const fixture = createFixture();
|
||||
const manifest = JSON.parse(readFileSync(fixture.manifestPath, "utf8")) as { collections: unknown[] };
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
||||
import { homedir } from "node:os";
|
||||
import { basename, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
||||
|
||||
import { WP4_07_REAL_RESOURCE_VERSIONS } from "./wp4-07-fixture.mjs";
|
||||
|
||||
@@ -74,15 +74,16 @@ function assetRecord(assetId, path, sourceReference, expectedSha256) {
|
||||
export function loadWp407RealAssets() {
|
||||
const manifestPath = resolve(process.env.DADA_WP4_07_FINAL_ASSET_MANIFEST ?? "");
|
||||
if (!process.env.DADA_WP4_07_FINAL_ASSET_MANIFEST || !existsSync(manifestPath)) throw new Error("WP4_07_FINAL_ASSET_MANIFEST_REQUIRED");
|
||||
const handoffPath = resolve(process.env.DADA_COMPLEX_ASSET_MANIFEST ?? join(homedir(), "Desktop", "sticker_web_handoff", "sticker_web_catalog_manifest.json"));
|
||||
const staticRoot = resolve(process.env.DADA_STATIC_STICKER_ROOT ?? join(homedir(), "Desktop", "贴纸素材"));
|
||||
const replicationRoot = resolve(process.env.DADA_REPLICATION_ASSET_ROOT ?? join(homedir(), "Desktop", "sticker_web_replication_assets"));
|
||||
const handoffPath = resolve(process.env.DADA_COMPLEX_ASSET_MANIFEST ?? join(replicationRoot, "sticker_web_handoff", "sticker_web_catalog_manifest.json"));
|
||||
const staticRoot = resolve(process.env.DADA_STATIC_STICKER_ROOT ?? join(replicationRoot, "sticker_normal"));
|
||||
const backgroundPath = resolve(process.env.DADA_WP4_07_BACKGROUND_PATH ?? join(homedir(), "Documents", "贴纸脚本", "time_01_input_20260716.png"));
|
||||
if (!existsSync(handoffPath) || !existsSync(staticRoot)) throw new Error("WP4_07_REAL_ARCHIVE_ROOT_REQUIRED");
|
||||
|
||||
const manifestRaw = readFileSync(manifestPath, "utf8");
|
||||
const manifest = JSON.parse(manifestRaw);
|
||||
const handoff = JSON.parse(readFileSync(handoffPath, "utf8"));
|
||||
const collectionRoots = Object.fromEntries(handoff.collections.map((collection) => [collection.id, resolve(collection.root)]));
|
||||
const collectionRoots = Object.fromEntries(handoff.collections.map((collection) => [collection.id, resolve(dirname(handoffPath), collection.root)]));
|
||||
const fontRoot = collectionRoots.font_panel;
|
||||
const dynamicRoot = collectionRoots.interactive_stickers;
|
||||
if (!fontRoot || !dynamicRoot) throw new Error("WP4_07_REAL_ARCHIVE_COLLECTION_REQUIRED");
|
||||
|
||||
Reference in New Issue
Block a user