- commit
- 7b6f2a1976644e2a828f7fe33827525fa2b78e84
- parent
- ff6b52303227d6a8d312a309c37759f0c9015b39
- author
- Dami <dami.etoile@gmail.com>
| M | .github/ | 1 | - |
| M | apps/ | 9 | +----- |
| M | apps/ | 9 | +----- |
| A | apps/ | 7 | +++++ |
| A | tools/ | 3 | ++ |
| M | tools/ | 5 | +--- |
| M | tools/ | 46 | +++++++++++++++++++++++++++++- |
| M | tools/ | 27 | +++++++++++++++--- |
@@ -1,18 +1,11 @@
11 import type { Db, MutationErrorEvent } from "jazz-tools";22 import { ChecklistStore, type RuntimeDiagnostics } from "./checklist-store.ts";3+import { assert, assertCount } from "./test-assert.ts";34 45 const test = (globalThis as unknown as {56 Deno: { test(name: string, body: () => void | Promise<void>): void };67 }).Deno.test;78 8-function assert(condition: unknown, message: string): asserts condition {9- if (!condition) throw new Error(message);10-}11-12-function assertCount(actual: number, expected: number, message: string): void {13- if (actual !== expected) throw new Error(`${message}: expected ${expected}, received ${actual}`);14-}15-169 function diagnostics(): RuntimeDiagnostics {1710 return {1811 storageState: "persistent-driver-open",@@ -6,19 +6,12 @@ import {
66 recreateResource,77 shutdownResource,88 } from "./resource-lifecycle.ts";9+import { assert, assertCount } from "./test-assert.ts";910 1011 const test = (globalThis as unknown as {1112 Deno: { test(name: string, body: () => void | Promise<void>): void };1213 }).Deno.test;1314 14-function assert(condition: unknown, message: string): asserts condition {15- if (!condition) throw new Error(message);16-}17-18-function assertCount(actual: number, expected: number, message: string) {19- if (actual !== expected) throw new Error(`${message}: expected ${expected}, received ${actual}`);20-}21-2215 test("concurrent recreation is single-flight and shutdown is idempotent", async () => {2316 const state = createSerializedResourceState<{ id: number }>();2417 let created = 0;@@ -0,0 +1,7 @@
1+export function assert(condition: unknown, message: string): asserts condition {2+ if (!condition) throw new Error(message);3+}4+5+export function assertCount(actual: number, expected: number, message: string): void {6+ if (actual !== expected) throw new Error(`${message}: expected ${expected}, received ${actual}`);7+}@@ -21,6 +21,7 @@ import {
2121 } from "./golden_path_core.ts";2222 import { loadEnvironment } from "./load_env.ts";2323 import { checklistUi } from "../apps/reference/src/ui-contract.ts";24+import { assert } from "./assert.ts";2425 2526 export type GoldenPathCommands = {2627 dev: string;@@ -67,10 +68,6 @@ const offlineDevelopmentBody = "Golden path development offline item";
6768 const offlineProductionBody = "Golden path production offline item";6869 const offlineProductionSyncedBody = "Golden path production offline item synced";6970 70-function assert(condition: unknown, message: string): asserts condition {71- if (!condition) throw new Error(message);72-}73-7471 function taskArgs(task: string, extra: string[] = []): string[] {7572 return ["task", task, ...(extra.length > 0 ? ["--", ...extra] : [])];7673 }@@ -162,6 +162,48 @@ async function copyDirectory(
162162 }163163 }164164 165+async function trackedFiles(source: string): Promise<string[] | null> {166+ const rootOutput = await new Deno.Command("git", {167+ args: ["-C", source, "rev-parse", "--show-toplevel"],168+ stdout: "piped",169+ stderr: "null",170+ }).output();171+ if (!rootOutput.success) return null;172+ const repositoryRoot = new TextDecoder().decode(rootOutput.stdout).trim();173+ if (resolve(repositoryRoot) !== resolve(source)) return null;174+175+ const filesOutput = await new Deno.Command("git", {176+ args: ["-C", source, "ls-files", "-z"],177+ stdout: "piped",178+ stderr: "null",179+ }).output();180+ if (!filesOutput.success) return null;181+ return new TextDecoder().decode(filesOutput.stdout).split("\0").filter(Boolean).sort();182+}183+184+async function copyTrackedFiles(185+ source: string,186+ destination: string,187+ files: readonly string[],188+): Promise<void> {189+ await Deno.mkdir(destination, { recursive: true });190+ for (const displayPath of files) {191+ const segments = displayPath.split(/[\\/]/);192+ if (193+ segments.some((name, index) =>194+ shouldExclude(name, index < segments.length - 1, segments.slice(0, index + 1).join("/"))195+ )196+ ) continue;197+ const from = join(source, displayPath);198+ const to = join(destination, displayPath);199+ const stat = await Deno.lstat(from);200+ if (stat.isSymlink) throw new Error(`clean checkout refuses symlink: ${displayPath}`);201+ if (!stat.isFile) continue;202+ await Deno.mkdir(dirname(to), { recursive: true });203+ await Deno.copyFile(from, to);204+ }205+}206+165207 export async function materializeCleanProject(166208 sourceRoot: string,167209 destinationRoot?: string,@@ -171,7 +213,9 @@ export async function materializeCleanProject(
171213 ? resolve(destinationRoot)172214 : join(await Deno.makeTempDir({ prefix: "lofi-golden-" }), "project");173215 if (source === destination) throw new Error("clean project destination must differ from source");174- await copyDirectory(source, source, destination);216+ const files = await trackedFiles(source);217+ if (files) await copyTrackedFiles(source, destination, files);218+ else await copyDirectory(source, source, destination);175219 return destination;176220 }177221 @@ -1,4 +1,5 @@
11 import { join, resolve } from "node:path";2+import { assert } from "./assert.ts";23 import {34 installNodeSentinel,45 materializeCleanProject,@@ -12,10 +13,6 @@ import {
1213 withDeadline,1314 } from "./golden_path_core.ts";1415 15-function assert(condition: unknown, message: string): asserts condition {16- if (!condition) throw new Error(message);17-}18-1916 async function exists(path: string): Promise<boolean> {2017 try {2118 await Deno.stat(path);@@ -128,6 +125,28 @@ Deno.test("clean project copy excludes secrets caches and the retired prototype"
128125 }129126 });130127 128+Deno.test("clean project materialization copies only tracked files from a repository", async () => {129+ const source = await workspaceTemp("tracked-source");130+ const destination = await workspaceTemp("tracked-destination");131+ try {132+ await Deno.writeTextFile(join(source, "tracked.ts"), "export const tracked = true;\n");133+ await Deno.writeTextFile(join(source, "untracked.ts"), "export const untracked = true;\n");134+ for (const args of [["init", "-q"], ["add", "tracked.ts"]]) {135+ const output = await new Deno.Command("git", { args, cwd: source }).output();136+ assert(output.success, `git ${args[0]} fixture setup failed`);137+ }138+139+ await materializeCleanProject(source, destination);140+ assert(await exists(join(destination, "tracked.ts")), "tracked source must be copied");141+ assert(!await exists(join(destination, "untracked.ts")), "untracked source must be excluded");142+ } finally {143+ await Promise.all([144+ removeWorkspaceTemp(source),145+ removeWorkspaceTemp(destination),146+ ]);147+ }148+});149+131150 Deno.test("author boundary permits schema declarations and reports UI plumbing", async () => {132151 const root = await workspaceTemp("golden-boundary");133152 try {