diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..632e222 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,10 @@ +# Frozen inputs must retain their committed bytes on every platform. +/PRD.md -text +/FeatureSummary.md -text +/DevelopmentPlan.md -text +/UIDesign.md -text +/tdd.md -text +/Dada-P0A-LowFi.pen -text +/tasks.md -text +/tasks.manifest.json -text +/Dada-LowFi-Preview/** -text diff --git a/scripts/verify-ci-isolation.mjs b/scripts/verify-ci-isolation.mjs index c86e7a3..88d0170 100644 --- a/scripts/verify-ci-isolation.mjs +++ b/scripts/verify-ci-isolation.mjs @@ -2,7 +2,16 @@ import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; const policy = JSON.parse(readFileSync("ci/runner-isolation-policy.json", "utf8")); +const manifest = JSON.parse(readFileSync("tasks.manifest.json", "utf8")); const workflow = readFileSync(".gitea/workflows/p0a-ci.yml", "utf8"); +const gitAttributes = readFileSync(".gitattributes", "utf8"); + +const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +const frozenBytePaths = [...Object.keys(manifest.source_hashes), "tasks.manifest.json"]; +for (const path of frozenBytePaths) { + assert.match(gitAttributes, new RegExp(`^/${escapeRegExp(path)}\\s+-text\\s*$`, "m")); +} +assert.match(gitAttributes, /^\/Dada-LowFi-Preview\/\*\*\s+-text\s*$/m); for (const label of policy.required_labels) assert.match(workflow, new RegExp(`\\b${label}\\b`)); assert.doesNotMatch(workflow, /\bcorepack enable\b/);