This updates `rust-release.yml` so that the last step of creating a release entails updating the `latest-alpha-cli` branch to point to the tag used to create the latest release. This will facilitate building automation to identify the most recent alpha release of Codex CLI (though note this branch could also point to an official release, as it is implemented today). This introduces a new job, `update-branch`, which depends on the `release` job. I made it separate from the `release` job because `update-branch` needs the `contents: write` permission, so this limits the amount of work we do with that permission. Note I also created a branch protection rule for `latest-alpha-cli` that: - specifies repository admins as the only members of the bypass list - only those with bypass permissions can create, update, or delete this branch - this branch requires a linear history - note that force pushes _are_ allowed This is the first step in fixing https://github.com/openai/codex/issues/3098.
241 lines
7.8 KiB
YAML
241 lines
7.8 KiB
YAML
# 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@v5
|
|
|
|
- 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
|
|
- runner: windows-latest
|
|
target: x86_64-pc-windows-msvc
|
|
- runner: windows-11-arm
|
|
target: aarch64-pc-windows-msvc
|
|
|
|
steps:
|
|
- uses: actions/checkout@v5
|
|
- uses: dtolnay/rust-toolchain@1.89
|
|
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-${{ matrix.runner }}-${{ matrix.target }}-release-${{ 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
|
|
|
|
- name: Stage artifacts
|
|
shell: bash
|
|
run: |
|
|
dest="dist/${{ matrix.target }}"
|
|
mkdir -p "$dest"
|
|
|
|
if [[ "${{ matrix.runner }}" == windows* ]]; then
|
|
cp target/${{ matrix.target }}/release/codex.exe "$dest/codex-${{ matrix.target }}.exe"
|
|
else
|
|
cp target/${{ matrix.target }}/release/codex "$dest/codex-${{ matrix.target }}"
|
|
fi
|
|
|
|
- if: ${{ matrix.runner == 'windows-11-arm' }}
|
|
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 }}"
|
|
|
|
# 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:
|
|
# codex-<target>.zst (existing)
|
|
# codex-<target>.tar.gz (new)
|
|
# codex-<target>.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.
|
|
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 ]]; 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 [[ "${{ matrix.runner }}" == windows* ]]; then
|
|
(cd "$dest" && 7z a "${base}.zip" "$base")
|
|
fi
|
|
|
|
# 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:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v5
|
|
|
|
- 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: Stage npm package
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
set -euo pipefail
|
|
TMP_DIR="${RUNNER_TEMP}/npm-stage"
|
|
python3 codex-cli/scripts/stage_rust_release.py \
|
|
--release-version "${{ steps.release_name.outputs.name }}" \
|
|
--tmp "${TMP_DIR}"
|
|
mkdir -p dist/npm
|
|
# Produce an npm-ready tarball using `npm pack` and store it in dist/npm.
|
|
# We then rename it to a stable name used by our publishing script.
|
|
(cd "$TMP_DIR" && npm pack --pack-destination "${GITHUB_WORKSPACE}/dist/npm")
|
|
mv "${GITHUB_WORKSPACE}"/dist/npm/*.tgz \
|
|
"${GITHUB_WORKSPACE}/dist/npm/codex-npm-${{ steps.release_name.outputs.name }}.tgz"
|
|
|
|
- name: Create GitHub Release
|
|
uses: softprops/action-gh-release@v2
|
|
with:
|
|
name: ${{ steps.release_name.outputs.name }}
|
|
tag_name: ${{ github.ref_name }}
|
|
files: dist/**
|
|
# Mark as prerelease only when the version has a suffix after x.y.z
|
|
# (e.g. -alpha, -beta). Otherwise publish a normal release.
|
|
prerelease: ${{ contains(steps.release_name.outputs.name, '-') }}
|
|
|
|
- uses: facebook/dotslash-publish-release@v2
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
with:
|
|
tag: ${{ github.ref_name }}
|
|
config: .github/dotslash-config.json
|
|
|
|
update-branch:
|
|
name: Update latest-alpha-cli branch
|
|
permissions:
|
|
contents: write
|
|
needs: release
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- name: Update latest-alpha-cli branch
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
set -euo pipefail
|
|
gh api \
|
|
repos/${GITHUB_REPOSITORY}/git/refs/heads/latest-alpha-cli \
|
|
-X PATCH \
|
|
-f sha="${GITHUB_SHA}" \
|
|
-f force=true
|