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