ReviewOS

also looking at this

craft-native/craft

fix(native): preserve workspace sources and native overlay lifecycle

#384
Merged chris wants to merge codex/windows-zig-cache-diagnostics into main
20 files +500 -227

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.

.github/workflows/windows-source-provenance.ymldeleted+0-42
Changes to .github/workflows/windows-source-provenance.yml
@@ -1,42 +0,0 @@
1name: Windows source provenance reproduction
2
3on:
4 push:
5 branches: [codex/windows-zig-cache-diagnostics]
6 paths:
7 - '.github/workflows/windows-source-provenance.yml'
8 - '.github/workflows/ci.yml'
9 - 'scripts/inspect-windows-source.ps1'
10 - 'scripts/verify-zig-core-project.ts'
11 - 'scripts/zig-source-cache-probe.zig'
12 workflow_dispatch:
13
14permissions:
15 contents: read
16
17# Diagnostic only: repeat the published workspace installer on an ephemeral
18# checkout. Never restore the source or hide the installer's exit status.
19jobs:
20 workspace-source:
21 runs-on: windows-2025
22 timeout-minutes: 10
23 steps:
24 - uses: actions/checkout@v6
25 - name: Setup the published Pantry runtime
26 uses: pantry-pm/pantry/packages/action@ae59a7b7fc5f4e6f7b8041a75e6b5772680bebdf # v0.11.101
27 with:
28 version: '0.11.101'
29 - name: Verify workspace installation preserves tracked sources
30 shell: pwsh
31 run: |
32 $source = Join-Path $env:GITHUB_WORKSPACE 'packages/zig/build.zig'
33 $expected = (Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash
34 Get-ChildItem -LiteralPath 'pantry' -Force | Where-Object { $_.LinkType } | Select-Object FullName, LinkType, Target | Format-List
35 pantry install --no-save
36 $installExit = $LASTEXITCODE
37 $present = Test-Path -LiteralPath $source
38 Write-Output "Tracked core source present after workspace reinstall: $present"
39 git status --short -- packages/zig/build.zig
40 if (-not $present) { throw 'Workspace reinstall removed the tracked core build file' }
41 if ((Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash -ne $expected) { throw 'Workspace reinstall changed the tracked core build file' }
42 if ($installExit -ne 0) { exit $installExit }
scripts/inspect-windows-source.ps1deleted+0-31
Changes to scripts/inspect-windows-source.ps1
@@ -1,31 +0,0 @@
1param([Parameter(Mandatory = $true)][string]$Stage)
2
3$source = Join-Path $PSScriptRoot '../packages/zig/build.zig'
4$present = Test-Path -LiteralPath $source
5$metadata = @{
6 stage = $Stage
7 source = $source
8 present = $present
9 tracked = (& git ls-files --stage -- packages/zig/build.zig)
10 status = (& git status --porcelain -- packages/zig/build.zig)
11}
12if ($present) {
13 $metadata.sha256 = (Get-FileHash -LiteralPath $source -Algorithm SHA256).Hash
14 $metadata.attributes = (Get-Item -LiteralPath $source).Attributes.ToString()
15}
16$metadata | ConvertTo-Json -Compress
17
18if (-not $present -and (Get-Command Get-MpThreatDetection -ErrorAction SilentlyContinue)) {
19 try {
20 Get-MpThreatDetection | Where-Object { $_.Resources -match 'build\.zig' } | ForEach-Object {
21 @{
22 threatId = $_.ThreatID
23 actionSucceeded = $_.ActionSuccess
24 detectedAt = $_.InitialDetectionTime
25 resources = @($_.Resources | Where-Object { $_ -match 'build\.zig' })
26 } | ConvertTo-Json -Compress
27 }
28 } catch {
29 Write-Output "Source security-event inspection unavailable: $($_.Exception.Message)"
30 }
31}
scripts/verify-zig-core-project.tsmodified+3-25
Changes to scripts/verify-zig-core-project.ts
@@ -1,42 +1,20 @@
1import { existsSync, mkdtempSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs'
2import { dirname, join, resolve } from 'node:path'
1import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
2import { join, resolve } from 'node:path'
33import { resolveZig } from './zig'
4import { createHash } from 'node:crypto'
5import { cpus } from 'node:os'
64
75const source = resolve(import.meta.dir, '..')
86const zig = resolveZig(source)
9if (process.platform === 'win32') {
10 const provenance = Bun.spawnSync(['pwsh', '-NoProfile', '-NonInteractive', '-File', join(source, 'scripts/inspect-windows-source.ps1'), '-Stage', 'compiler'], { cwd: source, stdout: 'pipe', stderr: 'pipe' })
11 console.log('Source after compiler:', provenance.stdout.toString(), provenance.stderr.toString())
12}
13const trackedBuild = join(source, 'packages/zig/build.zig')
14console.log('Tracked core project:', JSON.stringify({ file: trackedBuild, exists: existsSync(trackedBuild), sha256: existsSync(trackedBuild) ? createHash('sha256').update(readFileSync(trackedBuild)).digest('hex') : null, bun: Bun.version, executable: process.execPath, cpu: cpus()[0]?.model, platform: process.platform }))
15const zigLibrary = join(dirname(realpathSync(zig.path)), 'lib/std/zig.zig')
16console.log('Pinned compiler bytes:', JSON.stringify({ executable: zig.path, sha256: createHash('sha256').update(readFileSync(zig.path)).digest('hex'), library: zigLibrary, librarySha256: existsSync(zigLibrary) ? createHash('sha256').update(readFileSync(zigLibrary)).digest('hex') : null }))
7console.log('Tracked core project:', JSON.stringify({ file: join(source, 'packages/zig/build.zig'), exists: existsSync(join(source, 'packages/zig/build.zig')) }))
178// Keep the fixture on the checkout's volume. Hosted Windows keeps the
189// checkout on D: and the system temporary directory/compiler cache on C:.
1910const root = mkdtempSync(join(source, '.zig-core-project-'))
2011const core = join(root, 'packages', 'zig')
2112try {
2213 mkdirSync(core, { recursive: true })
23 if (process.platform === 'win32') {
24 const volumes = Bun.spawnSync(['pwsh', '-NoProfile', '-NonInteractive', '-Command', 'Get-Volume | Where-Object { $_.DriveLetter -in @("C", "D") } | Select-Object DriveLetter,FileSystemType,HealthStatus | ConvertTo-Json -Compress'], { stdout: 'pipe', stderr: 'pipe' })
25 console.log('Native source/cache volumes:', volumes.stdout.toString(), volumes.stderr.toString())
26 }
27 const native = Bun.spawnSync([zig.path, 'run', join(source, 'scripts/zig-source-cache-probe.zig'), '--cache-dir', join(root, 'io-build-cache'), '--', join(source, 'packages/zig'), join(root, 'io-manifest-cache')], { cwd: join(source, 'packages/zig'), stdout: 'pipe', stderr: 'pipe' })
28 console.log('Native source/cache probe:', native.exitCode, native.stdout.toString(), native.stderr.toString())
29 if (native.exitCode !== 0) throw new Error('The pinned native source/cache I/O probe failed.')
3014 writeFileSync(join(root, 'pantry.lock'), readFileSync(join(source, 'pantry.lock')))
3115 writeFileSync(join(root, 'package.json'), JSON.stringify({ version: '1.2.3' }))
3216 writeFileSync(join(root, 'build.zig'), 'const std=; pub fn build(b:*std.Build) void {_=b.standardOptimizeOption(.{});}\n')
3317 writeFileSync(join(core, 'build.zig'), 'const std=; pub fn build(b:*std.Build) void {_=b.standardOptimizeOption(.{}); const version=b.option([]const u8,"version","version") orelse "missing"; std.debug.print("PROJECT_VERSION={s}\\n",.{version});}\n')
34 const manifest = { name: 'craft', fingerprint: '0xf45c4a848bf58c1c' }
35 writeFileSync(join(core, 'build.zig.zon'), `.{ .name=.${manifest.name}, .fingerprint=${manifest.fingerprint}, .version="1.2.3", .dependencies=.{ .fixture=.{ .path="../../dependency" } }, .paths=.{"build.zig","build.zig.zon"} }\n`)
36 const dependency = join(root, 'dependency')
37 mkdirSync(dependency)
38 writeFileSync(join(dependency, 'build.zig'), 'const std=; pub fn build(b:*std.Build) void {_=b;}\n')
39 writeFileSync(join(dependency, 'build.zig.zon'), `.{ .name=.${manifest.name}, .fingerprint=${manifest.fingerprint}, .version="0.0.1", .paths=.{"build.zig","build.zig.zon"} }\n`)
4018 const env = { ...process.env, CRAFT_ZIG: zig.path }
4119 const runner = join(root, 'runner.ts')
4220 writeFileSync(runner, `import {runZig} from ${JSON.stringify(join(source, 'scripts/zig.ts'))};process.exitCode=runZig(${JSON.stringify(root)},['--core','--versioned','build']);\n`)
scripts/zig-source-cache-probe.zigdeleted+0-49
Changes to scripts/zig-source-cache-probe.zig
@@ -1,49 +0,0 @@
1const std = ;
2const Io = std.Io;
3const Cache = std.Build.Cache;
4
5pub fn main(init: std.process.Init) !void {
6 const arena = init.arena.allocator();
7 const io = init.io;
8 const args = try init.minimal.args.toSlice(arena);
9 const cwd = try std.process.currentPathAlloc(io, arena);
10 std.debug.print("NATIVE_CWD={s}\n", .{cwd});
11 const source = try Io.Dir.cwd().openDir(io, args[1], .{});
12 defer source.close(io);
13 const file = source.openFile(io, "build.zig", .{}) catch |err| {
14 std.debug.print("SOURCE_OPEN={t}\n", .{err});
15 return err;
16 };
17 defer file.close(io);
18 const stat = file.stat(io) catch |err| {
19 std.debug.print("SOURCE_STAT={t}\n", .{err});
20 return err;
21 };
22 const contents = source.readFileAlloc(io, "build.zig", arena, .limited(1024 * 1024)) catch |err| {
23 std.debug.print("SOURCE_READ={t}\n", .{err});
24 return err;
25 };
26 std.debug.print("SOURCE_SIZE={d} READ_BYTES={d}\n", .{ stat.size, contents.len });
27 var cache: Cache = .{
28 .io = io,
29 .gpa = arena,
30 .manifest_dir = try Io.Dir.cwd().createDirPathOpen(io, args[2], .{}),
31 .cwd = cwd,
32 };
33 defer cache.manifest_dir.close(io);
34 cache.addPrefix(.{ .path = null, .handle = Io.Dir.cwd() });
35 for (0..4) |_| cache.addPrefix(.{ .path = args[1], .handle = source });
36 var manifest = cache.obtain();
37 defer manifest.deinit();
38 var check_diagnostic: Cache.Manifest.CheckDiagnostic = undefined;
39 _ = try manifest.check(&check_diagnostic, .none);
40 var diagnostic: Cache.Manifest.AddDiscoveredPathDiagnostic = .none;
41 manifest.addDiscoveredPath(.{
42 .discovered_path = .{ .prefixed = .{ .prefix = 4, .sub_path = "build.zig" } },
43 .diagnostic = &diagnostic,
44 }) catch |err| {
45 std.debug.print("SOURCE_CACHE={t} DETAILS={f}\n", .{ err, diagnostic });
46 return err;
47 };
48 std.debug.print("SOURCE_CACHE=passed\n", .{});
49}