Skip to content

Commit 5aa12cf

Browse files
dzhelezovclaude
andauthored
feat(e2e): fail the gate if an example tracks a regenerable artifact (#134)
* feat(e2e): fail the gate if an example tracks a regenerable artifact The examples-e2e harness runs each example in an isolated temp copy, so a post-run git status on the repo tree never catches accidentally-committed regenerable artifacts. Add a STATIC per-example guard, alongside validateManifest / assertCommittedPin in loadExample, that inspects what git TRACKS for the example (git ls-files -z, cwd: ROOT) and FAILS the example if any tracked path is a regenerable artifact: .ponder/ or generated/ at any depth, ponder-env.d.ts, or package-lock.json — exactly the paths the root .gitignore already ignores. These must never be committed. The guard requires no live run, RPC, or secrets, is dep-free, throws HarnessFailure like the other static checks (so it participates in the same PASS/FAIL table), and names the offending path(s) with a git rm --cached remediation hint. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(e2e): anchor regenerable-artifact match to the example's direct children isRegenerableArtifact matched `.ponder`/`generated` path segments at any depth, which is broader than the .gitignore set it mirrors (examples/*/.ponder/, examples/*/generated/, examples/*/ponder-env.d.ts, examples/*/package-lock.json — all direct children). That would falsely fail a legitimately committed nested `src/generated/` (e.g. graphql-codegen output). Anchor the match: for a tracked path examples/<ex>/<rel>, FAIL iff the first segment of <rel> is `.ponder` or `generated`, or <rel> is exactly `ponder-env.d.ts` or `package-lock.json` — nothing deeper. The FAIL message, `git rm --cached` hint, -z NUL parse, and HarnessFailure are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ac824c6 commit 5aa12cf

1 file changed

Lines changed: 46 additions & 0 deletions

File tree

scripts/examples-e2e.mjs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ async function loadExample(example) {
161161

162162
validateManifest(example, manifest);
163163
assertCommittedPin(example, manifest, pkg);
164+
await assertNoTrackedArtifacts(example);
164165

165166
return { dir, manifest, pkg };
166167
}
@@ -242,6 +243,51 @@ function ponderDependency(pkg) {
242243
return undefined;
243244
}
244245

246+
async function assertNoTrackedArtifacts(example) {
247+
const result = await runCommand(
248+
'git',
249+
['ls-files', '-z', `examples/${example}`],
250+
{
251+
cwd: ROOT,
252+
env: process.env,
253+
timeoutMs: 30_000,
254+
},
255+
);
256+
if (result.code !== 0) {
257+
throw new HarnessFailure(
258+
`${example}: git ls-files failed: ${result.stderr.tail(MAX_TAIL_LINES, redactUrls)}`,
259+
);
260+
}
261+
262+
const prefix = `examples/${example}/`;
263+
const tracked = result.stdout
264+
.text()
265+
.split('\0')
266+
.filter((path) => path.length > 0);
267+
const offending = tracked.filter((path) =>
268+
isRegenerableArtifact(path.slice(prefix.length)),
269+
);
270+
if (offending.length > 0) {
271+
throw new HarnessFailure(
272+
`${example}: git tracks regenerable artifact(s) that must never be committed: ${offending.join(', ')}; run \`git rm --cached ${offending.join(' ')}\` (they are gitignored regenerables)`,
273+
);
274+
}
275+
}
276+
277+
function isRegenerableArtifact(relative) {
278+
const segments = relative.split('/');
279+
const first = segments[0];
280+
if (first === '.ponder') return true;
281+
282+
if (first === 'generated') return true;
283+
284+
if (relative === 'ponder-env.d.ts') return true;
285+
286+
if (relative === 'package-lock.json') return true;
287+
288+
return false;
289+
}
290+
245291
function checkLatestPin(example, manifest, pkg, latest) {
246292
const dependency = ponderDependency(pkg);
247293
if (dependency !== latest) {

0 commit comments

Comments
 (0)