Automate this: /mobile-release:ship-beta runs this skill end to end and uploads the result;
/mobile-release:bootstrap sets up signing and CI from nothing. /mobile-release:doctor diagnoses a
broken signing setup. What an agent may do unattended here: skills/release-automation.
Produce a signed, verified .ipa from an iOS source tree. Stop at a verified artifact on disk —
this skill does not upload. Hand off to the app-store-publish skill for TestFlight and the
App Store.
This skill requires macOS with Xcode installed. There is no Linux, Docker, or WSL path to a
signed .ipa. If uname -s does not print Darwin, stop and say so — see Confirm the host.
Process:
- Confirm the host is macOS with a real Xcode, and pin the version — Confirm the host below.
- Discover the project — workspace vs project, scheme, configuration, resolved build settings,
every embedded bundle — using
references/discovery.md. - Set the version and build number using
references/versioning.md.CURRENT_PROJECT_VERSIONmust be strictly higher than every build already uploaded for this marketing version. - Establish what is signing the build: certificates, profiles, entitlements, and automatic vs
manual signing —
references/signing-concepts.md. - Set up the App Store Connect API key using
references/asc-api-key.md. Copy the environment variable names character-for-character; a near-miss fails three steps later. - On CI, create the keychain and import the identity using
references/ci-keychain.md. Run that sequence exactly — it is what fastlane runs. - If the team shares signing material across machines, use
references/match.md. Read its §2 first: adoptingmatchrequires revoking your existing certificates. - Archive and export with
references/archive-export.md. Confirm themethodvalue againstxcodebuild -helpon this machine — never a hardcoded list. - Verify the
.ipawithreferences/verification.mdbefore handing it anywhere. Never report a build as done without quoting theAuthority=line. - To run all of this in CI, use the workflow in
references/github-actions.md. On any failure, look the error up inreferences/troubleshooting.mdbefore changing the build.
Load only the reference a step actually needs.
Contents
- Confirm the host
- Discover the project
- Archive and export
- Verify before upload
- Troubleshooting
- Core Instructions
- Confidence and freshness
- Output Format
- References
Confirm the host
uname -s # must print: Darwin
sw_vers # macOS product and build version
xcode-select -p # must be an Xcode.app path, not /Library/Developer/CommandLineTools
xcodebuild -version # e.g. Xcode 16.4
xcode-select -p printing /Library/Developer/CommandLineTools means only the Command Line Tools
are installed; the CLT cannot archive. Pin the Xcode version on CI with
sudo xcode-select -s /Applications/Xcode_16.4.app — macos-latest rotates its default without
notice, and a rotation changes both the SDK and the method values -exportArchive accepts.
If the host is Linux, say so and stop. The options are a macOS CI runner
(references/github-actions.md), Xcode Cloud, or a Mac over SSH. Still doable from Linux: edit the
project, write the ExportOptions.plist and the CI workflow, compute the next build number against
the App Store Connect API, prepare secrets. None of it produces a binary.
Discover the project
ls -d *.xcworkspace *.xcodeproj 2>/dev/null # a Podfile or .xcworkspace means use -workspace
xcodebuild -list -json -workspace MyApp.xcworkspace
xcodebuild -showBuildSettings -json \
-workspace MyApp.xcworkspace -scheme MyApp -configuration Release \
-destination 'generic/platform=iOS' > /tmp/buildsettings.json
Read out of those: PRODUCT_BUNDLE_IDENTIFIER, DEVELOPMENT_TEAM, CODE_SIGN_STYLE,
CODE_SIGN_IDENTITY, PROVISIONING_PROFILE_SPECIFIER, CODE_SIGN_ENTITLEMENTS,
MARKETING_VERSION, CURRENT_PROJECT_VERSION, DEBUG_INFORMATION_FORMAT. Two things fail a build
before signing is reached: the scheme must be shared (in xcshareddata/xcschemes/, or CI sees
no scheme at all) and its Archive action must use Release. Enumerate every embedded bundle —
widgets, extensions, watch app, App Clip — before going further; under manual signing each needs its
own profile and the same version and build number.
Archive and export
set -euo pipefail; set -o pipefail
NSUnbufferedIO=YES xcodebuild \
-workspace MyApp.xcworkspace -scheme MyApp -configuration Release \
-destination 'generic/platform=iOS' \
-archivePath "$PWD/build/MyApp.xcarchive" \
CURRENT_PROJECT_VERSION="$BUILD_NUMBER" \
archive 2>&1 | tee build/archive.log | xcbeautify
NSUnbufferedIO=YES xcodebuild -exportArchive \
-archivePath "$PWD/build/MyApp.xcarchive" \
-exportPath "$PWD/build/export" \
-exportOptionsPlist "$PWD/ExportOptions.plist" \
-allowProvisioningUpdates \
-authenticationKeyPath "$PWD/private_keys/AuthKey_ABC1234567.p8" \
-authenticationKeyID ABC1234567 \
-authenticationKeyIssuerID 6053b7fe-68a8-4acb-89be-165aa6465141 \
2>&1 | tee build/export.log | xcbeautify
-destination 'generic/platform=iOS' is what makes this a device archive rather than a simulator
build. set -o pipefail is not optional — without it a failed build reports xcbeautify's exit
status and looks green. The three -authenticationKey* flags must be passed together and are the
only supported way to use -allowProvisioningUpdates with no signed-in Xcode account.
The method key is the most common export failure. Accepted values depend on the installed
Xcode, and the old names (app-store, ad-hoc, development) are deprecated aliases of new ones
(app-store-connect, release-testing, debugging). Read the list off the machine with
xcodebuild -help | grep -A6 -i 'method' rather than trusting any document, this one included. The
complete ExportOptions.plist, manual and automatic, plus the key reference:
references/archive-export.md.
Verify before upload
unzip -q build/export/MyApp.ipa -d /tmp/v && APP="$(echo /tmp/v/Payload/*.app)"
codesign -dv --verbose=4 "$APP" # Authority, Identifier, TeamIdentifier
codesign --verify --deep --strict --verbose=2 "$APP"
codesign -d --entitlements :- "$APP"
security cms -D -i "$APP/embedded.mobileprovision" | plutil -p -
/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$APP/Info.plist"
ls build/MyApp.xcarchive/dSYMs/ # dSYMs are NOT inside the .ipa
Pass criteria: Authority=Apple Distribution: … (not Apple Development), chain ending at
Apple Root CA, no ProvisionedDevices in the profile, get-task-allow false, bundle ID identical
in all three places, every embedded bundle on the same version and build, dSYM UUIDs matching the
shipped binaries. Full command set and a one-shot script: references/verification.md.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| "No profiles for 'com.example.app' were found" | No installed profile matches this bundle ID for this profile type — often the host app has one and an extension does not | List installed profiles and compare their application-identifier against every bundle in the archive. Install the missing profile, or fastlane match appstore. To let Xcode create it, -allowProvisioningUpdates plus the -authenticationKey* trio |
| "Command CodeSign failed with a nonzero exit code" | A wrapper message, never the cause | Read the raw log a few lines above it, not the xcbeautify output — the real error is one of the rows below |
errSecInternalComponent |
The keychain will not release the private key to codesign |
Run the ci-keychain.md §2 sequence. The missing line is almost always security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$PW" "$KEYCHAIN" |
| Build hangs at signing until the job times out | Same cause, expressed as a blocking GUI prompt on a headless machine | Identical fix. Never respond to this by raising the timeout |
| Keychain locked partway through the archive | Default idle timeout is shorter than the build | security set-keychain-settings -lut 21600 "$KEYCHAIN", and security unlock-keychain immediately before building |
| Wrong team ID in the signed binary | Several teams visible to the account; DEVELOPMENT_TEAM empty or teamID absent from ExportOptions |
Set DEVELOPMENT_TEAM on the archive and teamID in ExportOptions.plist; confirm with the TeamIdentifier= line from codesign -dv --verbose=4 |
| "…doesn't match the bundle identifier" | Bundle ID differs between target, profile, and App Store Connect record | Compare Info.plist CFBundleIdentifier, codesign -dv Identifier=, and the profile's application-identifier. They are case-sensitive, and the ASC record's ID cannot be changed |
| "Provisioning profile 'X' doesn't include the … entitlement" | The .entitlements claims a capability the profile does not carry. Enabling it in the portal does not update existing profiles |
Enable the capability on the App ID, then regenerate the profile (match appstore --force) and reinstall. The binary's entitlements must be a subset of the profile's |
| "The bundle version must be higher than the previously uploaded version" | CFBundleVersion was used before for this marketing version — including for a rejected or deleted build |
Raise the build number and rebuild. Build numbers are never reusable, and the number is inside the signed binary |
| "exportOptionsPlist error for key 'method'" | The method value is not in this Xcode's vocabulary |
xcodebuild -help | grep -A6 -i method and use a value from that output |
Full error strings, the five-command triage, and what to do when the error is not listed:
references/troubleshooting.md.
Core Instructions
- Never report a build as done without quoting the
Authority=line fromcodesign -dv. Report the bundle ID, version, build number, signing identity and team ID every time. - Use manual signing for release builds. Automatic signing is permitted to create and rotate your team's certificates, and a team gets exactly one Apple Distribution certificate.
- Keep
destinationasexportinExportOptions.plist, neverupload, andmanageAppVersionAndBuildNumberasfalse. Both make the shipped binary differ from the one you verified, and the upload leg belongs toapp-store-publish. - Archive the
dSYMs/directory alongside the.ipa. It is not inside the.ipa, and without it this build's crash reports can never be symbolicated. - Never print a
.p8, a.p12,MATCH_PASSWORD, or a keychain password into the transcript or a CI log. The.p8is single-download: if it leaks, revoke it in App Store Connect immediately. - The repo
.gitignorealready blocks*.p8,AuthKey_*.p8,*.p12,*.cerand*.mobileprovision. That stops a commit, not a leak — signing material belongs in a secret store.
Confidence and freshness
Research date for this skill: 2026-08-05.
Confidence marks used throughout this skill and its references: [verified] — read off a primary vendor source on 2026-08-05. [snippet] — secondary source only, plausible but unconfirmed. [unverified] — not confirmed anywhere; run the check given before relying on it.
Solid: certificate types (Apple's own reference page); every security keychain command and flag
(read out of fastlane's create_keychain.rb and keychain_importer.rb); every fastlane environment
variable name (app_store_connect_api_key.rb, match/options.rb); API key creation and placement;
setup_ci's behaviour.
Not solid: the ExportOptions.plist method value list is [snippet] only — Apple publishes no
web copy of the xcodebuild man page and the mirror was proxy-blocked, so always run
xcodebuild -help. Also unverified: provisioning profile types and their one-year expiry (Apple's
page 404s), manageAppVersionAndBuildNumber, agvtool, match's subcommand list and the
match AppStore <bundle_id> naming, and all codesign output shapes. Nothing here was
executed — the authoring environment is Linux with no Xcode. Treat the first real run as the test.
Output Format
Report the artifact, the identity it was signed with, and the numbers someone needs before uploading. Never claim a build succeeded without a signing authority. State plainly what was not done.
Example output:
Release build — com.example.myapp
Artifact: build/export/MyApp.ipa — 42.1 MB
Version: 3.8.0 · Build: 413 (last upload for 3.8.0 was 412)
Toolchain: Xcode 16.4 (16F6) on macOS 15.5 · scheme MyApp, configuration Release
Signing — codesign -dv --verbose=4; codesign --verify --deep --strict exits 0:
Identifier=com.example.myapp
Authority=Apple Distribution: Example Ltd (ABCDE12345)
Authority=Apple Worldwide Developer Relations Certification Authority
Authority=Apple Root CA
TeamIdentifier=ABCDE12345
Profile: match AppStore com.example.myapp, expires 2027-06-14. No ProvisionedDevices,
get-task-allow false, aps-environment = production. The two embedded bundles
(…NotificationService, …WidgetExtension) each carry their own match AppStore profile, the same
team, and the same 3.8.0 (413).
Kept alongside the artifact: build/MyApp.xcarchive/dSYMs/ (3 dSYMs, 61 MB) — UUIDs match the
shipped binaries. DistributionSummary.plist and Packaging.log archived with the logs.
Not done: nothing was uploaded to App Store Connect and no TestFlight group was touched. The
.p8, the temporary keychain and the installed profiles were deleted in the cleanup step. Run the
app-store-publish skill against this .ipa for the upload.
End of example.
References
references/discovery.md— host and Xcode checks, what to do on Linux, workspace vs project,xcodebuild -list -json,-showBuildSettings, shared schemes, enumerating embedded bundles.references/versioning.md—MARKETING_VERSIONandCURRENT_PROJECT_VERSION, why build numbers must increase,agvtool, four ways to drive the build number from CI.references/signing-concepts.md— certificate types, provisioning profile types, whatcodesignchecks, entitlements in three places, automatic vs manual signing.references/asc-api-key.md— creating the.p8, team vs individual keys, where the file must live, and the exact flag and environment variable names for xcodebuild, altool, notarytool and fastlane.references/ci-keychain.md— the fullsecurity create-keychain→set-key-partition-listsequence line by line, installing profiles,fastlane setup_ci, producing the.p12, cleanup.references/match.md— storage backends, theMatchfile, every option and env var,match nuke, when to usecert+sighinstead, a complete CI lane.references/archive-export.md— the full archive and export invocations, themethodtrap, completeExportOptions.plistfor manual and automatic signing, key reference.references/verification.md—codesign -dv, entitlement subset checks, decoding the embedded profile, per-bundle version checks, dSYM UUIDs, a one-shot verify script.references/github-actions.md— completemacos-latestjob: secrets, keychain, profiles, archive, export, verify, artifacts, cleanup.references/troubleshooting.md— five-command triage plus the full error table.