From de57f51afba05a244f838c93e83846207ccd515a Mon Sep 17 00:00:00 2001 From: Greg Harper Date: Tue, 17 Mar 2026 13:06:32 -0700 Subject: [PATCH] Make macOS signing action entitlement-aware # Conflicts: # .github/actions/macos-code-sign/action.yml --- .github/actions/macos-code-sign/README.md | 24 ++++ .github/actions/macos-code-sign/action.yml | 110 ++++++++++++++++-- .../macos/codex-shared-keychain.entitlements | 10 ++ 3 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 .github/actions/macos-code-sign/README.md create mode 100644 .github/macos/codex-shared-keychain.entitlements diff --git a/.github/actions/macos-code-sign/README.md b/.github/actions/macos-code-sign/README.md new file mode 100644 index 0000000000..d33c822f9e --- /dev/null +++ b/.github/actions/macos-code-sign/README.md @@ -0,0 +1,24 @@ +# macos-code-sign + +This action can sign the default Rust release binaries or an explicit list of macOS code paths. + +By default, alpha and beta tag builds automatically receive the shared keychain entitlement plist. +Set `shared-keychain-entitlement: false` to opt out, or `true` to force it on for other builds. + +To sign a `Codex.app` bundle with the shared keychain entitlement, pass the app path plus the +checked-in entitlements plist: + +```yaml +- uses: ./.github/actions/macos-code-sign + with: + target: aarch64-apple-darwin + sign-binaries: "true" + sign-dmg: "false" + sign-paths: | + path/to/Codex.app + shared-keychain-entitlement: true +``` + +If only some signed paths should receive the entitlements plist, set `entitlements-paths` to the +subset of paths that should be signed with `--entitlements`. You can still provide +`entitlements-file` directly if a workflow needs a different plist. diff --git a/.github/actions/macos-code-sign/action.yml b/.github/actions/macos-code-sign/action.yml index ea4a19a8f5..e6507403f5 100644 --- a/.github/actions/macos-code-sign/action.yml +++ b/.github/actions/macos-code-sign/action.yml @@ -12,6 +12,22 @@ inputs: description: Whether to sign and notarize the macOS dmg. required: false default: "true" + sign-paths: + description: Optional newline-separated list of paths to sign when signing macOS binaries. Defaults to the release codex binaries. + required: false + default: "" + entitlements-file: + description: Optional path to an entitlements plist to apply while signing selected macOS code paths. + required: false + default: "" + entitlements-paths: + description: Optional newline-separated list of signed paths that should receive entitlements. Defaults to all sign-paths when entitlements-file is set. + required: false + default: "" + shared-keychain-entitlement: + description: Whether to apply the shared keychain entitlement plist. `auto` enables it for alpha and beta tag builds. + required: false + default: "auto" apple-certificate: description: Base64-encoded Apple signing certificate (P12). required: true @@ -117,8 +133,6 @@ runs: - name: Sign macOS binaries if: ${{ inputs.sign-binaries == 'true' }} shell: bash - env: - TARGET: ${{ inputs.target }} run: | set -euo pipefail @@ -132,16 +146,92 @@ runs: keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}") fi - for binary in codex codex-responses-api-proxy; do - path="codex-rs/target/${TARGET}/release/${binary}" - codesign --force --options runtime --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$path" + normalize_sign_path() { + local path="$1" + if [[ "$path" != /* ]]; then + path="${GITHUB_WORKSPACE}/${path}" + fi + printf '%s\n' "$path" + } + + sign_paths=() + while IFS= read -r path; do + [[ -n "$path" ]] && sign_paths+=("$(normalize_sign_path "$path")") + done < <(printf '%s\n' '${{ inputs.sign-paths }}') + + if ((${#sign_paths[@]} == 0)); then + for binary in codex codex-responses-api-proxy; do + sign_paths+=("$(normalize_sign_path "codex-rs/target/${{ inputs.target }}/release/${binary}")") + done + fi + + entitlements_file="${{ inputs.entitlements-file }}" + shared_keychain_entitlement="${{ inputs.shared-keychain-entitlement }}" + if [[ -z "$entitlements_file" ]]; then + case "$shared_keychain_entitlement" in + true) + entitlements_file=".github/macos/codex-shared-keychain.entitlements" + ;; + auto) + if [[ "${GITHUB_REF_TYPE:-}" == "tag" ]] && [[ "${GITHUB_REF_NAME:-}" =~ -(alpha|beta)(\.[0-9]+)?$ ]]; then + entitlements_file=".github/macos/codex-shared-keychain.entitlements" + fi + ;; + false) + ;; + *) + echo "shared-keychain-entitlement must be one of: auto, true, false" + exit 1 + ;; + esac + fi + + if [[ -n "$entitlements_file" ]]; then + entitlements_file="$(normalize_sign_path "$entitlements_file")" + if [[ ! -f "$entitlements_file" ]]; then + echo "Entitlements file $entitlements_file not found" + exit 1 + fi + fi + + entitlements_paths=() + while IFS= read -r path; do + [[ -n "$path" ]] && entitlements_paths+=("$(normalize_sign_path "$path")") + done < <(printf '%s\n' '${{ inputs.entitlements-paths }}') + + for path in "${sign_paths[@]}"; do + if [[ ! -e "$path" ]]; then + echo "Sign path $path not found" + exit 1 + fi + + codesign_args=(--force --options runtime --timestamp --sign "$APPLE_CODESIGN_IDENTITY") + codesign_args+=("${keychain_args[@]}") + + if [[ -n "$entitlements_file" ]]; then + apply_entitlements=true + if ((${#entitlements_paths[@]} > 0)); then + apply_entitlements=false + for entitlements_path in "${entitlements_paths[@]}"; do + if [[ "$path" == "$entitlements_path" ]]; then + apply_entitlements=true + break + fi + done + fi + + if [[ "$apply_entitlements" == "true" ]]; then + codesign_args+=(--entitlements "$entitlements_file") + fi + fi + + codesign "${codesign_args[@]}" "$path" done - name: Notarize macOS binaries if: ${{ inputs.sign-binaries == 'true' }} shell: bash env: - TARGET: ${{ inputs.target }} APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }} APPLE_NOTARIZATION_KEY_ID: ${{ inputs.apple-notarization-key-id }} APPLE_NOTARIZATION_ISSUER_ID: ${{ inputs.apple-notarization-issuer-id }} @@ -166,7 +256,7 @@ runs: notarize_binary() { local binary="$1" - local source_path="codex-rs/target/${TARGET}/release/${binary}" + local source_path="codex-rs/target/${{ inputs.target }}/release/${binary}" local archive_path="${RUNNER_TEMP}/${binary}.zip" if [[ ! -f "$source_path" ]]; then @@ -187,7 +277,6 @@ runs: if: ${{ inputs.sign-dmg == 'true' }} shell: bash env: - TARGET: ${{ inputs.target }} APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }} APPLE_NOTARIZATION_KEY_ID: ${{ inputs.apple-notarization-key-id }} APPLE_NOTARIZATION_ISSUER_ID: ${{ inputs.apple-notarization-issuer-id }} @@ -210,8 +299,7 @@ runs: source "$GITHUB_ACTION_PATH/notary_helpers.sh" - dmg_name="codex-${TARGET}.dmg" - dmg_path="codex-rs/target/${TARGET}/release/${dmg_name}" + dmg_path="codex-rs/target/${{ inputs.target }}/release/codex-${{ inputs.target }}.dmg" if [[ ! -f "$dmg_path" ]]; then echo "dmg $dmg_path not found" @@ -224,7 +312,7 @@ runs: fi codesign --force --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$dmg_path" - notarize_submission "$dmg_name" "$dmg_path" "$notary_key_path" + notarize_submission "codex-${{ inputs.target }}.dmg" "$dmg_path" "$notary_key_path" xcrun stapler staple "$dmg_path" - name: Remove signing keychain diff --git a/.github/macos/codex-shared-keychain.entitlements b/.github/macos/codex-shared-keychain.entitlements new file mode 100644 index 0000000000..db939087d0 --- /dev/null +++ b/.github/macos/codex-shared-keychain.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + 2DC432GLL2.com.openai.shared + + +