# Release workflow for codex-rs. # To release, follow a workflow like: # ``` # git tag -a rust-v0.1.0 -m "Release 0.1.0" # git push origin rust-v0.1.0 # ``` name: rust-release on: push: tags: - "rust-v*.*.*" concurrency: group: ${{ github.workflow }} cancel-in-progress: true jobs: tag-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Validate tag matches Cargo.toml version shell: bash run: | set -euo pipefail echo "::group::Tag validation" # 1. Must be a tag and match the regex [[ "${GITHUB_REF_TYPE}" == "tag" ]] \ || { echo "❌ Not a tag push"; exit 1; } [[ "${GITHUB_REF_NAME}" =~ ^rust-v[0-9]+\.[0-9]+\.[0-9]+(-(alpha|beta)(\.[0-9]+)?)?$ ]] \ || { echo "❌ Tag '${GITHUB_REF_NAME}' doesn't match expected format"; exit 1; } # 2. Extract versions tag_ver="${GITHUB_REF_NAME#rust-v}" cargo_ver="$(grep -m1 '^version' codex-rs/Cargo.toml \ | sed -E 's/version *= *"([^"]+)".*/\1/')" # 3. Compare [[ "${tag_ver}" == "${cargo_ver}" ]] \ || { echo "❌ Tag ${tag_ver} ≠ Cargo.toml ${cargo_ver}"; exit 1; } echo "✅ Tag and Cargo.toml agree (${tag_ver})" echo "::endgroup::" build: needs: tag-check name: ${{ matrix.runner }} - ${{ matrix.target }} runs-on: ${{ matrix.runner }} timeout-minutes: 30 defaults: run: working-directory: codex-rs strategy: fail-fast: false matrix: include: - runner: macos-14 target: aarch64-apple-darwin - runner: macos-14 target: x86_64-apple-darwin - runner: ubuntu-24.04 target: x86_64-unknown-linux-musl - runner: ubuntu-24.04 target: x86_64-unknown-linux-gnu - runner: ubuntu-24.04-arm target: aarch64-unknown-linux-musl - runner: ubuntu-24.04-arm target: aarch64-unknown-linux-gnu steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@1.88 with: targets: ${{ matrix.target }} - uses: actions/cache@v4 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ ${{ github.workspace }}/codex-rs/target/ key: cargo-release-${{ matrix.runner }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }} - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'aarch64-unknown-linux-musl'}} name: Install musl build tools run: | sudo apt install -y musl-tools pkg-config - name: Cargo build run: cargo build --target ${{ matrix.target }} --release --bin codex --bin codex-exec --bin codex-linux-sandbox - name: Stage artifacts shell: bash run: | dest="dist/${{ matrix.target }}" mkdir -p "$dest" cp target/${{ matrix.target }}/release/codex-exec "$dest/codex-exec-${{ matrix.target }}" cp target/${{ matrix.target }}/release/codex "$dest/codex-${{ matrix.target }}" # After https://github.com/openai/codex/pull/1228 is merged and a new # release is cut with an artifacts built after that PR, the `-gnu` # variants can go away as we will only use the `-musl` variants. - if: ${{ matrix.target == 'x86_64-unknown-linux-musl' || matrix.target == 'x86_64-unknown-linux-gnu' || matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'aarch64-unknown-linux-musl' }} name: Stage Linux-only artifacts shell: bash run: | dest="dist/${{ matrix.target }}" cp target/${{ matrix.target }}/release/codex-linux-sandbox "$dest/codex-linux-sandbox-${{ matrix.target }}" - name: Compress artifacts shell: bash run: | # Path that contains the uncompressed binaries for the current # ${{ matrix.target }} dest="dist/${{ matrix.target }}" # For compatibility with environments that lack the `zstd` tool we # additionally create a `.tar.gz` alongside every single binary that # we publish. The end result is: # codex-.zst (existing) # codex-.tar.gz (new) # ...same naming for codex-exec-* and codex-linux-sandbox-* # 1. Produce a .tar.gz for every file in the directory *before* we # run `zstd --rm`, because that flag deletes the original files. for f in "$dest"/*; do base="$(basename "$f")" # Skip files that are already archives (shouldn't happen, but be # safe). if [[ "$base" == *.tar.gz ]]; then continue fi # Create per-binary tar.gz tar -C "$dest" -czf "$dest/${base}.tar.gz" "$base" # Also create .zst (existing behaviour) *and* remove the original # uncompressed binary to keep the directory small. zstd -T0 -19 --rm "$dest/$base" done - uses: actions/upload-artifact@v4 with: name: ${{ matrix.target }} # Upload the per-binary .zst files as well as the new .tar.gz # equivalents we generated in the previous step. path: | codex-rs/dist/${{ matrix.target }}/* release: needs: build name: release runs-on: ubuntu-latest steps: - uses: actions/download-artifact@v4 with: path: dist - name: List run: ls -R dist/ - name: Define release name id: release_name run: | # Extract the version from the tag name, which is in the format # "rust-v0.1.0". version="${GITHUB_REF_NAME#rust-v}" echo "name=${version}" >> $GITHUB_OUTPUT - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: name: ${{ steps.release_name.outputs.name }} tag_name: ${{ github.ref_name }} files: dist/** # For now, tag releases as "prerelease" because we are not claiming # the Rust CLI is stable yet. prerelease: true - uses: facebook/dotslash-publish-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag: ${{ github.ref_name }} config: .github/dotslash-config.json