From 304baf733ed5c9fcc59cbedf609894ee8cd6bbc2 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Wed, 11 Feb 2026 14:37:32 -0800 Subject: [PATCH] feat: build windows support binaries in parallel --- .github/workflows/rust-release-windows.yml | 269 +++++++++++++++++++++ .github/workflows/rust-release.yml | 123 ++-------- 2 files changed, 286 insertions(+), 106 deletions(-) create mode 100644 .github/workflows/rust-release-windows.yml diff --git a/.github/workflows/rust-release-windows.yml b/.github/workflows/rust-release-windows.yml new file mode 100644 index 00000000000..ea42ef11e8c --- /dev/null +++ b/.github/workflows/rust-release-windows.yml @@ -0,0 +1,269 @@ +name: rust-release-windows + +on: + workflow_call: + inputs: + release-lto: + required: true + type: string + secrets: + AZURE_TRUSTED_SIGNING_CLIENT_ID: + required: true + AZURE_TRUSTED_SIGNING_TENANT_ID: + required: true + AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID: + required: true + AZURE_TRUSTED_SIGNING_ENDPOINT: + required: true + AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: + required: true + AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME: + required: true + +jobs: + build-windows-binaries: + name: Build Windows binaries - ${{ matrix.runner }} - ${{ matrix.target }} - ${{ matrix.bundle }} + runs-on: ${{ matrix.runs_on }} + timeout-minutes: 60 + permissions: + contents: read + defaults: + run: + working-directory: codex-rs + env: + CARGO_PROFILE_RELEASE_LTO: ${{ inputs.release-lto }} + + strategy: + fail-fast: false + matrix: + include: + - runner: windows-x64 + target: x86_64-pc-windows-msvc + bundle: primary + build_args: --bin codex --bin codex-responses-api-proxy + runs_on: + group: codex-runners + labels: codex-windows-x64 + - runner: windows-arm64 + target: aarch64-pc-windows-msvc + bundle: primary + build_args: --bin codex --bin codex-responses-api-proxy + runs_on: + group: codex-runners + labels: codex-windows-arm64 + - runner: windows-x64 + target: x86_64-pc-windows-msvc + bundle: helpers + build_args: --bin codex-windows-sandbox-setup --bin codex-command-runner + runs_on: + group: codex-runners + labels: codex-windows-x64 + - runner: windows-arm64 + target: aarch64-pc-windows-msvc + bundle: helpers + build_args: --bin codex-windows-sandbox-setup --bin codex-command-runner + runs_on: + group: codex-runners + labels: codex-windows-arm64 + + steps: + - uses: actions/checkout@v6 + - name: Print runner specs (Windows) + shell: powershell + run: | + $computer = Get-CimInstance Win32_ComputerSystem + $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 + $ramGiB = [math]::Round($computer.TotalPhysicalMemory / 1GB, 1) + Write-Host "Runner: $env:RUNNER_NAME" + Write-Host "OS: $([System.Environment]::OSVersion.VersionString)" + Write-Host "CPU: $($cpu.Name)" + Write-Host "Logical CPUs: $($computer.NumberOfLogicalProcessors)" + Write-Host "Physical CPUs: $($computer.NumberOfProcessors)" + Write-Host "Total RAM: $ramGiB GiB" + Write-Host "Disk usage:" + Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize Name, @{Name='Size(GB)';Expression={[math]::Round(($_.Used + $_.Free) / 1GB, 1)}}, @{Name='Free(GB)';Expression={[math]::Round($_.Free / 1GB, 1)}} + - uses: dtolnay/rust-toolchain@1.93 + with: + targets: ${{ matrix.target }} + + - uses: actions/cache@v5 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + ${{ github.workspace }}/codex-rs/target/ + key: cargo-${{ matrix.runner }}-${{ matrix.target }}-release-windows-${{ matrix.bundle }}-${{ hashFiles('**/Cargo.lock') }} + + - name: Cargo build (Windows binaries) + shell: bash + run: | + cargo build --target ${{ matrix.target }} --release ${{ matrix.build_args }} + + - name: Stage Windows binaries + shell: bash + run: | + output_dir="target/${{ matrix.target }}/release/staged-${{ matrix.bundle }}" + mkdir -p "$output_dir" + if [[ "${{ matrix.bundle }}" == "primary" ]]; then + cp target/${{ matrix.target }}/release/codex.exe "$output_dir/codex.exe" + cp target/${{ matrix.target }}/release/codex-responses-api-proxy.exe "$output_dir/codex-responses-api-proxy.exe" + else + cp target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe "$output_dir/codex-windows-sandbox-setup.exe" + cp target/${{ matrix.target }}/release/codex-command-runner.exe "$output_dir/codex-command-runner.exe" + fi + + - name: Upload Windows binaries + uses: actions/upload-artifact@v6 + with: + name: windows-binaries-${{ matrix.target }}-${{ matrix.bundle }} + path: | + codex-rs/target/${{ matrix.target }}/release/staged-${{ matrix.bundle }}/* + + build-windows: + needs: + - build-windows-binaries + name: Build - ${{ matrix.runner }} - ${{ matrix.target }} + runs-on: ${{ matrix.runs_on }} + timeout-minutes: 60 + permissions: + contents: read + id-token: write + defaults: + run: + working-directory: codex-rs + + strategy: + fail-fast: false + matrix: + include: + - runner: windows-x64 + target: x86_64-pc-windows-msvc + runs_on: + group: codex-runners + labels: codex-windows-x64 + - runner: windows-arm64 + target: aarch64-pc-windows-msvc + runs_on: + group: codex-runners + labels: codex-windows-arm64 + + steps: + - uses: actions/checkout@v6 + + - name: Download prebuilt Windows primary binaries + uses: actions/download-artifact@v7 + with: + name: windows-binaries-${{ matrix.target }}-primary + path: codex-rs/target/${{ matrix.target }}/release + + - name: Download prebuilt Windows helper binaries + uses: actions/download-artifact@v7 + with: + name: windows-binaries-${{ matrix.target }}-helpers + path: codex-rs/target/${{ matrix.target }}/release + + - name: Verify binaries + shell: bash + run: | + set -euo pipefail + ls -lh target/${{ matrix.target }}/release/codex.exe + ls -lh target/${{ matrix.target }}/release/codex-responses-api-proxy.exe + ls -lh target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe + ls -lh target/${{ matrix.target }}/release/codex-command-runner.exe + + - name: Sign Windows binaries with Azure Trusted Signing + uses: ./.github/actions/windows-code-sign + with: + target: ${{ matrix.target }} + client-id: ${{ secrets.AZURE_TRUSTED_SIGNING_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TRUSTED_SIGNING_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID }} + endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} + account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} + certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} + + - name: Stage artifacts + shell: bash + run: | + dest="dist/${{ matrix.target }}" + mkdir -p "$dest" + + cp target/${{ matrix.target }}/release/codex.exe "$dest/codex-${{ matrix.target }}.exe" + cp target/${{ matrix.target }}/release/codex-responses-api-proxy.exe "$dest/codex-responses-api-proxy-${{ matrix.target }}.exe" + cp target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe "$dest/codex-windows-sandbox-setup-${{ matrix.target }}.exe" + cp target/${{ matrix.target }}/release/codex-command-runner.exe "$dest/codex-command-runner-${{ matrix.target }}.exe" + + - if: ${{ matrix.runner == 'windows-arm64' }} + name: Install zstd + shell: powershell + run: choco install -y zstandard + + - name: Compress artifacts + shell: bash + run: | + # Path that contains the uncompressed binaries for the current + # ${{ matrix.target }} + dest="dist/${{ matrix.target }}" + repo_root=$PWD + + # For compatibility with environments that lack the `zstd` tool we + # additionally create a `.tar.gz` and `.zip` for every Windows binary. + # The end result is: + # codex-.zst + # codex-.tar.gz + # codex-.zip + for f in "$dest"/*; do + base="$(basename "$f")" + # Skip files that are already archives (shouldn't happen, but be + # safe). + if [[ "$base" == *.tar.gz || "$base" == *.zip || "$base" == *.dmg ]]; then + continue + fi + + # Don't try to compress signature bundles. + if [[ "$base" == *.sigstore ]]; then + continue + fi + + # Create per-binary tar.gz + tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base" + + # Create zip archive for Windows binaries. + # Must run from inside the dest dir so 7z won't embed the + # directory path inside the zip. + if [[ "$base" == "codex-${{ matrix.target }}.exe" ]]; then + # Bundle the sandbox helper binaries into the main codex zip so + # WinGet installs include the required helpers next to codex.exe. + # Fall back to the single-binary zip if the helpers are missing + # to avoid breaking releases. + bundle_dir="$(mktemp -d)" + runner_src="$dest/codex-command-runner-${{ matrix.target }}.exe" + setup_src="$dest/codex-windows-sandbox-setup-${{ matrix.target }}.exe" + if [[ -f "$runner_src" && -f "$setup_src" ]]; then + cp "$dest/$base" "$bundle_dir/$base" + cp "$runner_src" "$bundle_dir/codex-command-runner.exe" + cp "$setup_src" "$bundle_dir/codex-windows-sandbox-setup.exe" + # Use an absolute path so bundle zips land in the real dist + # dir even when 7z runs from a temp directory. + (cd "$bundle_dir" && 7z a "$repo_root/$dest/${base}.zip" .) + else + echo "warning: missing sandbox binaries; falling back to single-binary zip" + echo "warning: expected $runner_src and $setup_src" + (cd "$dest" && 7z a "${base}.zip" "$base") + fi + rm -rf "$bundle_dir" + else + (cd "$dest" && 7z a "${base}.zip" "$base") + fi + + # Keep raw executables and produce .zst alongside them. + zstd -T0 -19 "$dest/$base" + done + + - uses: actions/upload-artifact@v6 + with: + name: ${{ matrix.target }} + path: | + codex-rs/dist/${{ matrix.target }}/* diff --git a/.github/workflows/rust-release.yml b/.github/workflows/rust-release.yml index 8b084b1378c..d3e44e270fc 100644 --- a/.github/workflows/rust-release.yml +++ b/.github/workflows/rust-release.yml @@ -76,16 +76,6 @@ jobs: target: aarch64-unknown-linux-musl - runner: ubuntu-24.04-arm target: aarch64-unknown-linux-gnu - - runner: windows-x64 - target: x86_64-pc-windows-msvc - runs_on: - group: codex-runners - labels: codex-windows-x64 - - runner: windows-arm64 - target: aarch64-pc-windows-msvc - runs_on: - group: codex-runners - labels: codex-windows-arm64 steps: - uses: actions/checkout@v6 @@ -115,21 +105,6 @@ jobs: echo "Total RAM: $(sysctl -n hw.memsize | awk '{printf \"%.1f GiB\\n\", $1 / 1024 / 1024 / 1024}')" echo "Disk usage:" df -h . - - name: Print runner specs (Windows) - if: ${{ runner.os == 'Windows' }} - shell: powershell - run: | - $computer = Get-CimInstance Win32_ComputerSystem - $cpu = Get-CimInstance Win32_Processor | Select-Object -First 1 - $ramGiB = [math]::Round($computer.TotalPhysicalMemory / 1GB, 1) - Write-Host "Runner: $env:RUNNER_NAME" - Write-Host "OS: $([System.Environment]::OSVersion.VersionString)" - Write-Host "CPU: $($cpu.Name)" - Write-Host "Logical CPUs: $($computer.NumberOfLogicalProcessors)" - Write-Host "Physical CPUs: $($computer.NumberOfProcessors)" - Write-Host "Total RAM: $ramGiB GiB" - Write-Host "Disk usage:" - Get-PSDrive -PSProvider FileSystem | Format-Table -AutoSize Name, @{Name='Size(GB)';Expression={[math]::Round(($_.Used + $_.Free) / 1GB, 1)}}, @{Name='Free(GB)';Expression={[math]::Round($_.Free / 1GB, 1)}} - name: Install Linux bwrap build dependencies if: ${{ runner.os == 'Linux' }} shell: bash @@ -242,11 +217,7 @@ jobs: - name: Cargo build shell: bash run: | - if [[ "${{ contains(matrix.target, 'windows') }}" == 'true' ]]; then - cargo build --target ${{ matrix.target }} --release --bin codex --bin codex-responses-api-proxy --bin codex-windows-sandbox-setup --bin codex-command-runner - else - cargo build --target ${{ matrix.target }} --release --bin codex --bin codex-responses-api-proxy - fi + cargo build --target ${{ matrix.target }} --release --bin codex --bin codex-responses-api-proxy - if: ${{ contains(matrix.target, 'linux') }} name: Cosign Linux artifacts @@ -255,18 +226,6 @@ jobs: target: ${{ matrix.target }} artifacts-dir: ${{ github.workspace }}/codex-rs/target/${{ matrix.target }}/release - - if: ${{ contains(matrix.target, 'windows') }} - name: Sign Windows binaries with Azure Trusted Signing - uses: ./.github/actions/windows-code-sign - with: - target: ${{ matrix.target }} - client-id: ${{ secrets.AZURE_TRUSTED_SIGNING_CLIENT_ID }} - tenant-id: ${{ secrets.AZURE_TRUSTED_SIGNING_TENANT_ID }} - subscription-id: ${{ secrets.AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID }} - endpoint: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} - account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} - certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }} - - if: ${{ runner.os == 'macOS' }} name: MacOS code signing (binaries) uses: ./.github/actions/macos-code-sign @@ -345,15 +304,8 @@ jobs: dest="dist/${{ matrix.target }}" mkdir -p "$dest" - if [[ "${{ matrix.runner }}" == windows* ]]; then - cp target/${{ matrix.target }}/release/codex.exe "$dest/codex-${{ matrix.target }}.exe" - cp target/${{ matrix.target }}/release/codex-responses-api-proxy.exe "$dest/codex-responses-api-proxy-${{ matrix.target }}.exe" - cp target/${{ matrix.target }}/release/codex-windows-sandbox-setup.exe "$dest/codex-windows-sandbox-setup-${{ matrix.target }}.exe" - cp target/${{ matrix.target }}/release/codex-command-runner.exe "$dest/codex-command-runner-${{ matrix.target }}.exe" - else - cp target/${{ matrix.target }}/release/codex "$dest/codex-${{ matrix.target }}" - cp target/${{ matrix.target }}/release/codex-responses-api-proxy "$dest/codex-responses-api-proxy-${{ matrix.target }}" - fi + cp target/${{ matrix.target }}/release/codex "$dest/codex-${{ matrix.target }}" + cp target/${{ matrix.target }}/release/codex-responses-api-proxy "$dest/codex-responses-api-proxy-${{ matrix.target }}" if [[ "${{ matrix.target }}" == *linux* ]]; then cp target/${{ matrix.target }}/release/codex.sigstore "$dest/codex-${{ matrix.target }}.sigstore" @@ -364,34 +316,18 @@ jobs: cp target/${{ matrix.target }}/release/codex-${{ matrix.target }}.dmg "$dest/codex-${{ matrix.target }}.dmg" fi - - if: ${{ matrix.runner == 'windows-arm64' }} - name: Install zstd - shell: powershell - run: choco install -y zstandard - - name: Compress artifacts shell: bash run: | # Path that contains the uncompressed binaries for the current # ${{ matrix.target }} dest="dist/${{ matrix.target }}" - repo_root=$PWD - - # We want to ship the raw Windows executables in the GitHub Release - # in addition to the compressed archives. Keep the originals for - # Windows targets; remove them elsewhere to limit the number of - # artifacts that end up in the GitHub Release. - keep_originals=false - if [[ "${{ matrix.runner }}" == windows* ]]; then - keep_originals=true - fi # For compatibility with environments that lack the `zstd` tool we - # additionally create a `.tar.gz` for all platforms and `.zip` for - # Windows alongside every single binary that we publish. The end result is: + # additionally create a `.tar.gz` alongside every binary we publish. + # The end result is: # codex-.zst (existing) # codex-.tar.gz (new) - # codex-.zip (only for Windows) # 1. Produce a .tar.gz for every file in the directory *before* we # run `zstd --rm`, because that flag deletes the original files. @@ -411,43 +347,9 @@ jobs: # Create per-binary tar.gz tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base" - # Create zip archive for Windows binaries - # Must run from inside the dest dir so 7z won't - # embed the directory path inside the zip. - if [[ "${{ matrix.runner }}" == windows* ]]; then - if [[ "$base" == "codex-${{ matrix.target }}.exe" ]]; then - # Bundle the sandbox helper binaries into the main codex zip so - # WinGet installs include the required helpers next to codex.exe. - # Fall back to the single-binary zip if the helpers are missing - # to avoid breaking releases. - bundle_dir="$(mktemp -d)" - runner_src="$dest/codex-command-runner-${{ matrix.target }}.exe" - setup_src="$dest/codex-windows-sandbox-setup-${{ matrix.target }}.exe" - if [[ -f "$runner_src" && -f "$setup_src" ]]; then - cp "$dest/$base" "$bundle_dir/$base" - cp "$runner_src" "$bundle_dir/codex-command-runner.exe" - cp "$setup_src" "$bundle_dir/codex-windows-sandbox-setup.exe" - # Use an absolute path so bundle zips land in the real dist - # dir even when 7z runs from a temp directory. - (cd "$bundle_dir" && 7z a "$repo_root/$dest/${base}.zip" .) - else - echo "warning: missing sandbox binaries; falling back to single-binary zip" - echo "warning: expected $runner_src and $setup_src" - (cd "$dest" && 7z a "${base}.zip" "$base") - fi - rm -rf "$bundle_dir" - else - (cd "$dest" && 7z a "${base}.zip" "$base") - fi - fi - - # Also create .zst (existing behaviour) *and* remove the original - # uncompressed binary to keep the directory small. - zstd_args=(-T0 -19) - if [[ "${keep_originals}" == false ]]; then - zstd_args+=(--rm) - fi - zstd "${zstd_args[@]}" "$dest/$base" + # Also create .zst and remove the uncompressed binaries to keep + # non-Windows artifact directories small. + zstd -T0 -19 --rm "$dest/$base" done - uses: actions/upload-artifact@v6 @@ -458,6 +360,13 @@ jobs: path: | codex-rs/dist/${{ matrix.target }}/* + build-windows: + needs: tag-check + uses: ./.github/workflows/rust-release-windows.yml + with: + release-lto: ${{ contains(github.ref_name, '-alpha') && 'thin' || 'fat' }} + secrets: inherit + shell-tool-mcp: name: shell-tool-mcp needs: tag-check @@ -470,6 +379,7 @@ jobs: release: needs: - build + - build-windows - shell-tool-mcp name: release runs-on: ubuntu-latest @@ -518,6 +428,7 @@ jobs: - name: Delete entries from dist/ that should not go in the release run: | rm -rf dist/shell-tool-mcp* + rm -rf dist/windows-binaries* ls -R dist/