Skip to content

Commit 309922b

Browse files
authored
Merge pull request #13 from nodeselector/feat/misc-enhancements
feat: Enhanced certificate handling, improved ergonomics, and documentation
2 parents c6c0306 + 6314bea commit 309922b

14 files changed

Lines changed: 30307 additions & 131 deletions

File tree

‎.github/linters/.gitleaks.toml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[extend]
2+
useDefault = true
3+
4+
[allowlist]
5+
paths = [
6+
'''.*__tests__/.*\.ts$''',
7+
]
8+
9+
# Allow string comparisons checking for PEM headers (false positives - not actual secrets)
10+
# These match findings where the line contains .includes() checks
11+
regexTarget = "match"
12+
regexes = [
13+
'''\.includes\(''',
14+
]

‎.github/workflows/ci.yml‎

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -68,37 +68,42 @@ jobs:
6868
run: |
6969
name="Apple Development: Created via API (DEADBEEFACID)"
7070
result=$(security find-certificate -c "$name" -a)
71-
if echo $result | grep -q "$name"; then
71+
if echo "$result" | grep -q "$name"; then
7272
echo "Certificate found in keychain"
7373
echo "::debug::Certificate found in keychain"
7474
else
7575
echo "Unable to find certificate in keychain"
7676
echo "::error title=Certificate not found::Unable to find certificate in keychain"
77-
echo $result
77+
echo "$result"
7878
exit 1
7979
fi
8080
8181
- name: Read contents of authkey into GitHub env
8282
run: npm run ci-load-app-key
8383

8484
- name: Test App Store Connect API Key Import
85+
id: test-api-key
8586
uses: ./
8687
with:
8788
asset-type: app-store-connect-api-key
8889
secret-value: ${{ env.APP_STORE_CONNECT_API_KEY }}
8990

90-
- name: Validate certificate installed
91+
- name: Validate API key installed
9192
run: |
92-
if ! [[ -f $RUNNER_TEMP/.app-store-connect-api-key.p8 ]]; then
93-
echo "App Store Connect API Key not found"
93+
KEY_PATH="${{ steps.test-api-key.outputs.app-store-connect-api-key-key-path }}"
94+
if ! [[ -f "$KEY_PATH" ]]; then
95+
echo "App Store Connect API Key not found at $KEY_PATH"
9496
echo "::error title=App Store Connect API Key not found::App Store Connect API Key not found"
95-
ls -la $RUNNER_TEMP
97+
ls -la ~/.appstoreconnect/private_keys/ 2>/dev/null || echo "Directory does not exist"
9698
exit 1
9799
fi
100+
echo "App Store Connect API Key found at $KEY_PATH"
98101
99-
if ! [[ -f $RUNNER_TEMP/.app-store-connect-api-key.json ]]; then
100-
echo "App Store Connect API Key object not found"
101-
echo "::error title=App Store Connect API Key object not found::App Store Connect API Key object not found"
102-
ls -la $RUNNER_TEMP
102+
KEY_INFO="$HOME/.appstoreconnect/private_keys/keyinfo.json"
103+
if ! [[ -f "$KEY_INFO" ]]; then
104+
echo "App Store Connect API Key info not found at $KEY_INFO"
105+
echo "::error title=App Store Connect API Key info not found::App Store Connect API Key info not found"
106+
ls -la ~/.appstoreconnect/private_keys/ 2>/dev/null || echo "Directory does not exist"
103107
exit 1
104108
fi
109+
echo "App Store Connect API Key info found at $KEY_INFO"

‎.github/workflows/linter.yml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,4 +46,5 @@ jobs:
4646
TYPESCRIPT_DEFAULT_STYLE: prettier
4747
VALIDATE_ALL_CODEBASE: true
4848
VALIDATE_JAVASCRIPT_STANDARD: false
49+
VALIDATE_TYPESCRIPT_STANDARD: false
4950
VALIDATE_JSCPD: false

‎README.md‎

