also looking at this
fix: preserve sources when removing linked workspace directories
#247
4 files
+98
-46
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | import assert from 'node:assert/strict' |
| 2 | import { mkdir, mkdtemp, lstat, rename, rm } from 'node:fs/promises' | |
| 2 | import { mkdir, mkdtemp, lstat, readFile, rm } from 'node:fs/promises' | |
| 3 | 3 | import { tmpdir } from 'node:os' |
| 4 | 4 | import { join } from 'node:path' |
| 5 | 5 | import { installPackage } from '../../packages/ts-pantry/src/installer' |
| @@ -9,8 +9,8 @@ const artifacts = join(import.meta.dir, '../../artifacts/windows-native-tools') | ||
| 9 | 9 | await mkdir(artifacts, { recursive: true }) |
| 10 | 10 | const root = await mkdtemp(join(tmpdir(), 'pantry native Windows ')) |
| 11 | 11 | const proof: Record<string, unknown> = {} |
| 12 | async function run(name: string, command: string[]): Promise<string> { | |
| 13 | const child = Bun.spawn(command, { cwd: root, env: { ...process.env, NO_COLOR: '1' }, stdout: 'pipe', stderr: 'pipe' }) | |
| 12 | async function run(name: string, command: string[], environment: Record<string, string> = {}): Promise<string> { | |
| 13 | const child = Bun.spawn(command, { cwd: root, env: { ...process.env, NO_COLOR: '1', ...environment }, stdout: 'pipe', stderr: 'pipe' }) | |
| 14 | 14 | const timeout = setTimeout(() => child.kill(), 180_000) |
| 15 | 15 | try { |
| 16 | 16 | const [code, stdout, stderr] = await Promise.all([child.exited, new Response(child.stdout).text(), new Response(child.stderr).text()]) |
| @@ -28,18 +28,30 @@ try { | ||
| 28 | 28 | proof.cliVersion = version |
| 29 | 29 | let tree = join(root, 'pantry') |
| 30 | 30 | await installPackage('ziglang.org', '0.17.0', { installDir: tree }) |
| 31 | const compilerSha256 = Bun.SHA256.hash(await readFile(join(tree, '.bin/zig.exe')), 'hex') | |
| 32 | proof.compilerSha256 = compilerSha256 | |
| 31 | 33 | const source = join(root, 'fixture.zig') |
| 32 | 34 | await Bun.write(source, 'const std = ; pub fn main() void { std.debug.print("NATIVE_ZIG_STDLIB_OK\\n", .{}); }\n') |
| 33 | 35 | for (const phase of ['installed', 'relocated']) { |
| 34 | 36 | const alias = join(tree, '.bin/zig.exe') |
| 35 | 37 | assert((await lstat(alias)).isSymbolicLink()) |
| 38 | assert.equal(Bun.SHA256.hash(await readFile(alias), 'hex'), compilerSha256) | |
| 36 | 39 | assert.equal((await run(`${phase}-version`, [alias, 'version'])).trim(), '0.17.0') |
| 37 | 40 | const output = join(root, `${phase}.exe`) |
| 38 | 41 | await run(`${phase}-compile`, [alias, 'build-exe', source, '-OReleaseSafe', `-femit-bin=${output}`]) |
| 39 | 42 | assert.equal((await run(`${phase}-execute`, [output])).trim(), 'NATIVE_ZIG_STDLIB_OK') |
| 40 | 43 | if (phase === 'installed') { |
| 41 | 44 | const moved = join(root, 'moved pantry') |
| 42 | await rename(tree, moved) | |
| 45 | // Exercise the Windows user operation itself. Bun 1.4.3's rename rejects | |
| 46 | // this installed tree with EPERM while the native Windows move succeeds. | |
| 47 | // One native move must complete; source absence and the next phase's | |
| 48 | // symlink, byte identity, compile and execution assertions stay required. | |
| 49 | await run('relocate-prefix', ['powershell', '-NoProfile', '-NonInteractive', '-Command', 'Move-Item -LiteralPath $env:PANTRY_RELOCATION_SOURCE -Destination $env:PANTRY_RELOCATION_DESTINATION -ErrorAction Stop'], { | |
| 50 | PANTRY_RELOCATION_SOURCE: tree, | |
| 51 | PANTRY_RELOCATION_DESTINATION: moved, | |
| 52 | }) | |
| 53 | await assert.rejects(lstat(tree), { code: 'ENOENT' }) | |
| 54 | proof.prefixRelocatedByNativeWindows = true | |
| 43 | 55 | tree = moved |
| 44 | 56 | } |
| 45 | 57 | } |