Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .codex-release/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*
!.gitignore
!README.md
!release.env.example
19 changes: 19 additions & 0 deletions .codex-release/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Local Release Secrets

This directory is for local macOS arm64 release material. It is intentionally
ignored by git except for this README, `.gitignore`, and `release.env.example`.

Typical local files:

- `developer-id.p12`
- `AuthKey_XXXX.p8`
- `release.env`
- ad hoc release notes or handoff scratch files

The release runner automatically reads `.codex-release/release.env` when it
exists. Keep real certificate passwords, keys, and local config in ignored files
under this directory.

Notarization submission state is written under the selected release output
directory, not here, so no-publish runs can be resumed with the same
`--output-dir`.
13 changes: 13 additions & 0 deletions .codex-release/release.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Copy to .codex-release/release.env and fill local values.
CODEX_RELEASE_REMOTE=origin
CODEX_RELEASE_BRANCH=o3/main
GITHUB_REPOSITORY=o3dotdev/o3-codex

APPLE_CERTIFICATE_P12_PATH=.codex-release/developer-id.p12
APPLE_CERTIFICATE_PASSWORD=
APPLE_NOTARIZATION_KEY_P8_PATH=.codex-release/AuthKey_XXXX.p8
APPLE_NOTARIZATION_KEY_ID=
APPLE_NOTARIZATION_ISSUER_ID=
APPLE_NOTARIZATION_TIMEOUT_SECONDS=1800
APPLE_NOTARIZATION_POLL_INTERVAL_SECONDS=30
APPLE_NOTARIZATION_SUBMIT_ATTEMPTS=3
10 changes: 9 additions & 1 deletion .github/actions/run-argument-comment-lint/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,15 @@ inputs:
runs:
using: composite
steps:
- name: Skip macOS Bazel lint without BuildBuddy
if: ${{ runner.os == 'macOS' && inputs.buildbuddy-api-key == '' }}
shell: bash
run: |
echo "Skipping argument-comment-lint Bazel job because BUILDBUDDY_API_KEY is not configured."
echo "macOS Bazel CI depends on BuildBuddy/cache for the pinned Apple SDK package; direct Apple downloads can return 403 on hosted runners."

- uses: ./.github/actions/setup-bazel-ci
if: ${{ runner.os != 'macOS' || inputs.buildbuddy-api-key != '' }}
with:
target: ${{ inputs.target }}
install-test-prereqs: true
Expand All @@ -26,7 +34,7 @@ runs:
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends pkg-config libcap-dev

- name: Run argument comment lint on codex-rs via Bazel
if: ${{ runner.os != 'Windows' }}
if: ${{ runner.os != 'Windows' && (runner.os != 'macOS' || inputs.buildbuddy-api-key != '') }}
env:
BUILDBUDDY_API_KEY: ${{ inputs.buildbuddy-api-key }}
shell: bash
Expand Down
13 changes: 11 additions & 2 deletions .github/scripts/run-bazel-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,21 @@ else
# --noexperimental_remote_repo_contents_cache:
# disable remote repo contents cache enabled in .bazelrc startup options.
# https://bazel.build/reference/command-line-reference#startup_options-flag--experimental_remote_repo_contents_cache
# --remote_cache= and --remote_executor=:
# clear remote cache/execution endpoints configured in .bazelrc.
# --bes_backend=, --bes_results_url=, --experimental_remote_downloader=,
# --remote_cache=, and --remote_executor=:
# clear remote BuildBuddy endpoints configured in .bazelrc.
# Bazel requires the remote downloader to be used with gRPC caching, so it
# must be cleared alongside the remote cache in local fallback mode.
# https://bazel.build/reference/command-line-reference#common_options-flag--bes_backend
# https://bazel.build/reference/command-line-reference#common_options-flag--bes_results_url
# https://bazel.build/reference/command-line-reference#common_options-flag--experimental_remote_downloader
# https://bazel.build/reference/command-line-reference#common_options-flag--remote_cache
# https://bazel.build/reference/command-line-reference#common_options-flag--remote_executor
bazel_run_args=(
"${bazel_args[@]}"
--bes_backend=
--bes_results_url=
--experimental_remote_downloader=
--remote_cache=
--remote_executor=
)
Expand Down
57 changes: 41 additions & 16 deletions .github/workflows/bazel.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
target: x86_64-apple-darwin

runs-on: ${{ matrix.os }}
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}

# Configure a human readable name for each job
name: Bazel test on ${{ matrix.os }} for ${{ matrix.target }}
Expand All @@ -40,28 +42,35 @@ jobs:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Skip Bazel macOS without BuildBuddy
if: ${{ env.BUILDBUDDY_API_KEY == '' && runner.os == 'macOS' }}
shell: bash
run: |
echo "Skipping Bazel macOS job because BUILDBUDDY_API_KEY is not configured."
echo "macOS Bazel CI depends on BuildBuddy/cache for the pinned Apple SDK package; direct Apple downloads can return 403 on hosted runners."

