Files
codex/.github/workflows/rust-release-windows.yml
Benjamin Carlsson ce7fbb373b Bundle native voice runtimes in Windows releases (#44922)
## Why

Windows release packages need the voice helper and native audio libraries. Realtime TLS connections on fresh Windows installations also need platform certificate validation so Windows can retrieve missing trusted roots on demand.

## What changed

- Build and sign the voice helper and audio DLLs for Windows x64 and ARM64, bundle a pinned Microsoft CRT DLL, and verify signatures and runtime receipts before packaging.
- Add verified, pinned Cygwin and native build tools plus MSVC linker, compiler, and path handling fixes for the Windows Bazel builds.
- Include voice resources in primary release archives and WinGet packages. Preserve WinGet executable names, update manifest hashes, and recognize the package root through matching entrypoint metadata. Keep Python runtime wheels voice-free to preserve their existing Windows support floor.
- Use Windows platform TLS validation for realtime WebSockets when no custom CA bundle is configured, preserving custom CA behavior.

## Testing

Add coverage for build-input integrity and unsafe paths, signed Windows runtime assembly, WinGet file and hash preservation, package discovery, and TLS trust selection, untrusted certificate rejection, and hostname validation.

GitOrigin-RevId: 423da35872fa5549d69fd4ca97d922bb49599386
2026-09-11 21:59:24 +00:00

627 lines
28 KiB
YAML

name: rust-release-windows
on:
workflow_call:
env:
WINDOWS_BINARIES: "codex codex-code-mode-host codex-responses-api-proxy codex-windows-sandbox-setup codex-windows-sandbox-service codex-command-runner codex-app-server"
jobs:
build-windows-binaries:
name: Build Windows binaries - ${{ matrix.runner }} - ${{ matrix.target }} - ${{ matrix.bundle }}
runs-on: ${{ matrix.runs_on }}
# Windows release builds can exceed an hour, so keep the timeout aligned
# with the top-level release build headroom.
timeout-minutes: 90
permissions:
contents: read
defaults:
run:
working-directory: codex-rs
strategy:
fail-fast: false
matrix:
include:
- runner: windows-x64
target: x86_64-pc-windows-msvc
bundle: primary
binaries: "codex codex-code-mode-host codex-responses-api-proxy"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
- runner: windows-arm64
target: aarch64-pc-windows-msvc
bundle: primary
binaries: "codex codex-code-mode-host codex-responses-api-proxy"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-arm64
- runner: windows-x64
target: x86_64-pc-windows-msvc
bundle: helpers
binaries: "codex-windows-sandbox-setup codex-windows-sandbox-service codex-command-runner"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
- runner: windows-arm64
target: aarch64-pc-windows-msvc
bundle: helpers
binaries: "codex-windows-sandbox-setup codex-windows-sandbox-service codex-command-runner"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-arm64
- runner: windows-x64
target: x86_64-pc-windows-msvc
bundle: app-server
binaries: "codex-app-server codex-code-mode-host"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
- runner: windows-arm64
target: aarch64-pc-windows-msvc
bundle: app-server
binaries: "codex-app-server codex-code-mode-host"
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-arm64
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ./.github/actions/setup-ci
- 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@e081816240890017053eacbb1bdf337761dc5582 # 1.95.0
with:
targets: ${{ matrix.target }}
- name: Configure rusty_v8 artifact overrides and verify checksums
uses: ./.github/actions/setup-rusty-v8
with:
target: ${{ matrix.target }}
- name: Configure LLVM linker
uses: ./.github/actions/setup-msvc-env
with:
target: ${{ matrix.target }}
- name: Cargo build (Windows binaries)
shell: bash
run: |
target="${{ matrix.target }}"
if [[ "$target" == "x86_64-pc-windows-msvc" ]]; then
export LIBSQLITE3_FLAGS=SQLITE_DISABLE_INTRINSIC
fi
build_args=()
for binary in ${{ matrix.binaries }}; do
build_args+=(--bin "$binary")
done
STABLE_GIT_COMMIT="$(git rev-parse HEAD)"
export STABLE_GIT_COMMIT
cargo build --target "$target" --release --timings "${build_args[@]}"
- name: Upload Cargo timings
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: cargo-timings-rust-release-windows-${{ matrix.target }}-${{ matrix.bundle }}
path: ${{ env.CARGO_TARGET_DIR }}/**/cargo-timings/cargo-timing.html
if-no-files-found: warn
- name: Stage Windows binaries
shell: bash
run: |
release_dir="$CARGO_TARGET_DIR/${{ matrix.target }}/release"
output_dir="$release_dir/staged-${{ matrix.bundle }}"
mkdir -p "$output_dir"
for binary in ${{ matrix.binaries }}; do
pdb_name="${binary//-/_}"
pdb_path="$release_dir/${pdb_name}.pdb"
if [[ ! -f "$pdb_path" ]]; then
pdb_path="$release_dir/${binary}.pdb"
fi
if [[ ! -f "$pdb_path" ]]; then
echo "PDB for $binary not found at $release_dir/${pdb_name}.pdb or $release_dir/${binary}.pdb" >&2
exit 1
fi
cp "$release_dir/${binary}.exe" "$output_dir/${binary}.exe"
cp "$pdb_path" "$output_dir/${binary}.pdb"
done
- name: Upload Windows binaries
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: windows-binaries-${{ matrix.target }}-${{ matrix.bundle }}
path: |
${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release/staged-${{ matrix.bundle }}/*
build-windows-voice:
name: Build Windows voice - ${{ matrix.target }}
runs-on: ${{ matrix.runs_on }}
timeout-minutes: 120
permissions:
contents: read
strategy:
fail-fast: false
matrix:
include:
- target: x86_64-pc-windows-msvc
arch: x86_64
python_arch: x64
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
- target: aarch64-pc-windows-msvc
arch: aarch64
python_arch: arm64
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-arm64
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
with:
python-version: "3.12"
architecture: ${{ matrix.python_arch }}
- name: Prepare Bazel CI
uses: ./.github/actions/prepare-bazel-ci
with:
target: ${{ matrix.target }}
cache-scope: release-voice-windows
- uses: ./.github/actions/setup-msvc-env
with:
target: ${{ matrix.target }}
- name: Refresh Bazel PATH for the selected MSVC target
shell: pwsh
run: ./.github/scripts/compute-bazel-windows-path.ps1
- name: Download pinned Windows build inputs
shell: pwsh
env:
GH_TOKEN: ${{ github.token }}
run: |
$manifest = Get-Content -Raw .github/scripts/voice-cygwin-snapshot.json | ConvertFrom-Json
$pin = $manifest.archive
$source = $manifest.sourceArchive
$directory = Join-Path $env:RUNNER_TEMP "voice-cygwin-snapshot"
New-Item -ItemType Directory -Path $directory | Out-Null
gh release download $pin.tag --repo "${{ github.repository }}" --pattern $pin.name --pattern $source.name --dir $directory
if ($LASTEXITCODE -ne 0) { throw "Cannot obtain the public Cygwin inputs and source" }
$sourceFile = Join-Path $directory $source.name
if ((Get-Item $sourceFile).Length -ne $source.bytes -or
(Get-FileHash $sourceFile -Algorithm SHA256).Hash.ToLowerInvariant() -ne $source.sha256) {
throw "Public Cygwin source archive mismatch"
}
"VOICE_CYGWIN_ARCHIVE=$(Join-Path $directory $pin.name)" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Verify and install pinned public build tools
shell: pwsh
run: |
& ./.github/scripts/setup-voice-windows.ps1 -Target "${{ matrix.target }}" -SnapshotArchive $env:VOICE_CYGWIN_ARCHIVE
if (-not $env:SystemRoot) { throw "Windows SystemRoot is required for native audio actions" }
"VOICE_WINDOWS_SYSTEM_ROOT=$env:SystemRoot" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
$hostArch = $env:PROCESSOR_ARCHITEW6432
if (-not $hostArch) { $hostArch = $env:PROCESSOR_ARCHITECTURE }
if (-not $hostArch) { throw "Windows host architecture is required" }
"VOICE_WINDOWS_HOST_ARCH=$hostArch" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Build same-commit native runtime and helper
shell: bash
env:
ARCH: ${{ matrix.arch }}
VOICE_ARCH: ${{ matrix.arch }}
# Avoid Java JIT crashes in the Windows ARM64 voice build.
BAZEL_HOST_JVM_ARG: ${{ matrix.arch == 'aarch64' && '-Xint' || '' }}
BUILDBUDDY_API_KEY: ""
run: |
(cd codex-rs && cargo update --workspace)
python .github/scripts/watch_voice_bazel.py "$(cygpath -w "$BASH")" ./.github/scripts/run-bazel-ci.sh \
--remote-download-toplevel \
--print-failed-action-summary \
--windows-msvc-host-platform \
-- \
test -c opt \
--platforms=//:local_windows_msvc \
--extra_toolchains=@local_config_cc//:cc-toolchain-x64_windows,@local_config_cc//:cc-toolchain-arm64_windows,//third_party/voice:windows_pkg_config_toolchain,//third_party/voice:windows_cmake_toolchain \
--repo_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=0 \
--repo_env=BAZEL_MSVC_RUNTIME_VISUAL_STUDIO_EULA=1 \
--inject_repository="voice_windows_tools=$VOICE_WINDOWS_BAZEL_REPOSITORY" \
--//third_party/voice:windows_installed_tools=@voice_windows_tools//:tools \
--action_env="SystemRoot=$VOICE_WINDOWS_SYSTEM_ROOT" \
--host_action_env="SystemRoot=$VOICE_WINDOWS_SYSTEM_ROOT" \
--action_env="PROCESSOR_ARCHITECTURE=$VOICE_WINDOWS_HOST_ARCH" \
--host_action_env="PROCESSOR_ARCHITECTURE=$VOICE_WINDOWS_HOST_ARCH" \
--output_groups=default,receipt,sdk \
--workspace_status_command=./scripts/workspace-status.cmd \
--build_metadata=COMMIT_SHA="$GITHUB_SHA" \
-- \
"//third_party/voice:native_runtime_windows_$ARCH" \
@rules_rust//cargo/private/cargo_build_script_runner:test \
//codex-rs/voice-host:codex-voice-host
- name: Stage verified voice build output
shell: bash
env:
TARGET: ${{ matrix.target }}
ARCH: ${{ matrix.arch }}
run: |
set -euo pipefail
runtime="bazel-bin/third_party/voice/native_runtime_windows_${ARCH}"
PYTHONPATH=third_party/voice python - "$runtime" "$TARGET" <<'PY'
from pathlib import Path
import sys
from package_runtime import runtime_files
runtime_files(Path(sys.argv[1]).resolve(strict=True), sys.argv[2])
PY
output="voice-unsigned/${TARGET}"
mkdir -p "$output"
cp -R "$runtime" "$output/runtime"
cp bazel-bin/codex-rs/voice-host/codex-voice-host.exe "$output/codex-voice-host.exe"
chmod -R u+w "$output"
tar -C "$output" -czf "voice-unsigned-${TARGET}.tar.gz" runtime codex-voice-host.exe
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: voice-${{ matrix.target }}-unsigned
path: voice-unsigned-${{ matrix.target }}.tar.gz
if-no-files-found: error
build-windows-symbols:
needs:
- build-windows-binaries
name: Build Windows symbols - ${{ matrix.target }}
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
defaults:
run:
working-directory: codex-rs
strategy:
fail-fast: false
matrix:
target:
- aarch64-pc-windows-msvc
- x86_64-pc-windows-msvc
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ./.github/actions/setup-ci
- name: Download prebuilt Windows binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
pattern: windows-binaries-${{ matrix.target }}-*
merge-multiple: true
path: ${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release
- name: Build symbols archive
shell: bash
run: |
bash "${GITHUB_WORKSPACE}/.github/scripts/archive-release-symbols-and-strip-binaries.sh" \
--target "${{ matrix.target }}" \
--artifact-name "${{ matrix.target }}" \
--release-dir "${CARGO_TARGET_DIR}/${{ matrix.target }}/release" \
--archive-dir "symbols-dist/${{ matrix.target }}" \
--binaries "${WINDOWS_BINARIES}"
- name: Upload symbols archive
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ matrix.target }}-symbols
path: codex-rs/symbols-dist/${{ matrix.target }}/*
if-no-files-found: error
build-windows:
needs:
- build-windows-binaries
- build-windows-voice
name: Build - ${{ matrix.runner }} - ${{ matrix.target }}
runs-on: ${{ matrix.runs_on }}
environment:
name: azure-artifact-signing
deployment: false
timeout-minutes: 120
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: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
- runner: windows-x64
target: aarch64-pc-windows-msvc
runs_on:
group: ${{ github.event.repository.name }}-runners
labels: ${{ github.event.repository.name }}-windows-x64
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ./.github/actions/setup-ci
- name: Download prebuilt Windows primary binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: windows-binaries-${{ matrix.target }}-primary
path: ${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release
- name: Download prebuilt Windows helper binaries
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: windows-binaries-${{ matrix.target }}-helpers
path: ${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release
- name: Download prebuilt Windows app-server binary
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: windows-binaries-${{ matrix.target }}-app-server
path: ${{ env.CARGO_TARGET_DIR }}/${{ matrix.target }}/release
- name: Verify binaries
shell: bash
run: |
set -euo pipefail
for binary in ${WINDOWS_BINARIES}; do
ls -lh "$CARGO_TARGET_DIR/${{ matrix.target }}/release/${binary}.exe"
done
- name: Sign Windows binaries with Azure Trusted Signing
uses: ./.github/actions/windows-code-sign
with:
target: ${{ matrix.target }}
binaries: ${{ env.WINDOWS_BINARIES }}
client-id: ${{ secrets.AZURE_ARTIFACT_SIGNING_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_ARTIFACT_SIGNING_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_ARTIFACT_SIGNING_SUBSCRIPTION_ID }}
endpoint: ${{ secrets.AZURE_ARTIFACT_SIGNING_ENDPOINT }}
account-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}
- name: Download matching native voice build
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: voice-${{ matrix.target }}-unsigned
path: ${{ runner.temp }}/unsigned-voice
- name: Stage voice files for signing
id: voice_files
shell: bash
run: |
set -euo pipefail
target="${{ matrix.target }}"
unsigned="${RUNNER_TEMP}/unsigned-voice/extracted"
signed="${GITHUB_WORKSPACE}/signed-voice/${target}"
mkdir -p "$unsigned" "$signed"
tar -xzf "$(cygpath -u "${RUNNER_TEMP}/unsigned-voice/voice-unsigned-${target}.tar.gz")" -C "$(cygpath -u "$unsigned")"
python "${GITHUB_WORKSPACE}/third_party/voice/release_runtime.py" stage \
--source "$unsigned/runtime" --target "$target" --output "$signed/runtime"
cp "$unsigned/codex-voice-host.exe" "$signed/codex-voice-host.exe"
chmod -R u+w "$signed"
{
echo 'files<<EOF'
echo "$signed/codex-voice-host.exe"
find "$signed/runtime/bin" -name '*.dll' -type f -print
echo 'EOF'
} >> "$GITHUB_OUTPUT"
- name: Sign Windows voice helper and native DLLs
uses: azure/trusted-signing-action@1d365fec12862c4aa68fcac418143d73f0cea293 # v0.5.11
with:
endpoint: ${{ secrets.AZURE_ARTIFACT_SIGNING_ENDPOINT }}
trusted-signing-account-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_ACCOUNT_NAME }}
certificate-profile-name: ${{ secrets.AZURE_ARTIFACT_SIGNING_CERTIFICATE_PROFILE_NAME }}
exclude-environment-credential: true
exclude-workload-identity-credential: true
exclude-managed-identity-credential: true
exclude-shared-token-cache-credential: true
exclude-visual-studio-credential: true
exclude-visual-studio-code-credential: true
exclude-azure-cli-credential: false
exclude-azure-powershell-credential: true
exclude-azure-developer-cli-credential: true
exclude-interactive-browser-credential: true
cache-dependencies: false
files: ${{ steps.voice_files.outputs.files }}
- uses: ./.github/actions/setup-msvc-env
with:
target: ${{ matrix.target }}
- name: Seal and verify signed voice output
shell: pwsh
run: |
$target = "${{ matrix.target }}"
$signed = Join-Path $env:GITHUB_WORKSPACE "signed-voice/$target"
python (Join-Path $env:GITHUB_WORKSPACE 'third_party/voice/windows_crt.py') --root (Join-Path $signed 'runtime') --target $target --helper (Join-Path $signed 'codex-voice-host.exe')
if ($LASTEXITCODE -ne 0) { throw 'Cannot stage verified Microsoft CRT' }
$files = @(Join-Path $signed "codex-voice-host.exe") + @(
Get-ChildItem (Join-Path $signed "runtime/bin") -Filter *.dll -File |
ForEach-Object FullName
)
if ($files.Count -lt 8) { throw "Voice DLL closure is incomplete" }
foreach ($file in $files) {
$signature = Get-AuthenticodeSignature $file
if ($signature.Status -ne 'Valid') { throw "Unsigned voice file: $file ($($signature.Status))" }
}
python (Join-Path $env:GITHUB_WORKSPACE 'third_party/voice/release_runtime.py') seal --target $target --output (Join-Path $signed 'runtime')
if ($LASTEXITCODE -ne 0) { throw 'Cannot seal signed voice runtime' }
"VOICE_RELEASE_DIR=$signed" | Out-File $env:GITHUB_ENV -Encoding utf8 -Append
- name: Stage artifacts
shell: bash
run: |
dest="dist/${{ matrix.target }}"
mkdir -p "$dest"
for binary in ${WINDOWS_BINARIES}; do
cp "$CARGO_TARGET_DIR/${{ matrix.target }}/release/${binary}.exe" \
"$dest/${binary}-${{ matrix.target }}.exe"
done
- name: Build Codex package archives
shell: bash
run: |
set -euo pipefail
target="${{ matrix.target }}"
archive_script="${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh"
voice_args=(--voice-release-dir "$VOICE_RELEASE_DIR")
voice_args+=(--release-version "${GITHUB_REF_NAME#rust-v}")
bash "$archive_script" \
--target "$target" --bundle primary \
--entrypoint-dir "$CARGO_TARGET_DIR/$target/release" \
--archive-dir "dist/$target" "${voice_args[@]}"
bash "$archive_script" \
--target "$target" --bundle app-server \
--entrypoint-dir "$CARGO_TARGET_DIR/$target/release" \
--archive-dir "dist/$target"
- name: Verify packaged Windows voice closure
shell: pwsh
run: |
$target = "${{ matrix.target }}"
$package = Join-Path $env:RUNNER_TEMP "verify-voice-archive-$target"
New-Item -ItemType Directory -Path $package | Out-Null
tar -xzf "dist/$target/codex-package-$target.tar.gz" -C $package
if ($LASTEXITCODE -ne 0) { throw "Cannot extract the final Windows package" }
$voice = Join-Path $package 'codex-resources/voice'
$helper = Join-Path $voice 'bin/codex-voice-host.exe'
if (-not (Test-Path $helper) -or -not (Test-Path (Join-Path $voice 'bin/gstreamer-1.0-0.dll'))) {
throw 'Signed Windows voice files are missing from the canonical package'
}
$signature = Get-AuthenticodeSignature $helper
if ($signature.Status -ne 'Valid') { throw "Packaged helper signature: $($signature.Status)" }
python -c 'import sys; from pathlib import Path; sys.path.insert(0, sys.argv[3]); from package_runtime import runtime_files; runtime_files(Path(sys.argv[1]), sys.argv[2], public_release=True)' $voice $target (Join-Path $env:GITHUB_WORKSPACE 'third_party/voice')
if ($LASTEXITCODE -ne 0) { throw 'Packaged runtime receipt verification failed' }
- name: Build Python runtime wheel
shell: bash
run: |
set -euo pipefail
case "${{ matrix.target }}" in
aarch64-pc-windows-msvc)
platform_tag="win_arm64"
;;
x86_64-pc-windows-msvc)
platform_tag="win_amd64"
;;
*)
echo "No Python runtime wheel platform tag for ${{ matrix.target }}"
exit 1
;;
esac
python -m venv "${RUNNER_TEMP}/python-runtime-build-venv"
"${RUNNER_TEMP}/python-runtime-build-venv/Scripts/python.exe" -m pip install build
# The wheel's Windows support floor predates the native voice
# runtime's verified device/VC++ prerequisites. Keep it voice-free.
wheel_archives="${RUNNER_TEMP}/voice-free-wheel/${{ matrix.target }}"
bash "${GITHUB_WORKSPACE}/.github/scripts/build-codex-package-archive.sh" \
--target "${{ matrix.target }}" --bundle primary \
--entrypoint-dir "$CARGO_TARGET_DIR/${{ matrix.target }}/release" \
--archive-dir "$wheel_archives"
python - "$wheel_archives/codex-package-${{ matrix.target }}.tar.gz" <<'PY'
import sys
import tarfile
with tarfile.open(sys.argv[1]) as archive:
assert not any("codex-resources/voice/" in member.name for member in archive)
PY
stage_dir="${RUNNER_TEMP}/openai-codex-cli-bin-${{ matrix.target }}"
wheel_dir="${GITHUB_WORKSPACE}/python-runtime-dist/${{ matrix.target }}"
python "${GITHUB_WORKSPACE}/sdk/python/scripts/update_sdk_artifacts.py" \
stage-runtime \
"$stage_dir" \
"$wheel_archives/codex-package-${{ matrix.target }}.tar.gz" \
--codex-version "${GITHUB_REF_NAME}" \
--platform-tag "$platform_tag"
"${RUNNER_TEMP}/python-runtime-build-venv/Scripts/python.exe" -m build --wheel --outdir "$wheel_dir" "$stage_dir"
- name: Upload Python runtime wheel
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: python-runtime-wheel-${{ matrix.target }}
path: python-runtime-dist/${{ matrix.target }}/*.whl
if-no-files-found: error
- name: Compress artifacts
shell: bash
run: |
# Path that contains the uncompressed binaries for the current
# ${{ matrix.target }}
dest="dist/${{ matrix.target }}"
repo_root=$PWD
target="${{ matrix.target }}"
export dest repo_root target
# 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-<target>.zst
# codex-<target>.tar.gz
# codex-<target>.zip
# Variables in the single-quoted script expand in the child shell.
# shellcheck disable=SC2016
printf '%s\0' "$dest"/* |
xargs -0 -n1 -P2 bash -c '
set -euo pipefail
f=$1
base="$(basename "$f")"
# Skip files that are already archives (should not happen, but be
# safe).
if [[ "$base" == *.tar.gz || "$base" == *.tar.zst || "$base" == *.zip || "$base" == *.dmg ]]; then
exit 0
fi
# Do not try to compress signature bundles.
if [[ "$base" == *.sigstore ]]; then
exit 0
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 does not embed the
# directory path inside the zip.
if [[ "$base" == "codex-${target}.exe" ]]; then
# Preserve WinGet executable paths while including the signed
# runtime, resources, and package metadata from the canonical tar.
bundle_dir="$(mktemp -d)"
tar -xzf "$dest/codex-package-${target}.tar.gz" -C "$bundle_dir"
python "${GITHUB_WORKSPACE}/scripts/build_winget_package.py" "$(cygpath -w "$bundle_dir")"
(cd "$bundle_dir" && 7z a "$repo_root/$dest/${base}.zip" .)
rm -rf "$bundle_dir"
else
(cd "$dest" && 7z a "${base}.zip" "$base")
fi
# Keep raw executables and produce .zst alongside them.
"${GITHUB_WORKSPACE}/.github/workflows/zstd" -T0 -19 "$dest/$base"
' _
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: ${{ matrix.target }}
path: |
codex-rs/dist/${{ matrix.target }}/*