clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

test: make checkout evidence commit-exact

Dami committed 7b6f2a1
commit
7b6f2a1976644e2a828f7fe33827525fa2b78e84
parent
ff6b52303227d6a8d312a309c37759f0c9015b39
author
Dami <dami.etoile@gmail.com>
8 files changed, +81 -26
M.github/workflows/m2-layer1.yml1-
Mapps/reference/src/_lofi/checklist-store_test.ts9+-----
Mapps/reference/src/_lofi/resource-lifecycle_test.ts9+-----
Aapps/reference/src/_lofi/test-assert.ts7+++++
Atools/assert.ts3++
Mtools/golden_path.ts5+---
Mtools/golden_path_core.ts46+++++++++++++++++++++++++++++-
Mtools/golden_path_core_test.ts27+++++++++++++++---
M .github/workflows/m2-layer1.yml +0 -1
M apps/reference/src/_lofi/checklist-store_test.ts +1 -8
M apps/reference/src/_lofi/resource-lifecycle_test.ts +1 -8
A apps/reference/src/_lofi/test-assert.ts +7 -0
A tools/assert.ts +3 -0
M tools/golden_path.ts +1 -4
M tools/golden_path_core.ts +45 -1
@@ -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
M tools/golden_path_core_test.ts +23 -4
@@ -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 {