mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-10 10:40:32 +01:00
fix(windows): LF for Dockerfiles, normalized home-guard exclusions, path.relative fixture containment, ship push test spawn timeouts
This commit is contained in:
1 parent
66c0e5f02e
commit
3fa37ba225
4 files changed
+21
-8
No files matched your search
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
Reference in new issue
Block a user