From 0b9cb2b9e767cba706c30381d9890a139b7d54d9 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 7 Jul 2025 23:51:34 -0700 Subject: [PATCH] chore: create a release script for the Rust CLI (#1479) This is a stopgap solution before migrating the build for the npm release to GitHub Actions (which is ultimately what should be done to ensure hermetic builds). The idea is that instead of continuing to create PRs like https://github.com/openai/codex/pull/1472 where I have to check in a change to the `WORKFLOW_URL`, this script uses `gh run list` to get the `WORKFLOW_URL` dynamically and then threads the value through to `install_native_deps.sh`. To create the 0.3.0 release on npm, I ran: ```shell ./codex-cli/scripts/stage_rust_release.py --release-version 0.3.0 ``` and then did `npm publish --dry-run` followed by `npm publish` in the temp directory created by `stage_rust_release.py`. --- codex-cli/.gitignore | 4 ++ codex-cli/scripts/install_native_deps.sh | 33 ++++++++----- codex-cli/scripts/stage_release.sh | 7 ++- codex-cli/scripts/stage_rust_release.py | 62 ++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 14 deletions(-) create mode 100755 codex-cli/scripts/stage_rust_release.py diff --git a/codex-cli/.gitignore b/codex-cli/.gitignore index 49a5628d..f886e64f 100644 --- a/codex-cli/.gitignore +++ b/codex-cli/.gitignore @@ -1,3 +1,7 @@ # Added by ./scripts/install_native_deps.sh +/bin/codex-aarch64-apple-darwin +/bin/codex-aarch64-unknown-linux-musl /bin/codex-linux-sandbox-arm64 /bin/codex-linux-sandbox-x64 +/bin/codex-x86_64-apple-darwin +/bin/codex-x86_64-unknown-linux-musl diff --git a/codex-cli/scripts/install_native_deps.sh b/codex-cli/scripts/install_native_deps.sh index 5286ac48..353ffafd 100755 --- a/codex-cli/scripts/install_native_deps.sh +++ b/codex-cli/scripts/install_native_deps.sh @@ -8,7 +8,7 @@ # the native implementation when users set CODEX_RUST=1. # # Usage -# install_native_deps.sh [RELEASE_ROOT] [--full-native] +# install_native_deps.sh [--full-native] [--workflow-url URL] [CODEX_CLI_ROOT] # # The optional RELEASE_ROOT is the path that contains package.json. Omitting # it installs the binaries into the repository's own bin/ folder to support @@ -20,32 +20,43 @@ set -euo pipefail # Parse arguments # ------------------ -DEST_DIR="" +CODEX_CLI_ROOT="" INCLUDE_RUST=0 -for arg in "$@"; do - case "$arg" in +# Until we start publishing stable GitHub releases, we have to grab the binaries +# from the GitHub Action that created them. Update the URL below to point to the +# appropriate workflow run: +WORKFLOW_URL="https://github.com/openai/codex/actions/runs/15981617627" + +while [[ $# -gt 0 ]]; do + case "$1" in --full-native) INCLUDE_RUST=1 ;; + --workflow-url) + shift || { echo "--workflow-url requires an argument"; exit 1; } + if [ -n "$1" ]; then + WORKFLOW_URL="$1" + fi + ;; *) - if [[ -z "$DEST_DIR" ]]; then - DEST_DIR="$arg" + if [[ -z "$CODEX_CLI_ROOT" ]]; then + CODEX_CLI_ROOT="$1" else - echo "Unexpected argument: $arg" >&2 + echo "Unexpected argument: $1" >&2 exit 1 fi ;; esac + shift done # ---------------------------------------------------------------------------- # Determine where the binaries should be installed. # ---------------------------------------------------------------------------- -if [[ $# -gt 0 ]]; then +if [ -n "$CODEX_CLI_ROOT" ]; then # The caller supplied a release root directory. - CODEX_CLI_ROOT="$1" BIN_DIR="$CODEX_CLI_ROOT/bin" else # No argument; fall back to the repo’s own bin directory. @@ -62,10 +73,6 @@ mkdir -p "$BIN_DIR" # Download and decompress the artifacts from the GitHub Actions workflow. # ---------------------------------------------------------------------------- -# Until we start publishing stable GitHub releases, we have to grab the binaries -# from the GitHub Action that created them. Update the URL below to point to the -# appropriate workflow run: -WORKFLOW_URL="https://github.com/openai/codex/actions/runs/15981617627" WORKFLOW_ID="${WORKFLOW_URL##*/}" ARTIFACTS_DIR="$(mktemp -d)" diff --git a/codex-cli/scripts/stage_release.sh b/codex-cli/scripts/stage_release.sh index 2fc59d3a..29b9f767 100755 --- a/codex-cli/scripts/stage_release.sh +++ b/codex-cli/scripts/stage_release.sh @@ -48,6 +48,7 @@ TMPDIR="" INCLUDE_NATIVE=0 # Default to a timestamp-based version (keep same scheme as before) VERSION="$(printf '0.1.%d' "$(date +%y%m%d%H%M)")" +WORKFLOW_URL="" # Manual flag parser - Bash getopts does not handle GNU long options well. while [[ $# -gt 0 ]]; do @@ -66,6 +67,10 @@ while [[ $# -gt 0 ]]; do shift || { echo "--version requires an argument"; usage 1; } VERSION="$1" ;; + --workflow-url) + shift || { echo "--workflow-url requires an argument"; exit 1; } + WORKFLOW_URL="$1" + ;; -h|--help) usage 0 ;; @@ -125,7 +130,7 @@ jq --arg version "$VERSION" \ # 2. Native runtime deps (sandbox plus optional Rust binaries) if [[ "$INCLUDE_NATIVE" -eq 1 ]]; then - ./scripts/install_native_deps.sh "$TMPDIR" --full-native + ./scripts/install_native_deps.sh --full-native --workflow-url "$WORKFLOW_URL" "$TMPDIR" touch "${TMPDIR}/bin/use-native" else ./scripts/install_native_deps.sh "$TMPDIR" diff --git a/codex-cli/scripts/stage_rust_release.py b/codex-cli/scripts/stage_rust_release.py new file mode 100755 index 00000000..6d1326af --- /dev/null +++ b/codex-cli/scripts/stage_rust_release.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +import json +import subprocess +import sys +import argparse +from pathlib import Path + + +def main() -> int: + parser = argparse.ArgumentParser( + description="""Stage a release for the npm module. + +Run this after the GitHub Release has been created and use +`--release-version` to specify the version to release. +""" + ) + parser.add_argument( + "--release-version", required=True, help="Version to release, e.g., 0.3.0" + ) + args = parser.parse_args() + version = args.release_version + + gh_run = subprocess.run( + [ + "gh", + "run", + "list", + "--branch", + f"rust-v{version}", + "--json", + "workflowName,url,headSha", + "--jq", + 'first(.[] | select(.workflowName == "rust-release"))', + ], + stdout=subprocess.PIPE, + check=True, + ) + gh_run.check_returncode() + workflow = json.loads(gh_run.stdout) + sha = workflow["headSha"] + + print(f"should `git checkout {sha}`") + + current_dir = Path(__file__).parent.resolve() + stage_release = subprocess.run( + [ + current_dir / "stage_release.sh", + "--version", + version, + "--workflow-url", + workflow["url"], + "--native", + ] + ) + stage_release.check_returncode() + + return 0 + + +if __name__ == "__main__": + sys.exit(main())