Skip to content

fix(windows): preserve parent env for run flags + extract astcenc via System32 bsdtar - #267

Merged
apotema merged 6 commits into
mainfrom
fix/windows-env-snapshot-and-astc-tar
Jun 29, 2026
Merged

fix(windows): preserve parent env for run flags + extract astcenc via System32 bsdtar#267
apotema merged 6 commits into
mainfrom
fix/windows-env-snapshot-and-astc-tar

Conversation

@apotema

@apotema apotema commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Two Windows fixes found while validating the v1.50.0 changes on Windows 11 (Zig 0.16). Both are independent; each is its own commit.

1. fix(run): parent environment stripped on Windows for env-injecting flags

The new direct-run path injects env vars for --headless, --scene, --screenshot, and --profile via buildEnvironWithExtra, which is meant to be "parent env + extras". On Windows it instead produced an extras-only environment, so the child zig build lost LOCALAPPDATA/PATH and failed with:

error: unable to resolve zig cache directory: AppDataDirUnavailable
labelle: build failed (exit 1)

Root cause: the helper switched on @TypeOf(block) and assumed the inherited env is a WindowsBlock, but Zig 0.16 represents it as a global (PEB-backed) block — hitting the "nothing to snapshot" branch. Fixed by using std.process.Environ.createMap, the platform-correct snapshot (PEB on Windows, environ on POSIX, WASI environ API on WASI).

2. fix(astc): labelle astc extraction fails under Git Bash/MSYS

Extraction used a bare tar -xf to read the astcenc release zip. In PowerShell/cmd this is System32 bsdtar (reads zips, fine), but when labelle runs from a Git Bash / MSYS / Cygwin shell, that shell's GNU tar shadows it on PATH and cannot read a zip (This does not look like a tar archive). Pinned the Windows extract to absolute <SystemRoot>\System32\tar.exe (with a bare-tar fallback).

Testing

  • zig build test294/294 pass.
  • Verified on Windows 11 / Zig 0.16 against a freshly scaffolded raylib project:
    • labelle run --screenshot=... --after=1s writes a valid 800×600 PNG and self-exits (previously: AppDataDirUnavailable).
    • labelle run --timeout confirmed still kills cleanly with no orphan.
    • labelle astc full download → extract → convert from a Git Bash shell produces a valid .astc (correct 0x5CA1AB13 magic, 8×8 blocks).

Note (not addressed here)

DebugAllocator reports a benign TimeoutState leak (runner.zig runZigInheritWithEnv) when a game self-exits before --timeout fires — the detached watchdog thread still holds its refcount and the process exits before it wakes. It's a deliberate trade-off of the UAF fix in #265 and harmless; flagging only for visibility.

apotema added 2 commits June 27, 2026 21:28
…eenshot/--profile

The env-injecting run flags strip the entire parent environment on
Windows, so the child `zig build` fails with `AppDataDirUnavailable`
(no LOCALAPPDATA). `buildEnvironWithExtra` switched on `@TypeOf(block)`
and assumed Windows uses a `WindowsBlock`, but Zig 0.16 represents the
inherited environment as a *global* (PEB-backed) block — so it hit the
"nothing to snapshot" branch and produced an extras-only env, dropping
PATH/LOCALAPPDATA/etc.

