also looking at this
fix(native): preserve workspace sources and native overlay lifecycle
#384
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.
| @@ -1,42 +0,0 @@ | ||
| 1 | name: Windows source provenance reproduction | |
| 2 | ||
| 3 | on: | |
| 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 | ||
| 14 | permissions: | |
| 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. | |
| 19 | jobs: | |
| 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 } | |
| @@ -1,31 +0,0 @@ | ||
| 1 | param([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 | } | |
| 12 | if ($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 | ||
| 18 | if (-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 | } | |
| @@ -1,42 +1,20 @@ | ||
| 1 | import { existsSync, mkdtempSync, mkdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from 'node:fs' | |
| 2 | import { dirname, join, resolve } from 'node:path' | |
| 1 | import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs' | |
| 2 | import { join, resolve } from 'node:path' | |
| 3 | 3 | import { resolveZig } from './zig' |
| 4 | import { createHash } from 'node:crypto' | |
| 5 | import { cpus } from 'node:os' | |
| 6 | 4 | |
| 7 | 5 | const source = resolve(import.meta.dir, '..') |
| 8 | 6 | const zig = resolveZig(source) |
| 9 | if (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 | } | |
| 13 | const trackedBuild = join(source, 'packages/zig/build.zig') | |
| 14 | console.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 })) | |
| 15 | const zigLibrary = join(dirname(realpathSync(zig.path)), 'lib/std/zig.zig') | |
| 16 | console.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 })) | |
| 7 | console.log('Tracked core project:', JSON.stringify({ file: join(source, 'packages/zig/build.zig'), exists: existsSync(join(source, 'packages/zig/build.zig')) })) | |
| 17 | 8 | // Keep the fixture on the checkout's volume. Hosted Windows keeps the |
| 18 | 9 | // checkout on D: and the system temporary directory/compiler cache on C:. |
| 19 | 10 | const root = mkdtempSync(join(source, '.zig-core-project-')) |
| 20 | 11 | const core = join(root, 'packages', 'zig') |
| 21 | 12 | try { |
| 22 | 13 | 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.') | |
| 30 | 14 | writeFileSync(join(root, 'pantry.lock'), readFileSync(join(source, 'pantry.lock'))) |
| 31 | 15 | writeFileSync(join(root, 'package.json'), JSON.stringify({ version: '1.2.3' })) |
| 32 | 16 | writeFileSync(join(root, 'build.zig'), 'const std=; pub fn build(b:*std.Build) void {_=b.standardOptimizeOption(.{});}\n') |
| 33 | 17 | 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`) | |
| 40 | 18 | const env = { ...process.env, CRAFT_ZIG: zig.path } |
| 41 | 19 | const runner = join(root, 'runner.ts') |
| 42 | 20 | writeFileSync(runner, `import {runZig} from ${JSON.stringify(join(source, 'scripts/zig.ts'))};process.exitCode=runZig(${JSON.stringify(root)},['--core','--versioned','build']);\n`) |
| @@ -1,49 +0,0 @@ | ||
| 1 | const std = ; | |
| 2 | const Io = std.Io; | |
| 3 | const Cache = std.Build.Cache; | |
| 4 | ||
| 5 | pub 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 | } | |