git clone https://github.com/greenstevester/fastlane-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/greenstevester/fastlane-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/match" ~/.claude/skills/greenstevester-fastlane-skill-match && rm -rf "$T"
skills/match/SKILL.mdCode Signing with Match
Set up Fastlane Match to manage iOS code signing certificates and provisioning profiles in a shared Git repository.
Pre-flight Checks
- Fastlane installed: !
fastlane --version 2>/dev/null | grep "fastlane " | head -1 || echo "✗ Not installed - run: brew install fastlane" - Fastfile exists: !
ls fastlane/Fastfile 2>/dev/null && echo "✓ Found" || echo "✗ Not found - run /setup-fastlane first" - Existing Matchfile: !
ls fastlane/Matchfile 2>/dev/null && echo "✓ Already configured" || echo "○ Not configured yet" - Git available: !
git --version 2>/dev/null | head -1 || echo "✗ Git not installed"
Arguments: ${ARGUMENTS:-setup}
What is Match?
Match stores your iOS certificates and provisioning profiles in a private Git repository, encrypted with a passphrase. Benefits:
- Team sharing: Everyone uses the same certificates (no more "works on my machine")
- CI/CD ready: Clone the repo in your pipeline, no manual cert management
- Revoke protection: Prevents accidental certificate revocation
- Audit trail: Git history shows who changed what and when
Step 1: Create a Private Git Repository
Create a private repository to store your encrypted certificates:
# GitHub CLI (recommended) gh repo create certificates --private --clone cd certificates && cd .. # Or manually at github.com/new (select Private)
Repository naming conventions:
orcertificatesios-certificatesfastlane-certs{company}-signing
Security: This repo will contain encrypted certificates. Keep it private and limit access to team members who need to build the app.
Step 2: Initialize Match
Run match init to create your Matchfile:
fastlane match init
When prompted:
- Storage mode: Select
git - Git URL: Enter your private repo URL (e.g.,
)git@github.com:yourorg/certificates.git
This creates
fastlane/Matchfile:
git_url("git@github.com:yourorg/certificates.git") storage_mode("git") type("development") # Default type, can be overridden per-lane # app_identifier(["com.yourcompany.app"]) # Optional: limit to specific apps # username("user@example.com") # Optional: Apple ID
Step 3: Generate Certificates
Generate certificates for each distribution type:
Development (for debugging on devices)
fastlane match development
App Store (for TestFlight and App Store)
fastlane match appstore
Ad Hoc (for direct device distribution)
fastlane match adhoc
First run prompts:
- Apple ID: Your Apple Developer account email
- Passphrase: Create a strong passphrase to encrypt certificates (save this securely!)
Important: Save the passphrase in a password manager. You'll need it for CI/CD and new team members.
Step 4: Integrate with Fastfile
Update your lanes to use Match before building:
default_platform(:ios) platform :ios do desc "Sync all certificates" lane :sync_signing do match(type: "development") match(type: "appstore") end desc "Build for TestFlight" lane :beta do |options| match(type: "appstore", readonly: true) increment_build_number unless options[:skip_build_increment] gym(scheme: "YourApp", export_method: "app-store") pilot(skip_waiting_for_build_processing: true) end desc "Build for App Store" lane :release do match(type: "appstore", readonly: true) increment_build_number gym(scheme: "YourApp", export_method: "app-store") deliver(submit_for_review: false, force: true) end end
Key pattern: Use
readonly: true in build lanes to prevent accidental certificate regeneration.
Step 5: Onboard Team Members
New team members run:
# Clone and decrypt existing certificates (readonly) fastlane match development --readonly fastlane match appstore --readonly
They'll need:
- Access to the private certificates repository
- The Match passphrase
- Apple Developer team membership
Readonly mode ensures they can't accidentally revoke or regenerate certificates.
Step 6: CI/CD Setup
Set these environment variables in your CI/CD system:
# Required MATCH_PASSWORD="your-match-passphrase" MATCH_GIT_URL="git@github.com:yourorg/certificates.git" # For App Store Connect (choose one method) # Method 1: App-specific password FASTLANE_USER="your@appleid.com" FASTLANE_APPLE_APPLICATION_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx" # Method 2: API Key (recommended for CI) APP_STORE_CONNECT_API_KEY_ID="ABC123" APP_STORE_CONNECT_API_KEY_ISSUER_ID="xyz-xyz-xyz" APP_STORE_CONNECT_API_KEY_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----"
GitHub Actions Example
- name: Install certificates run: fastlane match appstore --readonly env: MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} MATCH_GIT_URL: ${{ secrets.MATCH_GIT_URL }}
Xcode Cloud
Add to
ci_scripts/ci_post_clone.sh:
# Install Fastlane and sync certificates brew install fastlane fastlane match appstore --readonly
Set
MATCH_PASSWORD in Xcode Cloud environment variables.
Troubleshooting
"Couldn't decrypt the repo"
Wrong passphrase. Verify
MATCH_PASSWORD is correct.
"Code signing error: No provisioning profiles"
Run
fastlane match appstore (without --readonly) to generate profiles.
"Your certificate has been revoked"
Someone revoked certs in Apple Developer portal. Regenerate:
fastlane match nuke development # Removes all development certs fastlane match development # Regenerates
"Multiple teams found"
Specify team in Matchfile:
team_id("ABCD1234")
"Unable to find app with bundle identifier"
Register the app first:
fastlane produce create -a com.yourcompany.app -n "Your App Name"
Match Commands Reference
# Setup fastlane match init # Create Matchfile fastlane match development # Generate dev certs fastlane match appstore # Generate App Store certs fastlane match adhoc # Generate Ad Hoc certs # Team use (readonly - won't modify certs) fastlane match development --readonly fastlane match appstore --readonly # Maintenance fastlane match nuke development # Revoke all dev certs fastlane match nuke distribution # Revoke all dist certs fastlane match change_password # Change encryption passphrase # Debugging fastlane match development --verbose # Detailed output
Security Best Practices
- Private repository: Never make the certificates repo public
- Strong passphrase: Use 20+ characters, store in password manager
- Limit access: Only team members who build should have repo access
- Rotate periodically: Change passphrase annually with
match change_password - API keys over passwords: Use App Store Connect API keys for CI/CD
Files Created
fastlane/ └── Matchfile # Match configuration # In your certificates repo: certs/ ├── development/ # Development certificates └── distribution/ # App Store/Ad Hoc certificates profiles/ ├── development/ # Development provisioning profiles ├── appstore/ # App Store provisioning profiles └── adhoc/ # Ad Hoc provisioning profiles