- name: Check rusty_v8 MODULE.bazel checksums
if: matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu'
if: ${{ (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') && matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu' }}
shell: bash
run: |
python3 .github/scripts/rusty_v8_bazel.py check-module-bazel
python3 -m unittest discover -s .github/scripts -p test_rusty_v8_bazel.py

- name: Prepare Bazel CI
id: prepare_bazel
if: ${{ env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS' }}
uses: ./.github/actions/prepare-bazel-ci
with:
target: ${{ matrix.target }}
cache-scope: bazel-${{ github.job }}
install-test-prereqs: "true"
- name: Check MODULE.bazel.lock is up to date
if: matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu'
if: ${{ (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') && matrix.os == 'ubuntu-24.04' && matrix.target == 'x86_64-unknown-linux-gnu' }}
shell: bash
run: ./scripts/check-module-bazel-lock.sh

- name: bazel test //...
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
if: ${{ env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS' }}
shell: bash
run: |
bazel_targets=(
Expand Down Expand Up @@ -95,7 +104,7 @@ jobs:
"${bazel_targets[@]}"

- name: Upload Bazel execution logs
if: always() && !cancelled()
if: ${{ always() && !cancelled() && (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') }}
continue-on-error: true
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
Expand All @@ -106,7 +115,7 @@ jobs:
# Save the job-scoped Bazel repository cache after cache misses. Keep the
# upload non-fatal so cache service issues never fail the job itself.
- name: Save bazel repository cache
if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true'
if: ${{ always() && !cancelled() && (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') && steps.prepare_bazel.outputs.repository-cache-hit != 'true' }}
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
Expand Down Expand Up @@ -311,6 +320,8 @@ jobs:
- os: macos-15
target: aarch64-apple-darwin
runs-on: ${{ matrix.os }}
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
name: Bazel clippy on ${{ matrix.os }} for ${{ matrix.target }}

steps:
Expand All @@ -319,16 +330,23 @@ jobs:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Skip Bazel macOS without BuildBuddy
if: ${{ env.BUILDBUDDY_API_KEY == '' && runner.os == 'macOS' }}
shell: bash
run: |
echo "Skipping Bazel macOS job because BUILDBUDDY_API_KEY is not configured."
echo "macOS Bazel CI depends on BuildBuddy/cache for the pinned Apple SDK package; direct Apple downloads can return 403 on hosted runners."

- name: Prepare Bazel CI
id: prepare_bazel
if: ${{ env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS' }}
uses: ./.github/actions/prepare-bazel-ci
with:
target: ${{ matrix.target }}
cache-scope: bazel-${{ github.job }}

- name: bazel build --config=clippy lint targets
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
if: ${{ env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS' }}
shell: bash
run: |
bazel_clippy_args=(
Expand Down Expand Up @@ -370,7 +388,7 @@ jobs:
"${bazel_targets[@]}"

- name: Upload Bazel execution logs
if: always() && !cancelled()
if: ${{ always() && !cancelled() && (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') }}
continue-on-error: true
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
Expand All @@ -381,7 +399,7 @@ jobs:
# Save the job-scoped Bazel repository cache after cache misses. Keep the
# upload non-fatal so cache service issues never fail the job itself.
- name: Save bazel repository cache
if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true'
if: ${{ always() && !cancelled() && (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') && steps.prepare_bazel.outputs.repository-cache-hit != 'true' }}
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
Expand All @@ -397,6 +415,8 @@ jobs:
- os: macos-15
target: aarch64-apple-darwin
runs-on: ${{ matrix.os }}
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
name: Verify release build on ${{ matrix.os }} for ${{ matrix.target }}

steps:
Expand All @@ -405,16 +425,23 @@ jobs:
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
persist-credentials: false

- name: Skip Bazel macOS without BuildBuddy
if: ${{ env.BUILDBUDDY_API_KEY == '' && runner.os == 'macOS' }}
shell: bash
run: |
echo "Skipping Bazel macOS job because BUILDBUDDY_API_KEY is not configured."
echo "macOS Bazel CI depends on BuildBuddy/cache for the pinned Apple SDK package; direct Apple downloads can return 403 on hosted runners."

- name: Prepare Bazel CI
id: prepare_bazel
if: ${{ env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS' }}
uses: ./.github/actions/prepare-bazel-ci
with:
target: ${{ matrix.target }}
cache-scope: bazel-${{ github.job }}

- name: bazel build verify-release-build targets
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
if: ${{ env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS' }}
shell: bash
run: |
# This job exists to compile Rust code behind
Expand Down Expand Up @@ -458,8 +485,6 @@ jobs:

- name: Verify Bazel builds bwrap
if: runner.os == 'Linux'
env:
BUILDBUDDY_API_KEY: ${{ secrets.BUILDBUDDY_API_KEY }}
shell: bash
run: |
./.github/scripts/run-bazel-ci.sh \
Expand All @@ -473,7 +498,7 @@ jobs:
//codex-rs/bwrap:bwrap

- name: Upload Bazel execution logs
if: always() && !cancelled()
if: ${{ always() && !cancelled() && (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') }}
continue-on-error: true
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
Expand All @@ -484,7 +509,7 @@ jobs:
# Save the job-scoped Bazel repository cache after cache misses. Keep the
# upload non-fatal so cache service issues never fail the job itself.
- name: Save bazel repository cache
if: always() && !cancelled() && steps.prepare_bazel.outputs.repository-cache-hit != 'true'
if: ${{ always() && !cancelled() && (env.BUILDBUDDY_API_KEY != '' || runner.os != 'macOS') && steps.prepare_bazel.outputs.repository-cache-hit != 'true' }}
continue-on-error: true
uses: actions/cache/save@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/rust-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ jobs:
HEAD_SHA='${{ github.event.pull_request.head.sha }}'
echo "Base SHA: $BASE_SHA"
echo "Head SHA: $HEAD_SHA"
mapfile -t files < <(git diff --name-only --no-renames "$BASE_SHA" "$HEAD_SHA")
files=()
while IFS= read -r file; do
files+=("$file")
done < <(git diff --name-only --no-renames "$BASE_SHA" "$HEAD_SHA")
else
# On manual runs, default to the full fast-PR bundle.
files=("codex-rs/force" "tools/argument-comment-lint/force" ".github/force")
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/rust-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
# with `unsigned_run_id` and `signed_macos_asset`. The signed handoff archive
# should contain target or artifact directories such as `aarch64-apple-darwin/`
# with signed binaries.
#
# Local macOS arm64-only releases use `scripts/release_macos_arm64.py` instead
# of this workflow. That path publishes GitHub Release assets only and
# intentionally skips npm, PyPI, DotSlash, WinGet, Linux, Windows, Intel macOS,
# and desktop `Codex.app` packaging.

name: rust-release
on:
Expand Down
46 changes: 46 additions & 0 deletions scripts/release_macos_arm64.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Local macOS arm64 Release

This maintainer-only flow builds and publishes GitHub Release assets for the
M-series Mac CLI release path. It intentionally does not publish npm, PyPI,
DotSlash, WinGet, Linux, Windows, Intel macOS, or the desktop `Codex.app`.

Run from a clean worktree on an M-series Mac:

```bash
python3 scripts/release_macos_arm64.py \
--version 0.134.0-o3.5 \
--certificate-p12 /secure/path/developer-id.p12 \
--certificate-password "$APPLE_CERTIFICATE_PASSWORD" \
--notary-key-p8 /secure/path/AuthKey_XXXX.p8 \
--notary-key-id XXXX \
--notary-issuer-id YYYY \
--create-github-release
```

The default release target is `origin` / `o3dotdev/o3-codex` from the `o3/main`
branch. Real releases fail if run from a different branch unless
`--allow-non-release-branch` is passed.

Omit `--create-github-release` for a local no-publish run. No GitHub Release is
created, no local tag is created, and no version-bump commit is made in that
mode; assets stay under the output directory for inspection.

Secrets can also come from `APPLE_CERTIFICATE_P12_PATH`,
`APPLE_CERTIFICATE_PASSWORD`, `APPLE_NOTARIZATION_KEY_P8_PATH`,
`APPLE_NOTARIZATION_KEY_ID`, and `APPLE_NOTARIZATION_ISSUER_ID`.
The script also reads `.codex-release/release.env` when it exists; use
`.codex-release/release.env.example` as the local template. Real certs, keys,
and local config files under `.codex-release/` are ignored by git.

The runner submits signed binaries to Apple's notarization service without
`--wait`, stores submission IDs in `<output>/notary/submissions.json`, then
polls with a bounded timeout. If Apple remains `In Progress`, rerun the same
command with the same `--output-dir`; the saved notarization zip is restored
back into `target/`, so the exact submitted binary is packaged after Apple
accepts it. Resume runs reuse submission IDs instead of re-signing or
re-uploading. Tune polling with `--notary-timeout-seconds`,
`--notary-poll-interval-seconds`, and `--notary-submit-attempts`.

Use `--dry-run` to validate inputs and print the planned commands without
signing, tagging, or uploading. Release outputs are written under
`dist/local-macos-arm64-release/<version>/`, which is ignored by git.
Loading
Loading