mirror of
https://github.com/openai/codex.git
synced 2026-09-13 11:47:17 +00:00
## What changed - Add a tag-release workflow gated by `CODEX_PROVISIONED_MACOS_CANDIDATE` for Apple Silicon and Intel macOS candidate artifacts. - Package the CLI in `CodexCLI.app` with an embedded provisioning profile and a relocatable `bin/codex` launcher. Validate independently supplied profile, certificate, and team expectations before signing. - Sign and notarize candidates, then verify signatures, entitlements, architecture, stapling, and Gatekeeper acceptance on macOS. Retain verified artifacts after package smoke tests pass. - Filter code-mode smoke-test requests to `/v1/responses` so analytics requests are excluded from response parsing. ## Testing Add tests for profile validation, launcher relocation and symlink handling, standard and provisioned signing flows, and rejection of signing, notarization, identity, and entitlement failures. The candidate workflow runs package smoke tests, including sandboxed code mode. GitOrigin-RevId: f214f6a23dd10df62cd72a2c63c20cb0864fbc9c
214 lines
10 KiB
YAML
214 lines
10 KiB
YAML
# Optional side artifacts only; no release, npm, installer, or DMG publication.
|
|
# Required secrets in the codesigning environment:
|
|
# - CODEX_CLI_PROVISIONING_PROFILE_BASE64: profile approved for public distribution.
|
|
# - CODEX_CLI_PROVISIONING_PROFILE_SHA256: reviewed lowercase SHA-256 of that profile.
|
|
# - CODEX_CLI_PROVISIONING_TEAM_ID: expected ten-character Apple Team ID.
|
|
# - AKV_CODESIGN_CERTIFICATE_SHA256: required signing certificate pin.
|
|
# Set repository variable CODEX_PROVISIONED_MACOS_CANDIDATE to true to enable
|
|
# candidates on normal tag releases; unset it or set it to false to disable them.
|
|
name: Provisioned macOS CLI candidate
|
|
|
|
on:
|
|
workflow_call:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
CODEX_REPO_ROOT: ${{ github.workspace }}
|
|
|
|
jobs:
|
|
sign:
|
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/rust-v') && vars.CODEX_PROVISIONED_MACOS_CANDIDATE == 'true'
|
|
runs-on: ubuntu-24.04
|
|
timeout-minutes: 45
|
|
environment:
|
|
name: codesigning
|
|
deployment: false
|
|
permissions:
|
|
contents: read
|
|
id-token: write
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
target: [aarch64-apple-darwin, x86_64-apple-darwin]
|
|
env:
|
|
TARGET: ${{ matrix.target }}
|
|
PROVISIONED_MACOS: "true"
|
|
PROFILE_SHA256: ${{ secrets.CODEX_CLI_PROVISIONING_PROFILE_SHA256 }}
|
|
CERTIFICATE_SHA256: ${{ secrets.AKV_CODESIGN_CERTIFICATE_SHA256 }}
|
|
TEAM_ID: ${{ secrets.CODEX_CLI_PROVISIONING_TEAM_ID }}
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: "3.12"
|
|
- name: Validate the independently approved public-distribution profile
|
|
env:
|
|
PROFILE_BASE64: ${{ secrets.CODEX_CLI_PROVISIONING_PROFILE_BASE64 }}
|
|
run: |
|
|
python3 - <<'PY'
|
|
import base64
|
|
import os
|
|
from pathlib import Path
|
|
profile = Path(os.environ["RUNNER_TEMP"]) / "codex-cli.provisionprofile"
|
|
profile.write_bytes(base64.b64decode("".join(os.environ["PROFILE_BASE64"].split()), validate=True))
|
|
PY
|
|
python3 .github/scripts/macos-signing/provisioned_macos_cli_package.py validate-profile \
|
|
--profile "$RUNNER_TEMP/codex-cli.provisionprofile" --profile-sha256 "$PROFILE_SHA256" \
|
|
--certificate-sha256 "$CERTIFICATE_SHA256" --team-id "$TEAM_ID"
|
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: ${{ matrix.target }}
|
|
path: standard
|
|
- name: Extract the verified package from this tag-release run
|
|
run: |
|
|
python3 - <<'PY'
|
|
import json
|
|
import os
|
|
import tarfile
|
|
from pathlib import Path
|
|
with tarfile.open(f"standard/codex-package-{os.environ['TARGET']}.tar.gz") as archive:
|
|
archive.extractall("package", filter="data")
|
|
metadata = json.loads(Path("package/codex-package.json").read_text())
|
|
if metadata["target"] != os.environ["TARGET"]:
|
|
raise ValueError("Package target does not match this release job")
|
|
PY
|
|
- name: Set up the existing AKV signer
|
|
uses: ./.github/actions/setup-akv-pkcs11-codesigning
|
|
with:
|
|
rcodesign-blob-uri: ${{ secrets.AKV_CODESIGN_RCODESIGN_BLOB_URI }}
|
|
rcodesign-sha256: ${{ secrets.AKV_CODESIGN_RCODESIGN_SHA256 }}
|
|
akv-pkcs11-library-blob-uri: ${{ secrets.AKV_CODESIGN_PKCS11_LIBRARY_BLOB_URI }}
|
|
akv-pkcs11-library-sha256: ${{ secrets.AKV_CODESIGN_PKCS11_LIBRARY_SHA256 }}
|
|
azure-client-id: ${{ secrets.AKV_CODESIGN_AZURE_CLIENT_ID }}
|
|
azure-tenant-id: ${{ secrets.AKV_CODESIGN_TENANT }}
|
|
azure-subscription-id: ${{ secrets.AKV_CODESIGN_SUBSCRIPTION }}
|
|
key-vault-name: ${{ secrets.AKV_CODESIGN_KEY_VAULT_NAME }}
|
|
key-name: ${{ secrets.AKV_CODESIGN_KEY_NAME }}
|
|
key-version: ${{ secrets.AKV_CODESIGN_KEY_VERSION || '' }}
|
|
certificate-sha256: ${{ secrets.AKV_CODESIGN_CERTIFICATE_SHA256 }}
|
|
- name: Sign and notarize the provisioned bundle
|
|
env:
|
|
APPLE_NOTARIZATION_AKV_KEY_NAME: ${{ secrets.AKV_NOTARIZATION_KEY_NAME }}
|
|
APPLE_NOTARIZATION_AKV_KEY_VERSION: ${{ secrets.AKV_NOTARIZATION_KEY_VERSION }}
|
|
run: |
|
|
python3 .github/scripts/macos-signing/sign_macos_cli_package.py sign package \
|
|
--profile "$RUNNER_TEMP/codex-cli.provisionprofile" --profile-sha256 "$PROFILE_SHA256" \
|
|
--certificate-sha256 "$CERTIFICATE_SHA256" --team-id "$TEAM_ID"
|
|
mkdir signed
|
|
tar -czf "signed/codex-provisioned-package-$TARGET.tar.gz" -C package \
|
|
bin codex-resources codex-path codex-package.json CodexCLI.app
|
|
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
with:
|
|
name: provisioned-macos-signed-candidate-${{ matrix.target }}
|
|
path: signed/*.tar.gz
|
|
if-no-files-found: error
|
|
- name: Retain signing reports
|
|
if: always()
|
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
with:
|
|
name: provisioned-macos-signing-reports-${{ matrix.target }}
|
|
path: signing-verification/
|
|
if-no-files-found: warn
|
|
|
|
verify:
|
|
needs: sign
|
|
runs-on: macos-15-xlarge
|
|
timeout-minutes: 30
|
|
environment:
|
|
name: codesigning
|
|
deployment: false
|
|
# Read independent profile/certificate expectations without a signing token.
|
|
permissions:
|
|
contents: read
|
|
id-token: none
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
target: [aarch64-apple-darwin, x86_64-apple-darwin]
|
|
env:
|
|
TARGET: ${{ matrix.target }}
|
|
PROVISIONED_MACOS: "true"
|
|
PROFILE_SHA256: ${{ secrets.CODEX_CLI_PROVISIONING_PROFILE_SHA256 }}
|
|
CERTIFICATE_SHA256: ${{ secrets.AKV_CODESIGN_CERTIFICATE_SHA256 }}
|
|
TEAM_ID: ${{ secrets.CODEX_CLI_PROVISIONING_TEAM_ID }}
|
|
steps:
|
|
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
persist-credentials: false
|
|
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
|
with:
|
|
python-version: "3.12"
|
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: provisioned-macos-signed-candidate-${{ matrix.target }}
|
|
path: signed
|
|
- name: Extract candidate and load independent verification profile
|
|
env:
|
|
PROFILE_BASE64: ${{ secrets.CODEX_CLI_PROVISIONING_PROFILE_BASE64 }}
|
|
run: |
|
|
python3 - <<'PY'
|
|
import base64
|
|
import os
|
|
import tarfile
|
|
from pathlib import Path
|
|
profile = Path(os.environ["RUNNER_TEMP"]) / "codex-cli.provisionprofile"
|
|
profile.write_bytes(base64.b64decode("".join(os.environ["PROFILE_BASE64"].split()), validate=True))
|
|
with tarfile.open(f"signed/codex-provisioned-package-{os.environ['TARGET']}.tar.gz") as archive:
|
|
archive.extractall("package", filter="data")
|
|
PY
|
|
- name: Verify signatures, profile, architecture, stapling and Gatekeeper
|
|
run: |
|
|
profile_args=(--profile "$RUNNER_TEMP/codex-cli.provisionprofile"
|
|
--profile-sha256 "$PROFILE_SHA256" --certificate-sha256 "$CERTIFICATE_SHA256"
|
|
--team-id "$TEAM_ID")
|
|
python3 .github/scripts/macos-signing/sign_macos_cli_package.py verify package "${profile_args[@]}"
|
|
xcrun stapler staple package/CodexCLI.app
|
|
xcrun stapler validate package/CodexCLI.app
|
|
spctl --assess --type execute --verbose=4 package/CodexCLI.app
|
|
python3 .github/scripts/macos-signing/sign_macos_cli_package.py verify package "${profile_args[@]}"
|
|
mkdir verified
|
|
tar -czf "verified/codex-provisioned-package-$TARGET.tar.gz" -C package \
|
|
bin codex-resources codex-path codex-package.json CodexCLI.app
|
|
(cd verified && shasum -a 256 "codex-provisioned-package-$TARGET.tar.gz" > SHA256SUMS)
|
|
- uses: ./.github/actions/setup-ci
|
|
- uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
|
|
with:
|
|
version: "0.11.3"
|
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: ${{ matrix.target }}-app-server
|
|
path: app-server
|
|
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
|
|
with:
|
|
name: ${{ matrix.target }}-symbols
|
|
path: symbols
|
|
- name: Test the exact verified package, including sandboxed code mode
|
|
working-directory: scripts/codex_package/smoke_tests
|
|
env:
|
|
PYTHONPATH: ${{ github.workspace }}/sdk/python/src:${{ github.workspace }}/sdk/python/tests
|
|
run: |
|
|
# This repackaging does not change debug code. The normal release's
|
|
# primary symbols omit app-server symbols, so skip symbol-only tests.
|
|
uv run --frozen pytest -v --compression gzip --package-target "$TARGET" \
|
|
--cli-archive "$CODEX_REPO_ROOT/verified/codex-provisioned-package-$TARGET.tar.gz" \
|
|
--app-server-archive "$CODEX_REPO_ROOT/app-server/codex-app-server-package-$TARGET.tar.gz" \
|
|
--symbols-archive "$CODEX_REPO_ROOT/symbols/codex-symbols-$TARGET.tar.gz" \
|
|
test_codex_package.py -k 'not debug_symbols'
|
|
- name: Retain verified candidate only after package tests pass
|
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
with:
|
|
name: provisioned-macos-verified-candidate-${{ matrix.target }}
|
|
path: verified/*
|
|
if-no-files-found: error
|
|
- name: Retain verification reports
|
|
if: always()
|
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
|
with:
|
|
name: provisioned-macos-verification-reports-${{ matrix.target }}
|
|
path: signing-verification/
|
|
if-no-files-found: warn
|