iOS: CI/CD Integration via FastLane & Firebase using Gitlab : Part – 2

14 / Aug / 2024 by Sandeep Kumar 0 comments

In the first part of the blog, we covered the Fastlane setup/Installation with multiple files like Appfile, Gymfile, and Fastfile. Now we are moving to the next part of this series where we will use CI/CD workflow via Fastlane & Firebase on the local machine.

We will use the next step of the Code Signing approach via the match import command. This will create a separate repository and upload your private keys and certificates in a Gitlab repo for syncing them across the machines. This approach is secure.

Code Signing approach:  Using Fastlane match

The concept of match command is described in the codesigning guide.

Import: Prepare Certificates Using Match(Only one-time setup)

To import and encrypt a certificate (.cer), the private key (.p12), and the provisioning profiles (.mobileprovision or .provisionprofile) into the match repo run:


$ fastlane match import

You will be prompted for the certificate (.cer), the private key (.p12), and the provisioning profiles (.mobileprovision or .provisionprofile) paths. match will first validate the certificate (.cer) against the Developer Portal before importing the certificate, the private key, and the provisioning profiles into the specified match repository.

However, if there is no access to the developer portal but there are certificates, private keys, and profiles provided, you can use the skip_certificate_matching option to tell match not to verify the certificates. Like this:


$ fastlane match import --skip_certificate_matching true

The default type is ‘development’. So we need to specify the type explicitly.


$ fastlane match import --type enterprise --skip_certificate_matching true

This step will skip login to the Apple Developer Portal and will import the provided certificate, private key, and profile directly to the certificates Gitlab repo.

We need to be careful when using this option and ensure the certificates and profiles match the type (development, adhoc, appstore, enterprise, developer_id) and are not revoked or expired. 

4. Matchfile- Please follow the steps while initializing the match command with prompted options.

Step-1. Initialize Match in your iOS project by running the command fastlane match init in the root directory of your project.

null

Step-2. Fastlane Match supports multiple storage modes and will ask you to select the one you want to use like options 1, 2, and so on. In this blog, We have selected option 1 which is ‘git‘.

Step-3. The system will ask you for the URL of the Git Repo then Enter the EMPTY GIT-CETIFICATE URL (it’s added on prerequisites new to provide a separate shared repository ).

Now Match file will be created on your root Directory-
null


 git_url("EMPTY GIT-CETIFICATE Repoistory")
 storage_mode("git")
 type("enterprise") # The default type, can be: appstore, adhoc, enterprise or development
 ENV["MATCH_PASSWORD"] = "MATCH_PASSWORD"

Setup Fastfile actions- In the Fastlfile Actions, We will set up all the actions like custom variables, and lanes to execute the set of tasks in a sequential manner.

Add the Below Script on the Fastlane File-
null


# This file contains the fastlane.tools configuration
# You can find the documentation at https://docs.fastlane.tools
#
# For a list of all available actions, check out
#
#     https://docs.fastlane.tools/actions
#
# For a list of all available plugins, check out
#
#     https://docs.fastlane.tools/plugins/available-plugins
#

# Uncomment the line if you want fastlane to automatically update itself
# update_fastlane

#Variables
APP_BUNDLE_ID = "com.test.iOS"
#APP_BUNDLE_ID_NSE = "com.test.iOS.Todo_ios-NSE"
#APP_BUNDLE_ID_NCE = "com.test.iOS.Todo_ios-NCE"

APP_PROVISION_TITLE = "AdhocDistribution"
#APP_PROVISION_TITLE_NSE = "Todo_ios Adhoc NSE"
#APP_PROVISION_TITLE_NCE = "Todo_ios Adhoc NCE"

#App Method support list
#["app-store", "validation", "ad-hoc", "package", "enterprise", "development", "developer-id", "mac-application"]
APP_METHOD = "enterprise"

# The default type, can be: appstore, adhoc, enterprise or development
MATCH_METHOD = "enterprise"