Use `std.process.Environ.createMap`, which is the platform-correct
snapshot: it reads the PEB on Windows, `environ` on POSIX, and the WASI
environ API on WASI. Verified on Windows: `labelle run --screenshot`
now writes a valid PNG and self-exits instead of failing the build step.
`labelle astc` extracted the astcenc release zip with a bare `tar -xf`.
That resolves to System32 bsdtar in PowerShell/cmd (fine), but when
labelle runs from a Git Bash / MSYS / Cygwin shell the environment's GNU
`tar` shadows it on PATH — and GNU tar cannot read a zip ("This does not
look like a tar archive"), so the extract fails only for those users.

Resolve bsdtar by absolute path (<SystemRoot>\System32\tar.exe, with a
bare-`tar` fallback when SystemRoot is unset) so the libarchive-backed
system tar is always used regardless of PATH shadowing. Verified a full
download -> extract -> convert from a Git Bash shell.
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 85269b40-def6-46fb-bc76-61425bdd9e52

📥 Commits

Reviewing files that changed from the base of the PR and between 9a2c6ed and ad64e71.

📒 Files selected for processing (1)
  • src/astc/astcenc_bin.zig
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/astc/astcenc_bin.zig

📝 Walkthrough

Walkthrough

The PR updates Windows extraction and environment inheritance, and adds .y_axis = .up to several test project configurations.

Changes

Windows Environment and Tar Fixes

Layer / File(s) Summary
Windows tar path resolution in astc ensure
src/astc/astcenc_bin.zig
ensure on Windows resolves an absolute tar.exe path via windowsTarPath, falls back to bare tar when that lookup cannot be used, and keeps non-Windows extraction on unzip.
Full environment inheritance in buildEnvironWithExtra
src/cli/runner.zig
buildEnvironWithExtra now creates the environment map from config.globalEnviron() and appends extras after inheriting the parent environment.

Test project Y-axis settings

Layer / File(s) Summary
Y-axis configuration in test projects
test/gui-plugin-test/project.labelle, test/imgui-anchor-test/project.labelle, test/nuklear-plugin-test/project.labelle, test/plugin-manifest-test/project.labelle
Several test project definitions add .y_axis = .up alongside existing project configuration fields.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 On Windows the PATH was a tricky old maze,
But tar found its home in System32's ways.
The env now comes through with each trusty key,
Hop hop, all is tidy as tidy can be!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two Windows fixes in the changeset.
Description check ✅ Passed The description matches the Windows environment and astcenc extraction fixes in the patch.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/windows-env-snapshot-and-astc-tar

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request improves Windows compatibility in two areas. First, it resolves the absolute path to the system tar.exe using environment variables (SystemRoot or windir) to prevent Git Bash or MSYS GNU tar from shadowing it and failing to extract zip files. Second, it replaces custom environment mapping in runner.zig with environ.createMap to ensure the parent environment is correctly preserved on Windows. The reviewer feedback suggests improving error handling in the new tar resolution logic by propagating OutOfMemory errors immediately rather than catching them and falling back to alternative options.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/astc/astcenc_bin.zig Outdated
// extract would fail only for those users. The absolute path always hits the
// libarchive-backed system tar. Falls back to bare `tar` if SystemRoot is unset.
const extracted = if (builtin.os.tag == .windows) blk: {
const tar_exe = windowsTarPath(allocator) catch try allocator.dupe(u8, "tar");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If windowsTarPath fails due to error.OutOfMemory, it is best practice in Zig to propagate the OOM error immediately rather than catching it and attempting to fall back to "tar". Masking OutOfMemory can lead to secondary failures or unexpected behavior.

        const tar_exe = windowsTarPath(allocator) catch |err| switch (err) {
            error.OutOfMemory => return error.OutOfMemory,
            else => try allocator.dupe(u8, "tar"),
        };

Comment thread src/astc/astcenc_bin.zig Outdated
Comment on lines +153 to +154
const root = env.getAlloc(allocator, "SystemRoot") catch
try env.getAlloc(allocator, "windir");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similarly, if env.getAlloc(allocator, "SystemRoot") fails with error.OutOfMemory, we should propagate the OOM error immediately instead of attempting to query "windir".

    const root = env.getAlloc(allocator, "SystemRoot") catch |err| switch (err) {
        error.OutOfMemory => return error.OutOfMemory,
        else => try env.getAlloc(allocator, "windir"),
    };

Address review feedback: the bare-`tar` and `windir` fallbacks exist
only for a missing SystemRoot/windir env var, so they should not also
swallow `error.OutOfMemory`. Switch on the error and re-raise OOM
immediately in both `ensure`'s tar resolution and `windowsTarPath`.
@apotema

apotema commented Jun 28, 2026

Copy link
Copy Markdown
Contributor Author

Addressed both Gemini suggestions in a79f3b0error.OutOfMemory is now re-raised immediately in both the windowsTarPath (SystemRootwindir) and ensure (bare-tar) fallbacks, rather than being masked. Build + 294/294 tests still green.

The assembler now enforces the y-axis epic (engine#640): a project.labelle
without .y_axis fails generate with MissingYAxis. This fixture predates that;
declare .up to preserve its existing bottom-origin behavior (the migration
default for pre-existing games). Fixes the Versions Integration Test red-bar
(present on main too, not introduced by this PR).
The assembler enforces the y-axis epic at generate time; the imgui-anchor,
nuklear-plugin, and gui-plugin fixtures predate it. Declare .up (preserve
bottom-origin) so the Versions Integration Test's generate steps pass — same
fix as plugin-manifest-test in the prior commit.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes two Windows-specific regressions discovered while validating v1.50.0 on Windows 11 / Zig 0.16: (1) labelle run env-injecting flags now preserve the parent environment, and (2) labelle astc extraction is made robust when running under Git Bash/MSYS where GNU tar can shadow Windows’ bsdtar.

Changes:

  • Update buildEnvironWithExtra to snapshot the parent environment via std.process.Environ.createMap, preserving PATH/LOCALAPPDATA on Windows.
  • Pin Windows ASTC encoder extraction to <SystemRoot>\System32\tar.exe (bsdtar) with a fallback to bare tar when SystemRoot/windir are unavailable.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

File Description
src/cli/runner.zig Uses Environ.createMap to correctly snapshot the inherited environment across platforms (fixes Windows env stripping).
src/astc/astcenc_bin.zig Uses an absolute System32 tar.exe on Windows to avoid Git Bash/MSYS GNU tar incompatibility with zip extraction.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/astc/astcenc_bin.zig
Comment on lines +119 to +124
const tar_exe = windowsTarPath(allocator) catch |err| switch (err) {
// OOM is fatal — don't mask it behind the bare-`tar` fallback,
// which only exists for a missing SystemRoot/windir.
error.OutOfMemory => return error.OutOfMemory,
else => try allocator.dupe(u8, "tar"),
};
Comment thread src/astc/astcenc_bin.zig
Comment on lines +158 to +163
const root = env.getAlloc(allocator, "SystemRoot") catch |err| switch (err) {
// OOM is fatal — only fall back to the `windir` alias when SystemRoot
// is genuinely absent, not when the allocation itself failed.
error.OutOfMemory => return error.OutOfMemory,
else => try env.getAlloc(allocator, "windir"),
};
Both fallbacks (SystemRoot→windir, and windowsTarPath→bare tar) used a
catch-all `else` that masked any non-OOM error. Replace with the explicit
two-case form matching envVarOwnedOptional (android_sdk.zig): fall back only on
EnvironmentVariableMissing/InvalidWtf8 (the 'effectively unset' cases), and
propagate everything else (OOM + any future error) instead of silently running
whatever `tar` is on PATH. Addresses Copilot review on #267.
@apotema

apotema commented Jun 29, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the two Copilot findings on the Windows tar-path fallbacks (astcenc_bin.zig :124 / :163) in ad64e71.

Both used a catch-all else => fallback that masked any non-OOM error. Rather than propagate InvalidWtf8 (which would diverge from the repo's own envVarOwnedOptional, android_sdk.zig:251-254, that intentionally collapses unset / invalid-UTF8 to null), I matched that convention with the explicit two-case form:

catch |err| switch (err) {
    error.EnvironmentVariableMissing, error.InvalidWtf8 => <fallback>,
    else => return err,  // OOM + any future error propagate
};

So the fallback fires only for the genuinely unset/invalid env cases, and OOM (or any future error in the set) propagates instead of silently running whatever tar is on PATH — addressing the masking concern while staying consistent with the codebase's established env-var handling. Build + tests green.

@apotema
apotema merged commit 4e4550c into main Jun 29, 2026
6 checks passed
@apotema
apotema deleted the fix/windows-env-snapshot-and-astc-tar branch June 29, 2026 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants