diff --git a/.github/versions.lock b/.github/versions.lock index 1ebac4aa..44acfd71 100644 --- a/.github/versions.lock +++ b/.github/versions.lock @@ -40,6 +40,12 @@ HYPERFINE_VERSION=1.18.0 GO_VERSION=1.25.5 # install-tools.ps1 release zip on Windows; Nix devshell on Linux/macOS. TINYGO_VERSION=0.40.1 +# install-tools.ps1 release tarball on Windows; Linux/macOS get +# binaryen via the Nix tinygo wrapper which prepends binaryen-125 to +# PATH automatically (verified locally — see flake.lock). Pinning +# Windows explicitly here keeps the realworld TinyGo programs +# (which call wasm-opt as part of their build pipeline) reproducible. +BINARYEN_VERSION=125 # === [planned] tools (informational; not yet read by any script) === diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85830abd..f7bfb5fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,10 +25,11 @@ jobs: # the same split as developers using `rustup target add # wasm32-wasip1` on top of the Nix devshell. # - # Windows continues to run the existing per-tool install path in - # the `test` job below; PR-D will migrate it to - # `pwsh scripts/windows/install-tools.ps1` + the same gate-commit - # entrypoint. + # Windows uses `scripts/windows/install-tools.ps1` to provision the + # same toolset (Zig / WASI SDK / wasm-tools / wasmtime / TinyGo / + # Go / Rust) into `%LOCALAPPDATA%\zwasm-tools`, exposes them via + # `$GITHUB_PATH` / `$GITHUB_ENV`, and then runs the same + # `scripts/gate-commit.sh` entrypoint under Git Bash. test-nix: name: test (${{ matrix.os }}, nix devshell) strategy: @@ -80,63 +81,128 @@ jobs: bash scripts/gate-commit.sh ' + # Extras the Commit Gate intentionally skips — these are + # CI-specific quality gates that run after gate-commit.sh: + # Zig-built C API tests (separate test program path from + # FFI tests), static-link workflow, the Rust embedding + # example, and a 4.5 MB peak-RSS check via /usr/bin/time. + + - name: Run C API tests (zig build c-test) + run: nix develop --command zig build c-test + + # Order matters on Windows: `zig build static-lib` produces a + # `zwasm.lib` that has the same filename as the shared-lib + # import library written to `zig-out/lib/zwasm.lib`. Linux and + # macOS use distinct extensions (`libzwasm.a` vs + # `.so` / `.dylib`) and don't have this collision, but to keep + # the order symmetric across all OSes the Rust dynamic example + # runs *before* the static-lib build. + + - name: Run Rust FFI example (dynamic) + run: | + nix develop --command bash -c ' + export PATH="$HOME/.cargo/bin:$PATH" + cd examples/rust && cargo run + ' + + - name: Build static library (PIC + compiler_rt) + run: nix develop --command zig build static-lib -Dpic=true -Dcompiler-rt=true + + - name: Run static link tests + run: nix develop --command bash test/c_api/run_static_link_test.sh + + - name: Memory usage check (POSIX) + run: | + BINARY=zig-out/bin/zwasm + LIMIT_KB=4608 # 4.5 MB + if [ "$(uname)" = "Darwin" ]; then + MEM_OUTPUT=$(/usr/bin/time -l "$BINARY" --invoke sieve bench/wasm/sieve.wasm 1000000 2>&1 >/dev/null) + MEM_BYTES=$(echo "$MEM_OUTPUT" | grep "maximum resident set size" | awk '{print $1}') + MEM_KB=$((MEM_BYTES / 1024)) + else + MEM_OUTPUT=$(/usr/bin/time -v "$BINARY" --invoke sieve bench/wasm/sieve.wasm 1000000 2>&1 >/dev/null) + MEM_KB=$(echo "$MEM_OUTPUT" | grep "Maximum resident set size" | awk '{print $NF}') + fi + MEM_MB=$(python3 -c "print(f'{int(${MEM_KB}) / 1024:.2f}')") + echo "Peak memory: ${MEM_MB} MB (${MEM_KB} KB)" + if [ "$MEM_KB" -gt "$LIMIT_KB" ]; then + echo "FAIL: Peak memory exceeds 4.5 MB limit" + exit 1 + fi + echo "PASS: Within 4.5 MB limit" + test: + name: test (windows-latest) + runs-on: windows-latest defaults: run: shell: bash - strategy: - fail-fast: false - matrix: - os: [windows-latest] - runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Install Zig - uses: goto-bus-stop/setup-zig@v2 - with: - version: 0.16.0 - - name: Install Python uses: actions/setup-python@v5 with: python-version: '3.x' + - name: Provision toolchain (install-tools.ps1, no rust) + # install-tools.ps1 reads .github/versions.lock and provisions + # Zig + WASI SDK + wasm-tools + wasmtime + Go + TinyGo into + # %LOCALAPPDATA%\zwasm-tools, exporting PATH / WASI_SDK_PATH + # via $GITHUB_PATH and $GITHUB_ENV so subsequent steps see + # them. Rust is intentionally skipped — the GitHub-hosted + # Windows runner ships with rustup pre-installed, and using + # it directly mirrors the Linux/macOS test-nix pattern. The + # local-Windows path that does invoke install-tools.ps1 + # without -SkipRust still works. + shell: pwsh + run: pwsh -NoLogo -File scripts/windows/install-tools.ps1 -SkipRust + + - name: Setup Rust (runner's rustup, wasm32-wasip1 target) + run: | + source .github/versions.lock + rustup install "$RUST_VERSION" --no-self-update + rustup default "$RUST_VERSION" + rustup target add wasm32-wasip1 + rustc --version + - name: Cache Zig build artifacts uses: actions/cache@v4 with: path: | .zig-cache zig-cache - ~/.cache/zig ~/AppData/Local/zig - key: zig-${{ runner.os }}-${{ hashFiles('build.zig', 'build.zig.zon', 'src/**/*.zig') }} + key: zig-Windows-installtools-${{ hashFiles('build.zig', 'build.zig.zon', 'src/**/*.zig') }} restore-keys: | - zig-${{ runner.os }}- + zig-Windows-installtools- - - name: Build - run: zig build - - - name: Run unit tests - run: zig build test - - - name: Run C API tests - run: zig build c-test + - name: Sanity-check provisioned toolchain + run: | + set -euo pipefail + echo "=== install-tools.ps1 toolchain ===" + zig version + wasm-tools --version + wasmtime --version + rustc --version + rustup target list --installed + go version + tinygo version + echo "WASI_SDK_PATH=$WASI_SDK_PATH" - - name: Build shared library - run: zig build shared-lib + - name: Run Commit Gate + run: bash scripts/gate-commit.sh - - name: Run FFI tests (shared library) - run: bash test/c_api/run_ffi_test.sh + # Extras the Commit Gate intentionally skips — same set the + # test-nix job runs on Linux/macOS. - - name: Setup Rust - run: | - source .github/versions.lock - rustup install "$RUST_VERSION" --no-self-update - rustup default "$RUST_VERSION" - rustup target add wasm32-wasip1 - rustc --version + - name: Run C API tests (zig build c-test) + run: zig build c-test + # Order matters on Windows — see test-nix above. cargo run uses + # the shared-lib import library at zig-out/lib/zwasm.lib; the + # subsequent `zig build static-lib` would otherwise overwrite + # it with the static archive (LNK1143 from MSVC link.exe). - name: Run Rust FFI example (dynamic) run: cd examples/rust && cargo run @@ -146,105 +212,7 @@ jobs: - name: Run static link tests run: bash test/c_api/run_static_link_test.sh - - name: Install wasm-tools - run: | - source .github/versions.lock - cargo install wasm-tools --locked --version "$WASM_TOOLS_VERSION" - - - name: Download WebAssembly spec testsuite - run: git clone --depth 1 https://github.com/WebAssembly/spec.git "${{ runner.temp }}/wasm-spec" - - - name: Convert spec tests - run: python test/spec/convert.py "${{ runner.temp }}/wasm-spec/test/core" - - - name: Run spec tests (strict) - run: python test/spec/run_spec.py --build --summary --strict - - - name: Download wasmtime misc_testsuite - run: | - git clone --depth 1 --filter=blob:none --sparse \ - https://github.com/bytecodealliance/wasmtime.git "${{ runner.temp }}/wasmtime" - cd "${{ runner.temp }}/wasmtime" - git sparse-checkout set tests/misc_testsuite - - - name: Run E2E tests - env: - WASMTIME_MISC_DIR: ${{ runner.temp }}/wasmtime/tests/misc_testsuite - run: python test/e2e/run_e2e.py --convert --summary --verbose - - - name: Build ReleaseSafe - run: zig build -Doptimize=ReleaseSafe - - - name: Binary size check - shell: bash - run: | - # Build a stripped binary into an isolated prefix so the - # main `zig-out/bin/zwasm` (used by the memory check + later - # realworld tests) stays untouched. `-Dstrip=true` strips at - # link time via LLD; portable across ELF / Mach-O / PE - # without depending on a host `strip` tool (Windows runners - # don't have GNU strip; `zig objcopy --strip-all` is - # ELF-only). - rm -rf .strip-cache - zig build -Dstrip=true -Doptimize=ReleaseSafe --prefix .strip-cache - # Per-OS ceilings — regression guard, not a parity target. - # PE has higher reloc/import overhead than ELF; Mach-O is the - # most compact of the three. Each ceiling tracks the observed - # stripped size with ~80-100 KB of headroom so a real - # regression trips the gate before the budget runs out. - if [ "${{ runner.os }}" = "Windows" ]; then - STRIPPED=.strip-cache/bin/zwasm.exe - BINARY=zig-out/bin/zwasm.exe - LIMIT_BYTES=1887436 # 1.80 MB — PE (observed ~1.70 MB) - LIMIT_MB="1.80" - elif [ "${{ runner.os }}" = "macOS" ]; then - STRIPPED=.strip-cache/bin/zwasm - BINARY=zig-out/bin/zwasm - LIMIT_BYTES=1363148 # 1.30 MB — Mach-O (observed ~1.20 MB) - LIMIT_MB="1.30" - else - # Linux / other ELF - STRIPPED=.strip-cache/bin/zwasm - BINARY=zig-out/bin/zwasm - LIMIT_BYTES=1677721 # 1.60 MB — ELF (observed ~1.56 MB) - LIMIT_MB="1.60" - fi - SIZE_BYTES=$(wc -c < "$STRIPPED" | tr -d ' ') - SIZE_MB=$(python -c "print(f'{$SIZE_BYTES / 1048576:.2f}')") - RAW_BYTES=$(wc -c < "$BINARY" | tr -d ' ') - RAW_MB=$(python -c "print(f'{$RAW_BYTES / 1048576:.2f}')") - echo "Binary size (raw): ${RAW_MB} MB ($RAW_BYTES bytes)" - echo "Binary size (stripped): ${SIZE_MB} MB ($SIZE_BYTES bytes)" - echo "Ceiling for ${{ runner.os }}: ${LIMIT_MB} MB ($LIMIT_BYTES bytes)" - if [ "$SIZE_BYTES" -gt "$LIMIT_BYTES" ]; then - echo "FAIL: Stripped binary exceeds ${LIMIT_MB} MB limit" - exit 1 - fi - echo "PASS: Within ${LIMIT_MB} MB limit" - - - name: Memory usage check (POSIX) - if: runner.os != 'Windows' - run: | - BINARY=zig-out/bin/zwasm - LIMIT_KB=4608 # 4.5 MB - if [ "$(uname)" = "Darwin" ]; then - MEM_OUTPUT=$(/usr/bin/time -l "$BINARY" --invoke sieve bench/wasm/sieve.wasm 1000000 2>&1 >/dev/null) - MEM_BYTES=$(echo "$MEM_OUTPUT" | grep "maximum resident set size" | awk '{print $1}') - MEM_KB=$((MEM_BYTES / 1024)) - else - MEM_OUTPUT=$(/usr/bin/time -v "$BINARY" --invoke sieve bench/wasm/sieve.wasm 1000000 2>&1 >/dev/null) - MEM_KB=$(echo "$MEM_OUTPUT" | grep "Maximum resident set size" | awk '{print $NF}') - fi - MEM_MB=$(python -c "print(f'{int(${MEM_KB}) / 1024:.2f}')") - echo "Peak memory: ${MEM_MB} MB (${MEM_KB} KB)" - if [ "$MEM_KB" -gt "$LIMIT_KB" ]; then - echo "FAIL: Peak memory exceeds 4.5 MB limit" - exit 1 - fi - echo "PASS: Within 4.5 MB limit" - - name: Memory usage check (Windows) - if: runner.os == 'Windows' shell: pwsh run: | $LIMIT_KB = 4608 # 4.5 MB, same budget as POSIX path @@ -275,85 +243,6 @@ jobs: } Write-Host "PASS: Within 4.5 MB limit" - - name: Install wasmtime - run: | - source .github/versions.lock - if [ "${{ runner.os }}" = "macOS" ]; then - ARCH="aarch64"; OS="macos"; EXT="tar.xz"; BIN_NAME="wasmtime" - elif [ "${{ runner.os }}" = "Windows" ]; then - ARCH="x86_64"; OS="windows"; EXT="zip"; BIN_NAME="wasmtime.exe" - else - ARCH="x86_64"; OS="linux"; EXT="tar.xz"; BIN_NAME="wasmtime" - fi - ARCHIVE="wasmtime-v${WASMTIME_VERSION}-${ARCH}-${OS}.${EXT}" - curl -L --retry 3 --retry-delay 5 -f -o "${{ runner.temp }}/wasmtime.${EXT}" \ - "https://github.com/bytecodealliance/wasmtime/releases/download/v${WASMTIME_VERSION}/${ARCHIVE}" - mkdir -p "$HOME/.wasmtime/bin" - if [ "$EXT" = "zip" ]; then - unzip -q "${{ runner.temp }}/wasmtime.${EXT}" -d "${{ runner.temp }}/wasmtime-extract" - cp "${{ runner.temp }}/wasmtime-extract/wasmtime-v${WASMTIME_VERSION}-${ARCH}-${OS}/${BIN_NAME}" "$HOME/.wasmtime/bin/" - else - tar xJf "${{ runner.temp }}/wasmtime.${EXT}" -C "${{ runner.temp }}" - cp "${{ runner.temp }}/wasmtime-v${WASMTIME_VERSION}-${ARCH}-${OS}/${BIN_NAME}" "$HOME/.wasmtime/bin/" - fi - - - name: Install WASI SDK - if: runner.os != 'Windows' - run: | - source .github/versions.lock - if [ "${{ runner.os }}" = "macOS" ]; then - ARCH="arm64"; OS="macos" - else - ARCH="x86_64"; OS="linux" - fi - ARCHIVE="wasi-sdk-${WASI_SDK_VERSION}.0-${ARCH}-${OS}.tar.gz" - curl -L --retry 3 --retry-delay 5 -f -o "${{ runner.temp }}/wasi-sdk.tar.gz" \ - "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/${ARCHIVE}" - mkdir -p "${{ runner.temp }}/wasi-sdk" - tar xzf "${{ runner.temp }}/wasi-sdk.tar.gz" -C "${{ runner.temp }}/wasi-sdk" --strip-components=1 - - - name: Install WASI SDK (Windows) - if: runner.os == 'Windows' - shell: python - run: | - import pathlib - import tarfile - import urllib.request - - tool_versions = pathlib.Path(".github/versions.lock").read_text(encoding="utf-8").splitlines() - wasi_sdk_version = next( - line.split("=", 1)[1].split("#", 1)[0].strip().strip('"') - for line in tool_versions - if line.startswith("WASI_SDK_VERSION=") - ) - archive = pathlib.Path(r"${{ runner.temp }}\wasi-sdk.tar.gz") - dest = pathlib.Path(r"${{ runner.temp }}\wasi-sdk") - dest.mkdir(parents=True, exist_ok=True) - - url = ( - f"https://github.com/WebAssembly/wasi-sdk/releases/download/" - f"wasi-sdk-{wasi_sdk_version}/wasi-sdk-{wasi_sdk_version}.0-x86_64-windows.tar.gz" - ) - urllib.request.urlretrieve(url, archive) - - with tarfile.open(archive, "r:gz") as tf: - for member in tf.getmembers(): - parts = pathlib.PurePosixPath(member.name).parts - if len(parts) <= 1: - continue - member.name = str(pathlib.PurePosixPath(*parts[1:])) - tf.extract(member, dest) - - - name: Build real-world wasm programs - env: - WASI_SDK_PATH: ${{ runner.temp }}/wasi-sdk - run: python test/realworld/build_all.py - - - name: Run real-world compat tests - run: | - export PATH="$HOME/.wasmtime/bin:$PATH" - python test/realworld/run_compat.py - size-matrix: strategy: fail-fast: false diff --git a/scripts/windows/install-tools.ps1 b/scripts/windows/install-tools.ps1 index 1253563b..aec9675d 100644 --- a/scripts/windows/install-tools.ps1 +++ b/scripts/windows/install-tools.ps1 @@ -23,7 +23,7 @@ .PARAMETER OnlyTool Install just one tool. Accepts: zig, wasm-tools, wasmtime, wasi-sdk, - rust, go, tinygo (and 'all', the default). + rust, go, tinygo, binaryen (and 'all', the default). .EXAMPLE pwsh -NoLogo -File scripts\windows\install-tools.ps1 @@ -35,7 +35,13 @@ [CmdletBinding()] param( [switch]$Force, - [ValidateSet('zig', 'wasm-tools', 'wasmtime', 'wasi-sdk', 'rust', 'go', 'tinygo', 'all')] + # When set, skip the rust install entirely. CI runners ship with + # rustup pre-installed and are happy to `rustup target add + # wasm32-wasip1` directly; calling install-tools.ps1 with + # -SkipRust avoids re-bootstrapping a self-contained rustup tree + # under %LOCALAPPDATA%\zwasm-tools\rust-stable\. + [switch]$SkipRust, + [ValidateSet('zig', 'wasm-tools', 'wasmtime', 'wasi-sdk', 'rust', 'go', 'tinygo', 'binaryen', 'all')] [string]$OnlyTool = 'all' ) @@ -84,13 +90,21 @@ foreach ($k in 'ZIG_VERSION', 'WASM_TOOLS_VERSION', 'WASMTIME_VERSION', 'WASI_SD throw "install-tools.ps1: $k missing from versions.lock" } } -# Realworld toolchain pins (W52). Fail loudly if a requested install -# needs them but they're missing — keeps the script honest about its -# inputs. -$realworldKeys = @{ rust = 'RUST_VERSION'; go = 'GO_VERSION'; tinygo = 'TINYGO_VERSION' } +# Realworld toolchain pins (W52 + binaryen). Fail loudly if a requested +# install needs them but they're missing — keeps the script honest +# about its inputs. +$realworldKeys = @{ + rust = 'RUST_VERSION' + go = 'GO_VERSION' + tinygo = 'TINYGO_VERSION' + binaryen = 'BINARYEN_VERSION' +} foreach ($pair in $realworldKeys.GetEnumerator()) { $tool = $pair.Key; $key = $pair.Value - if ($OnlyTool -in @('all', $tool) -and -not $versions.ContainsKey($key)) { + # binaryen is also pulled in transitively when 'tinygo' is requested + # (TinyGo invokes wasm-opt at build time). + $needs = ($OnlyTool -in @('all', $tool)) -or ($tool -eq 'binaryen' -and $OnlyTool -eq 'tinygo') + if ($needs -and -not $versions.ContainsKey($key)) { throw "install-tools.ps1: $key missing from versions.lock (needed for $tool install)" } } @@ -261,6 +275,20 @@ if ($OnlyTool -in @('all', 'tinygo')) { $paths['tinygo'] = $dir } +if ($OnlyTool -in @('all', 'binaryen', 'tinygo')) { + # TinyGo invokes `wasm-opt` as part of its wasm build pipeline. + # On Linux/macOS the Nix `tinygo` derivation is wrapped to prepend + # binaryen-125's bin/ to PATH automatically; on Windows we install + # binaryen explicitly so `wasm-opt` is on PATH for tinygo. + # The release tarball extracts to + # `binaryen-version_-x86_64-windows/bin/wasm-opt.exe` — Resolve- + # SingleSubdir will flatten the version-stamped top dir, leaving + # `/bin/wasm-opt.exe`. + $url = "https://github.com/WebAssembly/binaryen/releases/download/version_$($versions.BINARYEN_VERSION)/binaryen-version_$($versions.BINARYEN_VERSION)-x86_64-windows.tar.gz" + $dir = Install-Tool -Name 'binaryen' -Version $versions.BINARYEN_VERSION -Url $url -Format 'tar.gz' + $paths['binaryen'] = $dir +} + # Rustup is special: it's a self-installer (rustup-init.exe), not an # archive. Install into a stamped directory under $installRoot with # its own CARGO_HOME / RUSTUP_HOME so the install is self-contained @@ -308,13 +336,37 @@ function Install-Rustup { return $stampedDir } -if ($OnlyTool -in @('all', 'rust')) { +if ($OnlyTool -in @('all', 'rust') -and -not $SkipRust) { $rustToolchain = $versions.RUST_VERSION # e.g. 'stable' $rustRoot = Install-Rustup -Toolchain $rustToolchain -InstallRoot $installRoot $paths['rust'] = $rustRoot } -# --- PATH and env wiring (User scope) --- +# --- PATH and env wiring (User scope, plus GitHub Actions if present) --- +# +# In CI the runner exports `$GITHUB_PATH` and `$GITHUB_ENV` — appending +# entries to those files exposes the change to subsequent steps in +# the same job. Local Windows installs do not have those vars set; +# behaviour falls back to the original User-scope-only path. + +$inGithubActions = ($env:GITHUB_PATH -and (Test-Path $env:GITHUB_PATH)) + +function Append-GithubPath { + param([Parameter(Mandatory)][string]$Entry) + if ($inGithubActions) { + Add-Content -Path $env:GITHUB_PATH -Value $Entry -Encoding utf8 + } +} + +function Append-GithubEnv { + param( + [Parameter(Mandatory)][string]$Key, + [Parameter(Mandatory)][string]$Value + ) + if ($inGithubActions -and (Test-Path $env:GITHUB_ENV)) { + Add-Content -Path $env:GITHUB_ENV -Value "$Key=$Value" -Encoding utf8 + } +} function Update-UserPath { param([Parameter(Mandatory)][string[]]$Add) @@ -329,6 +381,9 @@ function Update-UserPath { Write-Host "[path] +$p" $changed = $true } + # Always export to GITHUB_PATH so a re-run with cached User + # PATH still propagates entries to the current GHA job. + Append-GithubPath -Entry $p } if ($changed) { [Environment]::SetEnvironmentVariable('Path', ($entries -join ';'), 'User') @@ -341,18 +396,21 @@ function Update-UserPath { # (no bin/ subdir on Windows). # go — bin/ subdir holding go.exe + gofmt.exe. # tinygo — bin/ subdir holding tinygo.exe. +# binaryen — bin/ subdir holding wasm-opt.exe et al. # rust — cargo/bin/ holding cargo.exe + rustup.exe. $pathsToAdd = @() if ($paths.ContainsKey('zig')) { $pathsToAdd += $paths['zig'] } if ($paths.ContainsKey('wasm-tools')) { $pathsToAdd += $paths['wasm-tools'] } if ($paths.ContainsKey('wasmtime')) { $pathsToAdd += $paths['wasmtime'] } -if ($paths.ContainsKey('go')) { $pathsToAdd += (Join-Path $paths['go'] 'bin') } -if ($paths.ContainsKey('tinygo')) { $pathsToAdd += (Join-Path $paths['tinygo'] 'bin') } +if ($paths.ContainsKey('go')) { $pathsToAdd += (Join-Path $paths['go'] 'bin') } +if ($paths.ContainsKey('tinygo')) { $pathsToAdd += (Join-Path $paths['tinygo'] 'bin') } +if ($paths.ContainsKey('binaryen')) { $pathsToAdd += (Join-Path $paths['binaryen'] 'bin') } if ($paths.ContainsKey('rust')) { $pathsToAdd += (Join-Path (Join-Path $paths['rust'] 'cargo') 'bin') } Update-UserPath -Add $pathsToAdd if ($paths.ContainsKey('wasi-sdk')) { [Environment]::SetEnvironmentVariable('WASI_SDK_PATH', $paths['wasi-sdk'], 'User') + Append-GithubEnv -Key 'WASI_SDK_PATH' -Value $paths['wasi-sdk'] Write-Host "[env] WASI_SDK_PATH=$($paths['wasi-sdk'])" } @@ -364,6 +422,8 @@ if ($paths.ContainsKey('rust')) { $rustupHome = Join-Path $rustRoot 'rustup' [Environment]::SetEnvironmentVariable('CARGO_HOME', $cargoHome, 'User') [Environment]::SetEnvironmentVariable('RUSTUP_HOME', $rustupHome, 'User') + Append-GithubEnv -Key 'CARGO_HOME' -Value $cargoHome + Append-GithubEnv -Key 'RUSTUP_HOME' -Value $rustupHome Write-Host "[env] CARGO_HOME=$cargoHome" Write-Host "[env] RUSTUP_HOME=$rustupHome" }