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 } {
|
function itemDirectoryFor(collection: CollectionConfig, catalogPath: string, row: CsvRow): { directory: string; metadataPath: string } {
|
||||||
if (collection.id === "font_panel") {
|
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");
|
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") };
|
return { directory, metadataPath: resolveSourcePath("metadata.json", directory, collection.root.path, "font metadata") };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createHash } from "node:crypto";
|
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 { 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 { compileAssetArchive, compileStaticStickerCatalog } from "../packages/asset-compiler/dist/index.js";
|
||||||
import { createP0aColorCardRenderPlans } from "../packages/asset-renderer/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 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 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 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 replicationRoot = resolve(process.env.DADA_REPLICATION_ASSET_ROOT ?? join(homedir(), "Desktop", "sticker_web_replication_assets"));
|
||||||
const stickerRoot = resolve(process.env.DADA_STATIC_STICKER_ROOT ?? join(homedir(), "Desktop", "贴纸素材"));
|
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(handoffManifest)) throw new Error("normalized complex asset handoff is unavailable");
|
||||||
if (!existsSync(stickerRoot)) throw new Error("static sticker source is unavailable");
|
if (!existsSync(stickerRoot)) throw new Error("static sticker source is unavailable");
|
||||||
|
|
||||||
const complexDirectory = resolve(runDirectory, "inputs", "complex");
|
const complexDirectory = resolve(runDirectory, "inputs", "complex");
|
||||||
const staticDirectory = resolve(runDirectory, "inputs", "static");
|
const staticDirectory = resolve(runDirectory, "inputs", "static");
|
||||||
|
const normalizedHandoffDirectory = resolve(runDirectory, "inputs", "normalized-handoff");
|
||||||
mkdirSync(whiteDirectory, { recursive: true });
|
mkdirSync(whiteDirectory, { recursive: true });
|
||||||
mkdirSync(colorDirectory, { 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({
|
const complex = compileAssetArchive({
|
||||||
manifestPath: handoffManifest,
|
manifestPath: normalizedHandoffPath,
|
||||||
outputDirectory: complexDirectory,
|
outputDirectory: complexDirectory,
|
||||||
releaseVersion: P0A_COMPLEX_RELEASE_VERSION,
|
releaseVersion: P0A_COMPLEX_RELEASE_VERSION,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { spawnSync } from "node:child_process";
|
import { spawnSync } from "node:child_process";
|
||||||
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
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 phaseIndex = process.argv.indexOf("--phase");
|
||||||
const phase = phaseIndex >= 0 ? process.argv[phaseIndex + 1] : "green";
|
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 runId = process.env.DADA_TDD_RUN_ID ?? `wp6-04-${phase}-${new Date().toISOString().replace(/[^0-9]/g, "")}`;
|
||||||
const runDirectory = resolve("artifacts", "tdd", runId);
|
const runDirectory = resolve("artifacts", "tdd", runId);
|
||||||
const caseDirectory = resolve(runDirectory, "cases", "TDD-WP6-AUD-001-sensitive-operations");
|
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}`);
|
if (existsSync(runDirectory)) throw new Error(`Evidence run already exists: ${runId}`);
|
||||||
mkdirSync(caseDirectory, { recursive: true });
|
mkdirSync(caseDirectory, { recursive: true });
|
||||||
|
|
||||||
@@ -17,6 +19,9 @@ const environment = {
|
|||||||
...process.env,
|
...process.env,
|
||||||
DADA_EVIDENCE_DIR_WP6_AUD: caseDirectory,
|
DADA_EVIDENCE_DIR_WP6_AUD: caseDirectory,
|
||||||
DADA_PLAYWRIGHT_OUTPUT_DIR: resolve(runDirectory, "playwright-output"),
|
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"
|
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"],
|
["e2e-red", ".\\node_modules\\.bin\\playwright.CMD test tests/e2e/wp6-04-audit.spec.ts --config playwright.config.ts"],
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
["integration", "pnpm.cmd test:integration"],
|
["integration", "pnpm.cmd exec vitest run tests/integration --testTimeout=20000"],
|
||||||
["api", "pnpm.cmd test:api"],
|
["api", "pnpm.cmd check:openapi && pnpm.cmd exec vitest run tests/api --testTimeout=20000"],
|
||||||
["worker", "pnpm.cmd test:worker"],
|
["worker", "pnpm.cmd test:worker"],
|
||||||
["e2e", "pnpm.cmd test:e2e"],
|
["e2e", "pnpm.cmd test:e2e"],
|
||||||
["tdd-trace", "pnpm.cmd validate:tdd-trace"],
|
["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 });
|
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", () => {
|
it("rejects evidence collections, traversal and output inside a source root", () => {
|
||||||
const fixture = createFixture();
|
const fixture = createFixture();
|
||||||
const manifest = JSON.parse(readFileSync(fixture.manifestPath, "utf8")) as { collections: unknown[] };
|
const manifest = JSON.parse(readFileSync(fixture.manifestPath, "utf8")) as { collections: unknown[] };
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { createHash } from "node:crypto";
|
import { createHash } from "node:crypto";
|
||||||
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
||||||
import { homedir } from "node:os";
|
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";
|
import { WP4_07_REAL_RESOURCE_VERSIONS } from "./wp4-07-fixture.mjs";
|
||||||
|
|
||||||
@@ -74,15 +74,16 @@ function assetRecord(assetId, path, sourceReference, expectedSha256) {
|
|||||||
export function loadWp407RealAssets() {
|
export function loadWp407RealAssets() {
|
||||||
const manifestPath = resolve(process.env.DADA_WP4_07_FINAL_ASSET_MANIFEST ?? "");
|
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");
|
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 replicationRoot = resolve(process.env.DADA_REPLICATION_ASSET_ROOT ?? join(homedir(), "Desktop", "sticker_web_replication_assets"));
|
||||||
const staticRoot = resolve(process.env.DADA_STATIC_STICKER_ROOT ?? join(homedir(), "Desktop", "贴纸素材"));
|
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"));
|
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");
|
if (!existsSync(handoffPath) || !existsSync(staticRoot)) throw new Error("WP4_07_REAL_ARCHIVE_ROOT_REQUIRED");
|
||||||
|
|
||||||
const manifestRaw = readFileSync(manifestPath, "utf8");
|
const manifestRaw = readFileSync(manifestPath, "utf8");
|
||||||
const manifest = JSON.parse(manifestRaw);
|
const manifest = JSON.parse(manifestRaw);
|
||||||
const handoff = JSON.parse(readFileSync(handoffPath, "utf8"));
|
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 fontRoot = collectionRoots.font_panel;
|
||||||
const dynamicRoot = collectionRoots.interactive_stickers;
|
const dynamicRoot = collectionRoots.interactive_stickers;
|
||||||
if (!fontRoot || !dynamicRoot) throw new Error("WP4_07_REAL_ARCHIVE_COLLECTION_REQUIRED");
|
if (!fontRoot || !dynamicRoot) throw new Error("WP4_07_REAL_ARCHIVE_COLLECTION_REQUIRED");
|
||||||
|
|||||||
Reference in New Issue
Block a user