PROJECT_SCHEMA_QA = 'Todo_ios'
PROJECT_WORKSPACE = 'Todo_ios.xcworkspace'
PROJECT_XCODEPROJ = 'Todo_ios.xcodeproj'
IPA_FILE_NAME = "Todo_ios"
BUILD_PATH = "./builds"
INCLUDE_BITCODE = false
INCLUDE_SYMBOLS = false

FIREBASE_APP_ID = "FIREBASE_APP_ID"# i.e, "1:58453............"
TESTER_LIST = "test@gmail.com" # Add with coma separated
RELEASE_NOTES_FILE_PATH = "./fastlane/release-notes.txt"

MY_TEAM = "MY_TEAM" # i.e, "RT......"

GIT_URL_CERTIFICATES = "GIT_URL_CERTIFICATES"# i.e, "https:........./todo_ios_certificate"

#Fetch values from environmental variables
username = ENV['USERNAME']
personal_github_access_token = "PERSONAL_GITHUB_ACCESS_TOKEN" # i.e, "gl.........."
authorization_token_str = "#{username}:#{personal_github_access_token}"
basic_authorization_token = Base64.strict_encode64(authorization_token_str)

#Key chain passcode of MacOS machine
KEYCHAIN_PASSWORD = "KEYCHAIN_PASSWORD" # i.e, "Raj........."

default_platform(:ios)

platform :ios do

 desc "Sync certificates"
  lane :sync_certificates do
   #read-only disables match from overriding the existing certificates.
   match(type: MATCH_METHOD,
    app_identifier: APP_BUNDLE_ID,
    git_url: GIT_URL_CERTIFICATES,
    git_basic_authorization: basic_authorization_token,
    keychain_password: KEYCHAIN_PASSWORD,
    readonly: true,
    skip_certificate_matching: true,
    verbose: true)

 end
 
 
desc "Update project provisioning"
 lane :update_provisioning do
     
    CERTIFICATE_NAME = ENV["sigh_#{APP_BUNDLE_ID}_#{MATCH_METHOD}_certificate-name"]
    APP_PROVISION_PROFILE_PATH = ENV["sigh_#{APP_BUNDLE_ID}_#{MATCH_METHOD}_profile-path"]
     
    update_project_provisioning(
    xcodeproj: PROJECT_XCODEPROJ,
    profile: APP_PROVISION_PROFILE_PATH,
    build_configuration: "QA",
    code_signing_identity: CERTIFICATE_NAME
    )

 end
 
desc "Disable automatic code signing"
 lane :disable_auto_code_sign do
    # more advanced manual code signing
    update_code_signing_settings(
    use_automatic_signing: false,
    path: "./#{PROJECT_XCODEPROJ}",
    team_id: MY_TEAM,
    bundle_identifier: APP_BUNDLE_ID,
    code_sign_identity: "",
    profile_name: ""
    )
 end
     
 
 
desc "QA Build"
  lane :qa_build do
  
    APP_PROVISION_PROFILE_NAME = ENV["sigh_#{APP_BUNDLE_ID}_#{MATCH_METHOD}_profile-name"]
    #EXPORT_TEAM_ID  = ENV["sigh_#{APP_BUNDLE_ID}_#{MATCH_METHOD}_team-id"]
        
    gym(scheme: PROJECT_SCHEMA_QA,
    workspace: PROJECT_WORKSPACE,
    clean: true,
    silent: false,
    include_bitcode: INCLUDE_BITCODE,
    include_symbols: INCLUDE_SYMBOLS,
    xcargs: {
        BUNDLE_IDENTIFIER: APP_BUNDLE_ID,
        #PROVISIONING_PROFILE_SPECIFIER: APP_PROVISION_PROFILE_NAME,
        DEVELOPMENT_TEAM: MY_TEAM,
        CODE_SIGN_STYLE: "Manual"
    },
    export_xcargs: "-allowProvisioningUpdates",
    skip_profile_detection: true,
    export_options: {
        method: APP_METHOD,
        provisioningProfiles: {
         APP_BUNDLE_ID => APP_PROVISION_TITLE,
        }
    },
    configuration: 'Release',
    output_name: IPA_FILE_NAME,
    output_directory:BUILD_PATH,
    skip_codesigning: false,
    #export_team_id: EXPORT_TEAM_ID,
    verbose: true)
  end
  
  
