fix(windows): LF for Dockerfiles, normalized home-guard exclusions, path.relative fixture containment, ship push test spawn timeouts

This commit is contained in:
garrytan committed 2026-10-02 21:18:26 +00:00
1 parent 66c0e5f02e
commit 3fa37ba225
4 files changed
+21 -8

No files matched your search

+4
View File
@@ -24,6 +24,10 @@ setup text eol=lf
bin/* text eol=lf
**/scripts/* text eol=lf
# Dockerfiles build on Linux, and test/ci-image-cli-pin.test.ts joins their
# `\`-continued RUN lines with a `\\\n` match; CRLF left each RUN split.
Dockerfile* text eol=lf
# TypeScript/JavaScript: LF for portability across the bun toolchain.
*.ts text eol=lf
*.tsx text eol=lf
+1 -1
View File
@@ -38,7 +38,7 @@ export function freeHomeDir(env: NodeJS.ProcessEnv = process.env): string {
export function snapshotFreeHome(home: string): FreeHomeSnapshot {
const entries: FreeHomeSnapshot = new Map();
const volatile = new Set<string>(FREE_HOME_VOLATILE);
const volatile = new Set<string>(FREE_HOME_VOLATILE.map(entry => path.normalize(entry)));
const visit = (relative: string, depth: number) => {
if (volatile.has(relative)) return;
const absolute = path.join(home, relative);
+9 -4
View File
@@ -13,7 +13,7 @@ import { spawnSync, type SpawnSyncReturns } from 'node:child_process';
import { createHash } from 'node:crypto';
import { copyFileSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, readlinkSync, realpathSync, rmSync, symlinkSync, utimesSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { dirname, join, resolve } from 'node:path';
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
export const ROOT = resolve(import.meta.dir, '../..');
const listed = spawnSync('git', ['ls-files', '--cached', '--others', '--exclude-standard', '-z'], { cwd: ROOT, encoding: 'utf8', timeout: 10_000 });
@@ -25,13 +25,18 @@ export interface Fixture { dir: string; home: string; commands: string; env: Rec
const roots: string[] = [];
/** `target` is `base` or below it, compared by the platform's own path rules (separators, Windows drive case). */
const within = (base: string, target: string) => {
const rel = relative(base, target);
return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
};
/** Write inside the fixture only (never through a link out of it). */
export function put(file: string, content: string | Buffer, mode = 0o644) {
const root = roots.find(r => file.startsWith(r + '/'));
const root = roots.find(r => file !== r && within(r, file));
if (!root) throw new Error(`fixture write outside a fixture root: ${file}`);
mkdirSync(dirname(file), { recursive: true });
const parent = realpathSync(dirname(file));
if (parent !== realpathSync(root) && !parent.startsWith(realpathSync(root) + '/')) throw new Error(`fixture write escapes its root: ${file}`);
if (!within(realpathSync(root), realpathSync(dirname(file)))) throw new Error(`fixture write escapes its root: ${file}`);
writeFileSync(file, content, { mode });
}
+7 -3
View File
@@ -239,7 +239,7 @@ test('push idempotency requires the live remote SHA and fails closed on transpor
GIT_AUTHOR_NAME: 'Test', GIT_AUTHOR_EMAIL: 'test@example.invalid',
GIT_COMMITTER_NAME: 'Test', GIT_COMMITTER_EMAIL: 'test@example.invalid' };
const git = (...args: string[]) => {
const r = spawnSync('git', args, { cwd, env, encoding: 'utf8', timeout: 5000 });
const r = spawnSync('git', args, { cwd, env, encoding: 'utf8', timeout: 30_000 });
if (r.status !== 0) throw new Error(r.stderr || String(r.error));
};
try {
@@ -251,7 +251,11 @@ test('push idempotency requires the live remote SHA and fails closed on transpor
const source = fs.readFileSync(path.join(SHIP_DIR, 'SKILL.md.tmpl'), 'utf8');
const block = source.slice(source.indexOf('**Idempotency check:** Check if the branch'))
.match(/```bash\n([\s\S]*?)\n```/)![1].replaceAll('<branch-name>', 'feature');
const inspect = () => spawnSync('bash', ['-c', block], { cwd, env, encoding: 'utf8', timeout: 5000 });
const inspect = () => {
const r = spawnSync('bash', ['-c', block], { cwd, env, encoding: 'utf8', timeout: 30_000 });
if (r.error) throw new Error(`idempotency check did not run: ${r.error.message}; stderr: ${r.stderr}`);
return r;
};
expect(inspect().stdout).toContain('PUSH_NEEDED');
git('push', '-u', 'origin', 'feature');
expect(inspect().stdout).toContain('ALREADY_PUSHED');
@@ -264,4 +268,4 @@ test('push idempotency requires the live remote SHA and fails closed on transpor
expect(unavailable.stdout).not.toContain('ALREADY_PUSHED');
expect(unavailable.stdout).toContain('BLOCKED');
} finally { fs.rmSync(cwd, { recursive: true, force: true }); }
});
}, 120_000);