Lines changed: 137 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,47 @@
66
[![CodeQL](https://github.com/actions/typescript-action/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/actions/typescript-action/actions/workflows/codeql-analysis.yml)
77
[![Coverage](./badges/coverage.svg)](./badges/coverage.svg)
88

9-
This action sets up a macOS runner for code signing. It's in the early stages of
10-
development and is not yet ready for production use.
9+
This action sets up a macOS runner for code signing. It's in the early stages
10+
of development and is not yet ready for production use.
1111

12-
| Asset Type | Support |
13-
| ------------------------------------------------------------------------------------------------------------------------------------- | ------- |
14-
| Developer Certificate | ✅ |
15-
| [App Store Connect API Key](https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api) | ✅ |
16-
| Provisioning Profile | ❌ |
12+
| Asset Type | Support |
13+
| ---------- | ------- |
14+
| Developer Certificate | ✅ |
15+
| [App Store Connect API Key][api-key-docs] | ✅ |
16+
| Provisioning Profile | ❌ |
1717

18-
# Sample Workflow
18+
[api-key-docs]: https://developer.apple.com/documentation/appstoreconnectapi/creating_api_keys_for_app_store_connect_api
1919

20-
Apple introduced
21-
[managed signing at WWDC21](https://developer.apple.com/videos/play/wwdc2021/10204/).
22-
By setting up the runner with an App Store Connect API Key, you can use managed
23-
signing to sign your app. No provisioning profile is required.
20+
For an overview of the automated and manual code signing process, see
21+
[Apple Code Signing Overview](apple_codesigning_overview.md).
2422

25-
> [!NOTE]
26-
> Apple Developer Enterprise accounts do not support App Store Connect API keys.
23+
## Sample Workflow
24+
25+
**Note:** Apple Developer Enterprise accounts do not support App Store Connect
26+
API keys.
27+
28+
**Important:** "Automatically manage signing" in Xcode refers to how Xcode
29+
behaves when signed into a developer account through the GUI. This does not
30+
apply to `xcodebuild`, which requires explicit API authorization via the
31+
`-allowProvisioningUpdates` flag and authentication parameters, regardless
32+
of the Xcode project's signing configuration.
33+
34+
Both the API key and development certificate are required for automated
35+
signing. The App Store Connect API key enables `xcodebuild` to communicate
36+
with Apple's servers to download provisioning profiles remotely. However, if
37+
a development certificate is not installed in the keychain before `xcodebuild`
38+
runs, it will automatically create a new certificate in the Developer Portal.
39+
Installing the certificate first ensures `xcodebuild` uses your existing
40+
certificate for signing while still leveraging the API key for remote
41+
provisioning profile management.
42+
43+
The action persists the App Store Connect API key to a file on the runner
44+
because `xcodebuild` requires the `-authenticationKeyPath` parameter to point
45+
to a file path—it does not accept API credentials via environment variables.
46+
47+
## Streamlined Setup (Recommended)
48+
49+
For the common case where you need both API key and certificate:
2750

2851
```yml
2952
name: build with automatic signing
@@ -39,32 +62,111 @@ jobs:
3962
runs-on: macos-latest
4063
steps:
4164
- uses: actions/checkout@v4
65+
- uses: nodeselector/setup-apple-codesign@v0.0.2
66+
with:
67+
# Certificate (PEM or P12 - P12 must be base64 encoded)
68+
secret-value: ${{ secrets.CERTIFICATE_PEM_OR_P12 }}
69+
# Optional: password for encrypted certificates
70+
certificate-password: ${{ secrets.CERT_PASSWORD }}
71+
# App Store Connect API credentials
72+
app-store-connect-api-key-key-id: ${{ secrets.ASC_KEY_ID }}
73+
app-store-connect-api-key-issuer-id: ${{ secrets.ASC_ISSUER_ID }}
74+
app-store-connect-api-key-base64-private-key: ${{ secrets.ASC_KEY }}
75+
# This example shows xcodebuild invocation for illustration.
76+
# You may use a different action/API to invoke xcodebuild, but ensure
77+
# the necessary flags are set to support automated code signing.
78+
- name: Build with xcodebuild
79+
run: |
80+
xcodebuild -project helloworld.xcodeproj \
81+
CODE_SIGN_STYLE=Automatic \
82+
DEVELOPMENT_TEAM="2KP9M7XQZN" \
83+
-scheme helloworld \
84+
-sdk iphoneos \
85+
-configuration Debug \
86+
-allowProvisioningUpdates \
87+
-authenticationKeyID ${{ secrets.ASC_KEY_ID }} \
88+
-authenticationKeyPath '/path/to/AuthKey.p8' \
89+
-authenticationKeyIssuerID ${{ secrets.ASC_ISSUER_ID }} \
90+
-derivedDataPath build \
91+
build
92+
# Additional steps: archive, export, etc.
93+
```
94+
95+
## Advanced: Separate Setup Steps
96+
97+
For more control, you can set up the API key, certificate, and provisioning
98+
profile separately:
99+
100+
```yml
101+
name: build with manual signing (separate steps)
102+
103+
on:
104+
push:
105+
pull_request:
106+
branches:
107+
- main
108+
109+
jobs:
110+
test:
111+
runs-on: macos-latest
112+
steps:
113+
- uses: actions/checkout@v4
114+
# Set up App Store Connect API key
42115
- uses: nodeselector/setup-apple-codesign@v0.0.2
43116
with:
44117
asset-type: "app-store-connect-api-key"
45-
secret-value: ${{ secrets.APP_STORE_CONNECT_API_KEY_KEY }}
118+
app-store-connect-api-key-key-id: ${{ secrets.ASC_KEY_ID }}
119+
app-store-connect-api-key-issuer-id: ${{ secrets.ASC_ISSUER_ID }}
120+
app-store-connect-api-key-base64-private-key: ${{ secrets.ASC_KEY }}
121+
# Set up development certificate
46122
- uses: nodeselector/setup-apple-codesign@v0.0.2
47123
with:
48124
asset-type: "certificate"
49-
secret-value: ${{ secrets.CODE_SIGNING_CERTIFICATE_DEVELOPMENT_PEM }}
50-
- uses: nodeselector/xcodebuild@v0.0.2
51-
with:
52-
action: 'archive'
53-
scheme: "helloworld"
54-
project: "helloworld.xcodeproj"
55-
archive-path: "build/helloworld.xcarchive"
56-
destination: "generic/platform=iOS"
57-
allow-provisioning-updates: true
58-
- uses: nodeselector/xcodebuild@v0.0.2
59-
id: export
60-
with:
61-
action: 'export'
62-
archive-path: "build/helloworld.xcarchive"
63-
allow-provisioning-updates: true
64-
export-method: "ad-hoc"
65-
- uses: nodeselector/xcodebuild@v0.0.2
125+
secret-value: ${{ secrets.CERTIFICATE_PEM_OR_P12 }}
126+
certificate-password: ${{ secrets.CERT_PASSWORD }}
127+
# Optional: Install provisioning profile to host machine
128+
# Required for manual signing without -allowProvisioningUpdates
129+
- uses: nodeselector/setup-apple-codesign@v0.0.2
66130
with:
67-
action: 'upload'
68-
product-name: "helloworld"
69-
export-path: ${{ steps.export.outputs.export-path }}
131+
asset-type: "provisioning-profile"
132+
secret-value: ${{ secrets.PROVISIONING_PROFILE_BASE64 }}
133+
- name: Build with xcodebuild
134+
run: |
135+
# Option 1: Use locally installed provisioning profile
136+
# Profile must be installed via the provisioning-profile asset-type
137+
# The -allowProvisioningUpdates flag is unnecessary (and is a no-op)
138+
xcodebuild -project helloworld.xcodeproj \
139+
CODE_SIGN_STYLE=Manual \
140+
CODE_SIGN_IDENTITY="Apple Development" \
141+
PROVISIONING_PROFILE_SPECIFIER="my-ios-profile" \
142+
-scheme helloworld \
143+
-sdk iphoneos \
144+
-configuration Debug \
145+
-derivedDataPath build \
146+
build
147+
148+
# Option 2: Fetch provisioning profile remotely via App Store Connect
149+
# Uses -allowProvisioningUpdates with API authentication flags
150+
# xcodebuild -project helloworld.xcodeproj \
151+
# CODE_SIGN_STYLE=Manual \
152+
# PROVISIONING_PROFILE_SPECIFIER="my-ios-profile" \
153+
# DEVELOPMENT_TEAM="2KP9M7XQZN" \
154+
# -scheme helloworld \
155+
# -sdk iphoneos \
156+
# -configuration Debug \
157+
# -allowProvisioningUpdates \
158+
# -authenticationKeyID ${{ secrets.ASC_KEY_ID }} \
159+
# -authenticationKeyPath '/path/to/AuthKey.p8' \
160+
# -authenticationKeyIssuerID ${{ secrets.ASC_ISSUER_ID }} \
161+
# -derivedDataPath build \
162+
# build
70163
```
164+
165+
For manual signing or to specify a provisioning profile, several required and
166+
optional flags can be passed to `xcodebuild`. See the
167+
[Apple Code Signing Overview](apple_codesigning_overview.md) for detailed
168+
configuration options including:
169+
170+
- Using `CODE_SIGN_STYLE=Manual` with `PROVISIONING_PROFILE_SPECIFIER`
171+
- Installing provisioning profiles locally vs. fetching remotely
172+
- Certificate and profile management considerations

0 commit comments

Comments
 (0)