Skip to content

Commit 6e47bd6

Browse files
authored
Add code signing support for XCFrameworks (#844)
1 parent cd9a5fc commit 6e47bd6

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

.github/actions/build-xcframework/action.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ inputs:
1010
description: 'Tag name for release URLs (empty for manual builds)'
1111
required: false
1212
default: ''
13+
signing-certificate-base64:
14+
description: 'Base64-encoded .p12 signing certificate'
15+
required: false
16+
default: ''
17+
signing-certificate-password:
18+
description: 'Password for the .p12 signing certificate'
19+
required: false
20+
default: ''
1321

1422
outputs:
1523
body:
@@ -29,6 +37,20 @@ runs:
2937
- name: Build XCFrameworks
3038
run: Scripts/build_xcframework.sh OpenSwiftUI
3139
shell: bash
40+
- name: Code sign XCFrameworks
41+
if: ${{ inputs.signing-certificate-base64 != '' }}
42+
uses: ./.github/actions/codesign-xcframework
43+
with:
44+
signing-certificate-base64: ${{ inputs.signing-certificate-base64 }}
45+
signing-certificate-password: ${{ inputs.signing-certificate-password }}
46+
xcframework-paths: >-
47+
build/OpenSwiftUI.xcframework
48+
build/OpenSwiftUICore.xcframework
49+
build/OpenAttributeGraphShims.xcframework
50+
build/OpenCoreGraphicsShims.xcframework
51+
build/OpenObservation.xcframework
52+
build/OpenQuartzCoreShims.xcframework
53+
build/OpenRenderBoxShims.xcframework
3254
- name: Compute Checksums and Generate Summary
3355
id: checksums
3456
shell: bash
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
name: 'Code Sign XCFramework'
2+
description: 'Import a signing certificate and code sign xcframeworks'
3+
4+
inputs:
5+
signing-certificate-base64:
6+
description: 'Base64-encoded .p12 signing certificate'
7+
required: true
8+
signing-certificate-password:
9+
description: 'Password for the .p12 signing certificate'
10+
required: true
11+
signing-identity:
12+
description: 'Common Name of the signing certificate'
13+
required: false
14+
default: 'OpenSwiftUI'
15+
xcframework-paths:
16+
description: 'Space-separated paths to xcframeworks to sign'
17+
required: true
18+
19+
runs:
20+
using: 'composite'
21+
steps:
22+
- name: Set up signing keychain
23+
shell: bash
24+
env:
25+
CERTIFICATE_BASE64: ${{ inputs.signing-certificate-base64 }}
26+
CERTIFICATE_PASSWORD: ${{ inputs.signing-certificate-password }}
27+
SIGNING_IDENTITY: ${{ inputs.signing-identity }}
28+
run: |
29+
KEYCHAIN_PASSWORD="ci_signing_temp"
30+
KEYCHAIN_NAME="ci_signing.keychain-db"
31+
KEYCHAIN_PATH="$HOME/Library/Keychains/$KEYCHAIN_NAME"
32+
33+
# Save original keychain search list
34+
ORIGINAL_KEYCHAINS=$(security list-keychains -d user | sed 's/^ *"//;s/" *$//' | tr '\n' ' ')
35+
36+
# Clean up any existing CI keychain
37+
security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
38+
rm -f "$KEYCHAIN_PATH" 2>/dev/null || true
39+
40+
# Create and unlock keychain
41+
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
42+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
43+
44+
# NOTE: Do NOT call security set-keychain-settings here.
45+
# It corrupts keychain state in non-interactive CI sessions.
46+
47+
# Add to search list (prepend CI keychain, keep existing)
48+
security list-keychains -d user -s "$KEYCHAIN_PATH" $ORIGINAL_KEYCHAINS
49+
50+
# Decode and import certificate
51+
# Use -A to allow all applications access without prompting.
52+
# Per-app ACL entries (-T) cause "User interaction is not allowed" prompts.
53+
echo "$CERTIFICATE_BASE64" | base64 -d > /tmp/signing.p12
54+
security import /tmp/signing.p12 -k "$KEYCHAIN_PATH" -P "$CERTIFICATE_PASSWORD" -A
55+
rm -f /tmp/signing.p12
56+
57+
# Set partition list to allow codesign access without prompting
58+
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" 2>/dev/null || true
59+
60+
# Try to trust certificate for code signing (works on self-hosted runners)
61+
security find-certificate -c "$SIGNING_IDENTITY" -p "$KEYCHAIN_PATH" > /tmp/signing-cert.pem
62+
sudo security add-trusted-cert -d -r trustRoot -p codeSign /tmp/signing-cert.pem 2>/dev/null || true
63+
rm -f /tmp/signing-cert.pem
64+
65+
# Debug: inspect keychain contents
66+
echo "=== Certificates in keychain ==="
67+
security find-certificate -a -c "$SIGNING_IDENTITY" -Z "$KEYCHAIN_PATH" 2>&1 | head -5 || true
68+
echo "=== All identities (no policy filter) ==="
69+
security find-identity "$KEYCHAIN_PATH" || true
70+
echo "=== All identities (codesigning policy) ==="
71+
security find-identity -p codesigning "$KEYCHAIN_PATH" || true
72+
echo "=== Valid codesigning identities ==="
73+
security find-identity -v -p codesigning "$KEYCHAIN_PATH" || true
74+
75+
- name: Code sign xcframeworks
76+
shell: bash
77+
env:
78+
SIGNING_IDENTITY: ${{ inputs.signing-identity }}
79+
XCFRAMEWORK_PATHS: ${{ inputs.xcframework-paths }}
80+
run: |
81+
KEYCHAIN_PATH="$HOME/Library/Keychains/ci_signing.keychain-db"
82+
# Try: trusted codesigning → all codesigning → any identity (no policy)
83+
# Note: || true is needed because grep returns 1 on no match, which kills -e -o pipefail
84+
SIGNING_HASH=$(security find-identity -v -p codesigning "$KEYCHAIN_PATH" 2>/dev/null | grep "$SIGNING_IDENTITY" | head -1 | awk '{print $2}' || true)
85+
if [ -z "$SIGNING_HASH" ]; then
86+
SIGNING_HASH=$(security find-identity -p codesigning "$KEYCHAIN_PATH" 2>/dev/null | grep "$SIGNING_IDENTITY" | head -1 | awk '{print $2}' || true)
87+
fi
88+
if [ -z "$SIGNING_HASH" ]; then
89+
SIGNING_HASH=$(security find-identity "$KEYCHAIN_PATH" 2>/dev/null | grep "$SIGNING_IDENTITY" | head -1 | awk '{print $2}' || true)
90+
fi
91+
if [ -z "$SIGNING_HASH" ]; then
92+
echo "::error::Signing identity '$SIGNING_IDENTITY' not found"
93+
exit 1
94+
fi
95+
echo "Signing identity: $SIGNING_HASH ($SIGNING_IDENTITY)"
96+
for fw_path in $XCFRAMEWORK_PATHS; do
97+
if [ -d "$fw_path" ]; then
98+
echo "Signing $fw_path..."
99+
codesign --force --timestamp -v --sign "$SIGNING_HASH" --keychain "$KEYCHAIN_PATH" "$fw_path"
100+
else
101+
echo "::warning::$fw_path not found, skipping"
102+
fi
103+
done
104+
echo "Code signing completed."

.github/workflows/build_xcframework.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ jobs:
1515
- uses: actions/checkout@v4
1616
- name: Build XCFrameworks
1717
uses: ./.github/actions/build-xcframework
18+
with:
19+
signing-certificate-base64: ${{ secrets.SIGNING_CERTIFICATE_BASE_64 }}
20+
signing-certificate-password: ${{ secrets.SIGNING_CERTIFICATE_PASSWORD }}
1821
- name: Zip XCFrameworks
1922
run: cd build && zip -ry xcframeworks.zip *.xcframework
2023
- name: Upload XCFrameworks

.github/workflows/release.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ jobs:
2323
uses: ./.github/actions/build-xcframework
2424
with:
2525
tag-name: ${{ github.ref_name }}
26+
signing-certificate-base64: ${{ secrets.SIGNING_CERTIFICATE_BASE_64 }}
27+
signing-certificate-password: ${{ secrets.SIGNING_CERTIFICATE_PASSWORD }}
2628
- name: Create Release
2729
uses: ncipollo/release-action@v1
2830
with:

0 commit comments

Comments
 (0)