desc "Increament build version"
  lane :increment_version do
    LATEST_VERSION = 0
  latest_release = firebase_app_distribution_get_latest_release(
    app: FIREBASE_APP_ID
  )
  
  if latest_release != nil
    LATEST_VERSION = latest_release[:buildVersion].to_i
  end
  increment_build_number({ build_number: LATEST_VERSION + 1 })
end
    
desc "clean"
  lane :clean do
    clear_derived_data(derived_data_path: BUILD_PATH)
  end
  
desc "Create ipa"
  lane :build do
    #clean derived data for path
    clean
    
    # Disable automatic code signing
    #disable_auto_code_sign
    
    #match certificate & profile
    sync_certificates
    
    #update project provisioning
    #update_provisioning
    
    # Creates a signed file
    qa_build
 end
end

Note: Please update your own detail in the above script i.e,.

APP_BUNDLE_ID,

APP_PROVISION_TITLE,

FIREBASE_APP_ID,

TESTER_LIST,

MY_TEAM,

GIT_URL_CERTIFICATES,

PERSONAL_GITHUB_ACCESS_TOKEN,

KEYCHAIN_PASSWORD.

Now run fastlane ios build command, 

Result:- The above command will generate the .ipa file using above configuration setup with the fastlane summary.  

null

Trouble Shoot:-

    1. Validate Bundle Identifiers on FastFile.
    2. Validate that Schema and Project names are Proper.
    3. Make Sure You have disabled automatic Sign-in and manually add profiles.
    4. Make sure that your iOS project is properly set up Locally on Xcode and you are able to archive and export to the app store by disabling automatic signing.

Upload to firebase

Next, you need to set up the Firebase App Distribution plugin. This tool allows you to upload to Firebase from Fastlane with an action. From the Terminal, run the following command to add the App Distribution plugin to your Fastlane installation:


$ fastlane add_plugin firebase_app_distribution

null

If for some reason, plugins are not installed, try again after disabling the VPN.

The next step is to log in to Firebase. Follow the steps mentioned on the website.

  https://firebase.google.com/docs/cli

Now open Fastfile in a text editor, and paste the code below fastlane lane:
null

 
desc "Upload build to Firebase" 
  lane :upload_build_to_firebase do 
  firebase_app_distribution( 
  ipa_path: "#{BUILD_PATH}/#{IPA_FILE_NAME}.ipa", 
  app: FIREBASE_APP_ID, testers: TESTER_LIST, 
  #groups: "", 
  #release_notes_file: RELEASE_NOTES_FILE_PATH, 
  release_notes: "Testing build upload") 
end

Also, add the code below the fastlane build lane
null

 
desc "Create ipa"
............
......

# Increases the build number by 1 
#increment_version
 
# Creates a signed file 
qa_build 

# Build upload to firebase 
upload_build_to_firebase

end

 

After all these setups, run the following command from Terminal: fastlane ios build. 

Result:- This will generate the .ipa file and upload to Firebase App Distribution on Firebase Console.

null

Uploaded build link:

null

Concluding that we have covered the CI/CD workflow via Fastlane & Firebase on the local machine in Part – 2. We’ll cover in the next part how to use CI/CD workflow via Fastlane & Firebase with Gitlab Actions. Stay tuned!

FOUND THIS USEFUL? SHARE IT

Leave a Reply

Your email address will not be published. Required fields are marked *