mirror of
https://github.com/openai/codex.git
synced 2026-04-24 22:54:54 +00:00
[release] Add a dmg target for MacOS (#8207)
Add a dmg target that bundles the codex and codex responses api proxy binaries for MacOS. this target is signed and notarized. Verified by triggering a build here: https://github.com/openai/codex/actions/runs/20318136302/job/58367155205. Downloaded the artifact and verified that the dmg is signed and notarized, and the codex binary contained works as expected.
This commit is contained in:
76
.github/actions/macos-code-sign/action.yml
vendored
76
.github/actions/macos-code-sign/action.yml
vendored
@@ -4,6 +4,14 @@ inputs:
|
|||||||
target:
|
target:
|
||||||
description: Rust compilation target triple (e.g. aarch64-apple-darwin).
|
description: Rust compilation target triple (e.g. aarch64-apple-darwin).
|
||||||
required: true
|
required: true
|
||||||
|
sign-binaries:
|
||||||
|
description: Whether to sign and notarize the macOS binaries.
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
sign-dmg:
|
||||||
|
description: Whether to sign and notarize the macOS dmg.
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
apple-certificate:
|
apple-certificate:
|
||||||
description: Base64-encoded Apple signing certificate (P12).
|
description: Base64-encoded Apple signing certificate (P12).
|
||||||
required: true
|
required: true
|
||||||
@@ -107,6 +115,7 @@ runs:
|
|||||||
echo "::add-mask::$APPLE_CODESIGN_IDENTITY"
|
echo "::add-mask::$APPLE_CODESIGN_IDENTITY"
|
||||||
|
|
||||||
- name: Sign macOS binaries
|
- name: Sign macOS binaries
|
||||||
|
if: ${{ inputs.sign-binaries == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
@@ -127,6 +136,7 @@ runs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
- name: Notarize macOS binaries
|
- name: Notarize macOS binaries
|
||||||
|
if: ${{ inputs.sign-binaries == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }}
|
APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }}
|
||||||
@@ -149,6 +159,8 @@ runs:
|
|||||||
}
|
}
|
||||||
trap cleanup_notary EXIT
|
trap cleanup_notary EXIT
|
||||||
|
|
||||||
|
source "$GITHUB_ACTION_PATH/notary_helpers.sh"
|
||||||
|
|
||||||
notarize_binary() {
|
notarize_binary() {
|
||||||
local binary="$1"
|
local binary="$1"
|
||||||
local source_path="codex-rs/target/${{ inputs.target }}/release/${binary}"
|
local source_path="codex-rs/target/${{ inputs.target }}/release/${binary}"
|
||||||
@@ -162,32 +174,54 @@ runs:
|
|||||||
rm -f "$archive_path"
|
rm -f "$archive_path"
|
||||||
ditto -c -k --keepParent "$source_path" "$archive_path"
|
ditto -c -k --keepParent "$source_path" "$archive_path"
|
||||||
|
|
||||||
submission_json=$(xcrun notarytool submit "$archive_path" \
|
notarize_submission "$binary" "$archive_path" "$notary_key_path"
|
||||||
--key "$notary_key_path" \
|
|
||||||
--key-id "$APPLE_NOTARIZATION_KEY_ID" \
|
|
||||||
--issuer "$APPLE_NOTARIZATION_ISSUER_ID" \
|
|
||||||
--output-format json \
|
|
||||||
--wait)
|
|
||||||
|
|
||||||
status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"')
|
|
||||||
submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""')
|
|
||||||
|
|
||||||
if [[ -z "$submission_id" ]]; then
|
|
||||||
echo "Failed to retrieve submission ID for $binary"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "::notice title=Notarization::$binary submission ${submission_id} completed with status ${status}"
|
|
||||||
|
|
||||||
if [[ "$status" != "Accepted" ]]; then
|
|
||||||
echo "Notarization failed for ${binary} (submission ${submission_id}, status ${status})"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
}
|
||||||
|
|
||||||
notarize_binary "codex"
|
notarize_binary "codex"
|
||||||
notarize_binary "codex-responses-api-proxy"
|
notarize_binary "codex-responses-api-proxy"
|
||||||
|
|
||||||
|
- name: Sign and notarize macOS dmg
|
||||||
|
if: ${{ inputs.sign-dmg == 'true' }}
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
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 }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
for var in APPLE_CODESIGN_IDENTITY APPLE_NOTARIZATION_KEY_P8 APPLE_NOTARIZATION_KEY_ID APPLE_NOTARIZATION_ISSUER_ID; do
|
||||||
|
if [[ -z "${!var:-}" ]]; then
|
||||||
|
echo "$var is required"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
notary_key_path="${RUNNER_TEMP}/notarytool.key.p8"
|
||||||
|
echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$notary_key_path"
|
||||||
|
cleanup_notary() {
|
||||||
|
rm -f "$notary_key_path"
|
||||||
|
}
|
||||||
|
trap cleanup_notary EXIT
|
||||||
|
|
||||||
|
source "$GITHUB_ACTION_PATH/notary_helpers.sh"
|
||||||
|
|
||||||
|
dmg_path="codex-rs/target/${{ inputs.target }}/release/codex-${{ inputs.target }}.dmg"
|
||||||
|
|
||||||
|
if [[ ! -f "$dmg_path" ]]; then
|
||||||
|
echo "dmg $dmg_path not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
keychain_args=()
|
||||||
|
if [[ -n "${APPLE_CODESIGN_KEYCHAIN:-}" && -f "${APPLE_CODESIGN_KEYCHAIN}" ]]; then
|
||||||
|
keychain_args+=(--keychain "${APPLE_CODESIGN_KEYCHAIN}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
codesign --force --timestamp --sign "$APPLE_CODESIGN_IDENTITY" "${keychain_args[@]}" "$dmg_path"
|
||||||
|
notarize_submission "codex-${{ inputs.target }}.dmg" "$dmg_path" "$notary_key_path"
|
||||||
|
xcrun stapler staple "$dmg_path"
|
||||||
|
|
||||||
- name: Remove signing keychain
|
- name: Remove signing keychain
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
46
.github/actions/macos-code-sign/notary_helpers.sh
vendored
Normal file
46
.github/actions/macos-code-sign/notary_helpers.sh
vendored
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
notarize_submission() {
|
||||||
|
local label="$1"
|
||||||
|
local path="$2"
|
||||||
|
local notary_key_path="$3"
|
||||||
|
|
||||||
|
if [[ -z "${APPLE_NOTARIZATION_KEY_ID:-}" || -z "${APPLE_NOTARIZATION_ISSUER_ID:-}" ]]; then
|
||||||
|
echo "APPLE_NOTARIZATION_KEY_ID and APPLE_NOTARIZATION_ISSUER_ID are required for notarization"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$notary_key_path" || ! -f "$notary_key_path" ]]; then
|
||||||
|
echo "Notary key file $notary_key_path not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$path" ]]; then
|
||||||
|
echo "Notarization payload $path not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local submission_json
|
||||||
|
submission_json=$(xcrun notarytool submit "$path" \
|
||||||
|
--key "$notary_key_path" \
|
||||||
|
--key-id "$APPLE_NOTARIZATION_KEY_ID" \
|
||||||
|
--issuer "$APPLE_NOTARIZATION_ISSUER_ID" \
|
||||||
|
--output-format json \
|
||||||
|
--wait)
|
||||||
|
|
||||||
|
local status submission_id
|
||||||
|
status=$(printf '%s\n' "$submission_json" | jq -r '.status // "Unknown"')
|
||||||
|
submission_id=$(printf '%s\n' "$submission_json" | jq -r '.id // ""')
|
||||||
|
|
||||||
|
if [[ -z "$submission_id" ]]; then
|
||||||
|
echo "Failed to retrieve submission ID for $label"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "::notice title=Notarization::$label submission ${submission_id} completed with status ${status}"
|
||||||
|
|
||||||
|
if [[ "$status" != "Accepted" ]]; then
|
||||||
|
echo "Notarization failed for ${label} (submission ${submission_id}, status ${status})"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
71
.github/workflows/rust-release.yml
vendored
71
.github/workflows/rust-release.yml
vendored
@@ -128,11 +128,72 @@ jobs:
|
|||||||
account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
|
account-name: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
|
||||||
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}
|
certificate-profile-name: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE_NAME }}
|
||||||
|
|
||||||
- if: ${{ matrix.runner == 'macos-15-xlarge' }}
|
- if: ${{ runner.os == 'macOS' }}
|
||||||
name: MacOS code signing
|
name: MacOS code signing (binaries)
|
||||||
uses: ./.github/actions/macos-code-sign
|
uses: ./.github/actions/macos-code-sign
|
||||||
with:
|
with:
|
||||||
target: ${{ matrix.target }}
|
target: ${{ matrix.target }}
|
||||||
|
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 }}
|
||||||
|
|
||||||
|
- if: ${{ runner.os == 'macOS' }}
|
||||||
|
name: Build macOS dmg
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
target="${{ matrix.target }}"
|
||||||
|
release_dir="target/${target}/release"
|
||||||
|
dmg_root="${RUNNER_TEMP}/codex-dmg-root"
|
||||||
|
volname="Codex (${target})"
|
||||||
|
dmg_path="${release_dir}/codex-${target}.dmg"
|
||||||
|
|
||||||
|
# The previous "MacOS code signing (binaries)" step signs + notarizes the
|
||||||
|
# built artifacts in `${release_dir}`. This step packages *those same*
|
||||||
|
# signed binaries into a dmg.
|
||||||
|
codex_binary_path="${release_dir}/codex"
|
||||||
|
proxy_binary_path="${release_dir}/codex-responses-api-proxy"
|
||||||
|
|
||||||
|
rm -rf "$dmg_root"
|
||||||
|
mkdir -p "$dmg_root"
|
||||||
|
|
||||||
|
if [[ ! -f "$codex_binary_path" ]]; then
|
||||||
|
echo "Binary $codex_binary_path not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! -f "$proxy_binary_path" ]]; then
|
||||||
|
echo "Binary $proxy_binary_path not found"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ditto "$codex_binary_path" "${dmg_root}/codex"
|
||||||
|
ditto "$proxy_binary_path" "${dmg_root}/codex-responses-api-proxy"
|
||||||
|
|
||||||
|
rm -f "$dmg_path"
|
||||||
|
hdiutil create \
|
||||||
|
-volname "$volname" \
|
||||||
|
-srcfolder "$dmg_root" \
|
||||||
|
-format UDZO \
|
||||||
|
-ov \
|
||||||
|
"$dmg_path"
|
||||||
|
|
||||||
|
if [[ ! -f "$dmg_path" ]]; then
|
||||||
|
echo "dmg $dmg_path not found after build"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
- if: ${{ runner.os == 'macOS' }}
|
||||||
|
name: MacOS code signing (dmg)
|
||||||
|
uses: ./.github/actions/macos-code-sign
|
||||||
|
with:
|
||||||
|
target: ${{ matrix.target }}
|
||||||
|
sign-binaries: "false"
|
||||||
|
sign-dmg: "true"
|
||||||
apple-certificate: ${{ secrets.APPLE_CERTIFICATE_P12 }}
|
apple-certificate: ${{ secrets.APPLE_CERTIFICATE_P12 }}
|
||||||
apple-certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
apple-certificate-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
apple-notarization-key-p8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }}
|
apple-notarization-key-p8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }}
|
||||||
@@ -160,6 +221,10 @@ jobs:
|
|||||||
cp target/${{ matrix.target }}/release/codex-responses-api-proxy.sigstore "$dest/codex-responses-api-proxy-${{ matrix.target }}.sigstore"
|
cp target/${{ matrix.target }}/release/codex-responses-api-proxy.sigstore "$dest/codex-responses-api-proxy-${{ matrix.target }}.sigstore"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [[ "${{ matrix.target }}" == *apple-darwin ]]; then
|
||||||
|
cp target/${{ matrix.target }}/release/codex-${{ matrix.target }}.dmg "$dest/codex-${{ matrix.target }}.dmg"
|
||||||
|
fi
|
||||||
|
|
||||||
- if: ${{ matrix.runner == 'windows-11-arm' }}
|
- if: ${{ matrix.runner == 'windows-11-arm' }}
|
||||||
name: Install zstd
|
name: Install zstd
|
||||||
shell: powershell
|
shell: powershell
|
||||||
@@ -194,7 +259,7 @@ jobs:
|
|||||||
base="$(basename "$f")"
|
base="$(basename "$f")"
|
||||||
# Skip files that are already archives (shouldn't happen, but be
|
# Skip files that are already archives (shouldn't happen, but be
|
||||||
# safe).
|
# safe).
|
||||||
if [[ "$base" == *.tar.gz || "$base" == *.zip ]]; then
|
if [[ "$base" == *.tar.gz || "$base" == *.zip || "$base" == *.dmg ]]; then
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user