diff --git a/.github/actions/macos-code-sign/action.yml b/.github/actions/macos-code-sign/action.yml
index 0e19fa11d0a3..da5a48d6c178 100644
--- a/.github/actions/macos-code-sign/action.yml
+++ b/.github/actions/macos-code-sign/action.yml
@@ -7,6 +7,10 @@ inputs:
binaries:
description: Space-delimited binary basenames to sign and notarize.
default: "codex codex-responses-api-proxy"
+ codesign-identifier:
+ description: Optional explicit code signing identifier to embed in signed binaries.
+ required: false
+ default: ""
sign-binaries:
description: Whether to sign and notarize the macOS binaries.
required: false
@@ -56,6 +60,7 @@ runs:
echo "$APPLE_CERTIFICATE" | base64 -d > "$cert_path"
keychain_path="${RUNNER_TEMP}/codex-signing.keychain-db"
+ echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV"
security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
security set-keychain-settings -lut 21600 "$keychain_path"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
@@ -114,7 +119,6 @@ runs:
rm -f "$cert_path"
echo "APPLE_CODESIGN_IDENTITY=$APPLE_CODESIGN_IDENTITY" >> "$GITHUB_ENV"
- echo "APPLE_CODESIGN_KEYCHAIN=$keychain_path" >> "$GITHUB_ENV"
echo "::add-mask::$APPLE_CODESIGN_IDENTITY"
- name: Sign macOS binaries
@@ -123,6 +127,7 @@ runs:
env:
TARGET: ${{ inputs.target }}
BINARIES: ${{ inputs.binaries }}
+ CODESIGN_IDENTIFIER: ${{ inputs.codesign-identifier }}
run: |
set -euo pipefail
@@ -137,10 +142,14 @@ runs:
fi
entitlements_path="$GITHUB_ACTION_PATH/codex.entitlements.plist"
+ identifier_args=()
+ if [[ -n "${CODESIGN_IDENTIFIER:-}" ]]; then
+ identifier_args+=(--identifier "$CODESIGN_IDENTIFIER")
+ fi
for binary in ${BINARIES}; do
path="codex-rs/target/${TARGET}/release/${binary}"
- codesign --force --options runtime --timestamp --entitlements "$entitlements_path" --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path"
+ codesign --force --options runtime --timestamp --entitlements "$entitlements_path" --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "${identifier_args[@]}" "$path"
done
- name: Notarize macOS binaries
@@ -238,8 +247,6 @@ runs:
- name: Remove signing keychain
if: ${{ always() }}
shell: bash
- env:
- APPLE_CODESIGN_KEYCHAIN: ${{ env.APPLE_CODESIGN_KEYCHAIN }}
run: |
set -euo pipefail
if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" ]]; then
diff --git a/.github/workflows/fork-release.yml b/.github/workflows/fork-release.yml
index a77792f3a025..6028cac0ce9f 100644
--- a/.github/workflows/fork-release.yml
+++ b/.github/workflows/fork-release.yml
@@ -11,6 +11,7 @@ concurrency:
jobs:
compute-release:
+ if: ${{ github.ref_name == 'stable' }}
runs-on: ubuntu-latest
outputs:
base_version: ${{ steps.compute.outputs.base_version }}
@@ -33,6 +34,7 @@ jobs:
python3 scripts/compute_fork_release.py --write-github-output
build:
+ if: ${{ github.ref_name == 'stable' }}
needs: compute-release
name: Build - ${{ matrix.target }}
runs-on: macos-15
@@ -194,6 +196,20 @@ jobs:
log_progress "[60%] Cargo build completed in ${duration_min}m (${duration_sec}s)"
append_summary "- [60%] Cargo build completed in ${duration_min}m (${duration_sec}s)"
+ - name: "[62%] Sign and notarize release binary"
+ uses: ./.github/actions/macos-code-sign
+ with:
+ target: ${{ matrix.target }}
+ binaries: codex
+ codesign-identifier: com.rickgetz.codex
+ sign-binaries: "true"
+ sign-dmg: "false"
+ apple-certificate: ${{ secrets.APPLE_CERTIFICATE_P12 }}
+ apple-certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
+ apple-notarization-key-p8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }}
+ apple-notarization-key-id: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
+ apple-notarization-issuer-id: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
+
- name: "[65%] Stage artifacts"
shell: bash
run: |
@@ -211,6 +227,7 @@ jobs:
path: dist/${{ matrix.target }}/*
release:
+ if: ${{ github.ref_name == 'stable' }}
needs:
- compute-release
- build
@@ -303,6 +320,7 @@ jobs:
dist/**/*.zst
publish-npm:
+ if: ${{ github.ref_name == 'stable' }}
needs: release
runs-on: ubuntu-latest
permissions:
diff --git a/docs/fork-release.md b/docs/fork-release.md
index 717a844c2d6d..b3842e8a1b98 100644
--- a/docs/fork-release.md
+++ b/docs/fork-release.md
@@ -45,6 +45,28 @@ fails if they contain secret-like paths such as `.npmrc`, `.env*`, `.ssh/`,
`.aws/`, or key/certificate files. The publish step also redacts auth-shaped
output before writing logs.
+## One-time Apple signing setup
+
+The fork release workflow signs and notarizes the Apple Silicon `codex` binary
+before it is packaged for npm. The signed binary uses the code signing identifier
+`com.rickgetz.codex`, which gives macOS Keychain a stable code identity across
+npm upgrades.
+
+Configure these GitHub Actions secrets before relying on the release workflow:
+
+- `APPLE_CERTIFICATE_P12`: base64-encoded Developer ID Application certificate
+ exported as a `.p12`.
+- `APPLE_CERTIFICATE_PASSWORD`: password for the exported `.p12`.
+- `APPLE_NOTARIZATION_KEY_P8`: base64-encoded App Store Connect API key.
+- `APPLE_NOTARIZATION_KEY_ID`: key ID for the notarization API key.
+- `APPLE_NOTARIZATION_ISSUER_ID`: issuer ID for the notarization API key.
+
+The workflow imports the certificate into a temporary keychain, signs with the
+hardened runtime enabled, notarizes the binary, and deletes the temporary
+keychain before the build job exits. The certificate and notarization key are
+not added to any npm tarball; the publish job still audits tarball contents
+before publishing.
+
## Automatic counter behavior
The workflow derives the upstream base version from `codex-rs/Cargo.toml` and
@@ -62,11 +84,17 @@ Merge or push to `stable` and the workflow will:
1. Read the base version from `codex-rs/Cargo.toml`.
2. Compute the next fork counter for that base release line.
3. Build the macOS release binaries with the derived display version.
-4. Create the matching `rick-v-rick.` tag on the merge commit.
-5. Generate GitHub release notes that separate fork changes from mainline
+4. Sign and notarize the Apple Silicon `codex` binary with
+ `com.rickgetz.codex`.
+5. Create the matching `rick-v-rick.` tag on the merge commit.
+6. Generate GitHub release notes that separate fork changes from mainline
Codex refreshes.
-6. Create a GitHub release.
-7. Publish the npm package.
+7. Create a GitHub release.
+8. Publish the npm package.
+
+Manual `workflow_dispatch` runs are guarded to `stable` as well. Dispatching the
+workflow from another branch skips the release jobs so Apple signing secrets are
+only exposed to the maintained release branch.
## Release notes