Split The code is public; the app answers only to the crew.

The tree guard is red on any machine that has run the app: it fails on files .gitignore already excuses #162

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

scout/cs:scout/129-the-tree-guard-is-red-on-any-machine-tha → main 1 commit fork

  • Merged. scout/129-the-tree-guard-is-red-on-any-machine-tha was merged into main as b15300e.

Review

Sign in to review this change.
1 file changed +21 −6
src/tree.test.ts +21 −6
@@ -6,6 +6,19 @@
66 * `git ls-files` isn't available to a test — and fails the same way CI
77 * would if either shape came back: a Netscape cookie jar, or a directory
88 * named for one of the scratch patterns `.gitignore` already lists.
9 *
10 * "Ignored" and "committed" are different questions. A file that matches
11 * one of `.gitignore`'s own patterns (`data/app.db`, `static/app.*.js`)
12 * is proof the ignore is doing its job, not a violation — the app writes
13 * those on every run and every build, so flagging them makes the guard
14 * red on any machine that has used the app at all (issue #129). A
15 * directory named for one of `.gitignore`'s scratch entries is different:
16 * nothing normal creates `.scout-tmp/` or `.checkdata/`, so seeing one
17 * still means someone put it there on purpose, ignored or not — the
18 * ignore stops it from being committed, not from existing or from being
19 * force-added, so this still has to look. A cookie jar is checked
20 * regardless of either answer: ignoring a path doesn't undo a commit
21 * that already happened, so a cookie jar is a credential wherever it sits.
922 */
1023
1124 import { expect, test } from "bun:test";
@@ -25,9 +38,6 @@ const lines = gitignore
2538 const scratchDirNames = new Set(
2639 lines.filter((line) => line.endsWith("/")).map((line) => line.slice(0, -1)),
2740 );
28 const filePatterns = lines
29 .filter((line) => !line.endsWith("/") && line.includes("*"))
30 .map((line) => new RegExp(`^${line.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`));
3141
3242 /** Just enough of the file to know its first line, not the whole thing. */
3343 const firstLine = (path: string): string => {
@@ -52,9 +62,14 @@ const walk = (dir: string): void => {
5262 if (scratchDirNames.has(entry.name)) violations.push(`${relative}/ is a scratch directory .gitignore already refuses`);
5363 walk(path);
5464 } else if (entry.isFile()) {
55 if (filePatterns.some((pattern) => pattern.test(entry.name))) {
56 violations.push(`${relative} matches a scratch pattern .gitignore already refuses`);
57 } else if (firstLine(path).trim() === COOKIE_JAR_HEADER) {
65 // A file's name is not checked against .gitignore's own scratch
66 // patterns (`*.db`, `static/app.*.js`): matching one of those means
67 // the ignore is working, and running the app or building the bundle
68 // is exactly what produces them, on every machine, every time — the
69 // opposite of a violation. Its content still is: a cookie jar's
70 // header doesn't stop being a credential because its filename
71 // happens to be ignored.
72 if (firstLine(path).trim() === COOKIE_JAR_HEADER) {
5873 violations.push(`${relative} is a Netscape cookie jar`);
5974 }
6075 }

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs