Current as of Fastlane 2.228.0
This guide explains how to manually import existing Apple Developer certificates into Fastlane Match without revoking them using Ruby's IRB (Interactive Ruby). This approach allows you to bring existing certificates and private keys under Match management, enabling team synchronization without disrupting existing development workflows.
This manual import process allows you to:
- Extract existing Apple Developer certificates using Spaceship
- Add them to your Match encrypted repository
- Maintain your current certificates without revocation
- Enable team members to sync using
fastlane match - Preserve all existing app signing capabilities
The key advantage is avoiding certificate revocation, which would break existing builds and require re-signing of deployed applications.
- Ruby and Fastlane installed
- Access to Apple Developer account
- Existing certificates and private keys in your Keychain
- Git repository for Match storage
- Match password for encryption
Begin by opening a terminal and starting an IRB session:
irbOnce in the IRB environment, execute the following steps:
# Start IRB session first, then require spaceship
require 'spaceship'
# Login to Apple Developer Portal (will prompt for Apple ID credentials)
Spaceship.login
# Or with specific Apple ID:
# Spaceship.login('[email protected]')
# Optional: Select team if you have multiple
# Replace YOUR_TEAM_ID with your actual team ID from Apple Developer Portal
# Spaceship.select_team(team_id: 'YOUR_TEAM_ID')# List all certificates to verify access and see available certificates
puts "=== ALL CERTIFICATES ==="
all_certificates = Spaceship.certificate.all
all_certificates.each do |cert|
puts "ID: #{cert.id}, Name: #{cert.name}, Type: #{cert.class}"
end
puts "\n=== DEVELOPMENT CERTIFICATES ==="
development_certificates = Spaceship.certificate.AppleDevelopment.all
development_certificates.each do |cert|
puts "ID: #{cert.id}, Name: #{cert.name}, Expires: #{cert.expires}"
end
# Find your specific certificate by ID
# Replace XXX with your actual certificate ID from the list above
certificate = Spaceship.certificate.AppleDevelopment.all.find { |cert| cert.id == "XXX" }
# Verify certificate details
puts "\n=== SELECTED CERTIFICATE ==="
puts "Found certificate: #{certificate.name}"
puts "Certificate ID: #{certificate.id}"
puts "Expires: #{certificate.expires}"require 'match'
# Set your Match configuration
# Replace XXX with your actual repository URL
git_url = '[email protected]:XXX/XXX.git'
shallow_clone = false
branch = 'master'
# Set encryption password
# Replace XXX with your actual Match encryption password
ENV["MATCH_PASSWORD"] = 'XXX'
# Initialize storage (Note: from_params method specific to Fastlane 2.228.0)
storage = Match::Storage.from_params({
storage_mode: 'git',
git_url: git_url,
shallow_clone: shallow_clone,
git_branch: branch,
clone_branch_directly: false
})
# Download the repository
storage.download# Configure encryption parameters
encryption_params = {
storage_mode: 'git',
git_url: git_url,
shallow_clone: shallow_clone,
git_branch: branch,
clone_branch_directly: false,
working_directory: storage.working_directory
}
# Initialize encryption and decrypt files
encryption = Match::Encryption.for_storage_mode('git', encryption_params)
encryption.decrypt_files if encryption# Get working directory paths
working_dir = storage.working_directory
puts "Working directory: #{working_dir}"
# Create necessary directories
certs_dir = File.join(working_dir, "certs", "development")
puts "Certificates directory: #{certs_dir}"
FileUtils.mkdir_p(certs_dir)
profiles_dir = File.join(working_dir, "profiles", "development")
puts "Profiles directory: #{profiles_dir}"
FileUtils.mkdir_p(profiles_dir)
# Get certificate ID for filenames
cert_id = certificate.id
key_name = certificate.name
puts "Certificate will be stored as: #{cert_id}.cer and #{cert_id}.p12"Before proceeding, you need to export your certificate and private key from Keychain Access:
- Open Keychain Access on your Mac
- Find your development certificate in "Login" keychain under "My Certificates"
- Select both the certificate and its private key
- Right-click and choose "Export 2 items..."
- Save as a .p12 file with no password (or remember the password if you set one)
- Also export just the certificate as a .cer file
Open a new terminal tab/window to copy files while keeping IRB running:
# Copy the certificate file (replace /path/to/your/ with actual paths)
cp /path/to/your/certificate.cer "/path/to/match/working/directory/certs/development/#{cert_id}.cer"
# Copy the private key file
cp /path/to/your/private_key.p12 "/path/to/match/working/directory/certs/development/#{cert_id}.p12"Note: Replace /path/to/match/working/directory/ with the actual path shown in your IRB session from step 5
# Update match_version.txt file
version_file = File.join(working_dir, "match_version.txt")
File.write(version_file, Fastlane::VERSION)
puts "Updated match_version.txt to: #{Fastlane::VERSION}"# Encrypt the files
encryption.encrypt_files if encryption
# Commit and push changes to repository
files_to_commit = [
File.join("certs", "development", "#{cert_id}.cer"),
File.join("certs", "development", "#{cert_id}.p12"),
"match_version.txt"
]
storage.save_changes!(files_to_commit: files_to_commit)
puts "Successfully committed certificate #{cert_id} to Match repository"# Clear temporary changes
if defined?(Match::GitHelper)
Match::GitHelper.clear_changes
end
puts "Certificate import completed successfully!"
puts "Team members can now use: fastlane match development"
# Exit IRB
exitHere's the complete script to run in IRB:
# Start in IRB session
require 'spaceship'
require 'match'
# 1. Authentication
Spaceship.login
# 2. Find certificate with listing
puts "=== AVAILABLE DEVELOPMENT CERTIFICATES ==="
Spaceship.certificate.AppleDevelopment.all.each do |cert|
puts "ID: #{cert.id}, Name: #{cert.name}"
end
# Replace XXX with your actual certificate ID from the list above
certificate = Spaceship.certificate.AppleDevelopment.all.find { |cert| cert.id == "XXX" }
# 3. Match configuration
# Replace XXX with your actual repository URL and password
git_url = '[email protected]:XXX/XXX.git'
shallow_clone = false
ENV["MATCH_PASSWORD"] = 'XXX'
branch = 'master'
# 4. Initialize storage and encryption
storage = Match::Storage.from_params({
storage_mode: 'git',
git_url: git_url,
shallow_clone: shallow_clone,
git_branch: branch,
clone_branch_directly: false
})
storage.download
encryption_params = {
storage_mode: 'git',
git_url: git_url,
shallow_clone: shallow_clone,
git_branch: branch,
clone_branch_directly: false,
working_directory: storage.working_directory
}
encryption = Match::Encryption.for_storage_mode('git', encryption_params)
encryption.decrypt_files if encryption
# 5. Prepare directories
working_dir = storage.working_directory
certs_dir = File.join(working_dir, "certs", "development")
FileUtils.mkdir_p(certs_dir)
cert_id = certificate.id
# 6. PAUSE: Manually copy your .cer and .p12 files to the certs_dir
puts "MANUAL STEP REQUIRED:"
puts "1. Export your certificate and private key from Keychain Access"
puts "2. Copy certificate.cer to: #{File.join(certs_dir, "#{cert_id}.cer")}"
puts "3. Copy private_key.p12 to: #{File.join(certs_dir, "#{cert_id}.p12")}"
puts "Press Enter in IRB after files are copied..."
gets # Wait for user confirmation
# 7. Update version and commit
version_file = File.join(working_dir, "match_version.txt")
File.write(version_file, Fastlane::VERSION)
encryption.encrypt_files if encryption
storage.save_changes!(files_to_commit: [
File.join("certs", "development", "#{cert_id}.cer"),
File.join("certs", "development", "#{cert_id}.p12"),
"match_version.txt"
])
# 8. Cleanup
if defined?(Match::GitHelper)
Match::GitHelper.clear_changes
end
puts "Certificate #{cert_id} successfully imported to Match!"
exit- Uses
Match::Storage.from_params()instead of newerfor_mode()method - Encryption uses
for_storage_mode()method - Git storage configuration follows 2.228.0 API patterns
- Ensure compatibility with your specific Fastlane version
- IRB Session: Keep the IRB session active throughout the entire process
- Private Key Access: Ensure you have the private key that matches your certificate in Keychain Access
- File Names: Certificate files must be named exactly as
{cert_id}.cerand{cert_id}.p12 - Encryption: The MATCH_PASSWORD must be the same as used by other team members
- Team Consistency: All certificates must be for the same Apple Developer Team
- Manual File Copy: You'll need to switch to terminal for file copying, then return to IRB
- If certificate methods fail, check your Spaceship version and Apple ID permissions
- If storage initialization fails, verify your git URL and branch exist
- If encryption fails, ensure MATCH_PASSWORD is set correctly
- If commit fails, check your git permissions and network connectivity
- If methods like
for_modedon't work, you may be on a different Fastlane version
This approach allows you to bring existing certificates under Match management without the disruption of revoking and regenerating certificates, using IRB for interactive control.