Swift Tricks

My own, mostly internal blog of Swift tips and tricks

Using fastlane match with existing certificates without revoking them

Automatic

You can run fastlane match import command:

# Update type and app identifier as needed
fastlane match import --git_branch "your_branch" --type adhoc --readonly true --app_identifier "de.company.yourapp" --verbose

# Some other options you can set in command line or via the Matchfile
#git_url("git@gitlab.com:yourcompany/iOS-Certificates.git")
#git_branch("master")
#storage_mode("git")
#team_id("ABCD1234")
#type("appstore")

Manual method

Getting fastlane match up and running with existing certificates is not the most straightforward process. Fortunately, there is a great tutorial here.

In summary you’ll need to use irb ruby shell to manually add the files. Here’s the short version in commands (read the tutorial, the official docs, and the ruby gem docs for more info).

IMPORTANT! For multiple teams you need to store them on separate branches so in this case exchange master with whatever branch you want.

Steps

1. Export the appropriate cert and p12 files from your Keychain (by selecting Certificates and choosing the correct one(s)). Don’t add a password!

2. Download all required provisioning profiles from the Apple Developer Portal.

3. List all certificates for reference to find out certificate ids (you’ll need them for file names)

require 'spaceship'
Spaceship.login
Spaceship.select_team
Spaceship.certificate.all.each do |cert| 
  cert_type = Spaceship::Portal::Certificate::CERTIFICATE_TYPE_IDS[cert.type_display_id].to_s.split("::")[-1]
  puts "Cert id: #{cert.id}, name: #{cert.name}, expires: #{cert.expires.strftime("%Y-%m-%d")}, type: #{cert_type}"
end

4. Prepare the git repo

# Set vars (you might want to use https url instead if ssh doesn't work!)
require 'match'
git_url = 'git@gitlab.com:your/ios-certs-repo.git'
shallow_clone = false
ENV["MATCH_PASSWORD"] = 'password-to-decrypt-the-repo'
branch = 'your-branch'

# Download
storage = Match::Storage.for_mode('git', { git_url: git_url, shallow_clone: shallow_clone, git_branch: branch, clone_branch_directly: false})
storage.download

# Decrypt and get working dir
encryption = Match::Encryption.for_storage_mode('git', { git_url: git_url, working_directory: storage.working_directory})
encryption.decrypt_files

5. Now open up the folder (found under /var/folders/....., see the log under clone command) and add all the certificates in the proper folder structure. You’ll have /certs/development/*, /certs/profiles/*, and /certs/enterprise/* for the certificates and each needs to be named according to its certificate id (see step 3 above). Add both cert and p12 files (later without password).

6. Add provisioning profiles by placing them in /profiles/appstore/AppStore_com.bundle.id.mobileprovision, /profiles/development/Development_com.bundle.id.mobileprovision, and /profiles/inhouse/InHouse_com.bundle.id.mobileprovision appropriately. Files must be named as mentioned here, with proper bundle id and prefix.

7. Encrypt and commit the changes:

encryption.encrypt_files
files_to_commit = Dir[File.join(storage.working_directory, "**", "*.{cer,p12,mobileprovision}")]
storage.save_changes!(files_to_commit: files_to_commit, custom_message: 'Update match files')