11# KMP Publish iOS App on Firebase Action
22
3- This GitHub Action automates the process of building and publishing iOS applications to Firebase App Distribution. It handles the build process, signing, and deployment using Fastlane.
3+ This GitHub Action automates the process of building and publishing iOS applications to Firebase App
4+ Distribution. It handles the build process, signing, and deployment using Fastlane.
45
56## Features
67
@@ -25,15 +26,20 @@ Before using this action, ensure you have:
2526## Setup
2627
2728### SSH Key Setup
29+
28301 . Generate SSH Key Locally<br >
2931 In your terminal, run:
32+
3033``` yaml
3134ssh-keygen -t ed25519 -C "your_email@example.com"
3235```
36+
3337It will ask for a file path. Press enter a custom path like:
38+
3439``` yaml
3540~/.ssh/match_ci_key
3641```
42+
3743You can skip setting a passphrase when prompted (just hit enter twice).
3844
3945This generates two files:
@@ -44,11 +50,14 @@ This generates two files:
4450
45512 . Add the Private Key to the SSH Agent (optional but helpful)
4652 This step ensures the key is used during local development.
53+
4754``` yaml
4855eval "$(ssh-agent -s)"
4956ssh-add ~/.ssh/match_ci_key
5057```
58+
51593 . Add the Public Key to Your Certificates Repo (GitHub)
60+
5261- Go to your certificates repo on GitHub (e.g., openMF/ios-provisioning-profile).
5362- Go to Settings → Deploy Keys.
5463- Click “Add deploy key”.
@@ -57,22 +66,26 @@ ssh-add ~/.ssh/match_ci_key
5766``` yaml
5867cat ~/.ssh/match_ci_key.pub
5968```
69+
6070- Check ** Allow write access** .
6171- Click Add key.
6272
63734 . Convert the Private Key to Base64
6474 This is how we pass it to GitHub Actions securely.
75+
6576``` yaml
6677base64 -i ~/.ssh/match_ci_key | pbcopy
6778```
79+
6880This command copies the base64-encoded private key to your clipboard (macOS).
6981
70825 . Save the Private Key as a GitHub Secret
83+
7184- Go to the repo with your Fastfile (the project repo, not the certs repo).
7285- Navigate to Settings → Secrets and variables → Actions → New repository secret.
7386- Add the following:
74- - Name: MATCH_SSH_PRIVATE_KEY
75- - Value: Paste the base64-encoded private key
87+ - Name: MATCH_SSH_PRIVATE_KEY
88+ - Value: Paste the base64-encoded private key
7689
7790### Fastlane Setup
7891
@@ -91,60 +104,92 @@ Create a `fastlane/Fastfile` with the following content:
91104default_platform(:ios )
92105
93106platform :ios do
94- desc " Deploy to Firebase App Distribution"
95- lane :deploy_on_firebase do |options |
96-
97- firebase_app_id = options[:firebase_app_id ] || ENV [" FIREBASE_APP_ID" ]
98- match_type = options[:match_type ] || ENV [" MATCH_TYPE" ]
99- app_identifier = options[:app_identifier ] || ENV [" APP_IDENTIFIER" ]
100- git_url = options[:git_url ] || ENV [" GIT_URL" ]
101- git_branch = options[:git_branch ] || ENV [" GIT_BRANCH" ]
102- match_password = options[:match_password ] || ENV [" MATCH_PASSWORD" ]
103- git_private_key = options[:git_private_key ] || ENV [" GIT_PRIVATE_KEY" ] || " ./secrets/match_ci_key"
104- groups = options[:groups ] || ENV [" GROUPS" ]
105- serviceCredsFile = options[:serviceCredsFile ] || ENV [" SERVICE_CREDS_FILE" ]
107+ desc " Deploy iOS app to Firebase App Distribution"
108+ lane :deploy_on_firebase do |options |
109+ # === Variables at top ===
110+ firebase_app_id = options[:firebase_app_id ] || " 1:xxxxxx:ios:xxxxxxxx"
111+ match_type = options[:match_type ] || " adhoc"
112+ app_identifier = options[:app_identifier ] || " com.example.myapp"
113+ git_url = options[:git_url ] || " git@github.com:openMF/ios-provisioning-profile.git"
114+ git_branch = options[:git_branch ] || " master"
115+ match_password = options[:match_password ] || " your-default-match-password"
116+ git_private_key = options[:git_private_key ] || " ./secrets/match_ci_key"
117+ groups = options[:tester_groups ] || " qa-team,beta-testers"
118+ serviceCredsFile = options[:serviceCredsFile ] || " secrets/firebaseAppDistributionServiceCredentialsFile.json"
119+ provisioning_profile_name = options[:provisioning_profile_name ] || " match AdHoc com.example.myapp"
120+ appstore_key_id = options[:appstore_key_id ] || " DEFAULT_KEY_ID"
121+ appstore_issuer_id = options[:appstore_issuer_id ] || " DEFAULT_ISSUER_ID"
122+ key_filepath = options[:key_filepath ] || " secrets/Auth_key.p8"
106123
107- setup_ci(
108- provider: " circleci"
109- )
124+ # === Setup CI ===
125+ unless ENV [' CI' ]
126+ UI .message(" 🖥️ Running locally, skipping CI-specific setup." )
127+ else
128+ setup_ci(provider: " circleci" )
129+ end
110130
111- latest_release = firebase_app_distribution_get_latest_release(
112- app: firebase_app_id,
113- service_credentials_file: options[:serviceCredsFile ] || firebase_config[:serviceCredsFile ]
114- )
115-
116- if latest_release
117- increment_build_number(
118- xcodeproj: iosApp/ iosApp.xcodeproj,
119- build_number: latest_release[:buildVersion ].to_i + 1
120- )
121- else
122- UI .important(" ⚠️ No existing Firebase release found. Skipping build number increment." )
123- end
124-
125- match(
126- type: match_type,
127- app_identifier: app_identifier,,
128- readonly: true ,
129- git_url: git_url,
130- git_branch: git_branch,
131- git_private_key: git_private_key
132- )
131+ # === Load API Key ===
132+ app_store_connect_api_key(
133+ key_id: appstore_key_id,
134+ issuer_id: appstore_issuer_id,
135+ key_filepath: key_filepath,
136+ duration: 1200
137+ )
133138
134- build_ios_app(
135- scheme: " iosApp" ,
136- project: " iosApp/iosApp.xcodeproj" ,
137- output_name: " DeployIosApp.ipa" ,
138- output_directory: " iosApp/build" ,
139- )
139+ # === Fetch Match certs ===
140+ match(
141+ type: match_type,
142+ app_identifier: app_identifier,
143+ readonly: false ,
144+ git_url: git_url,
145+ git_branch: git_branch,
146+ git_private_key: git_private_key,
147+ force_for_new_devices: true ,
148+ api_key: Actions .lane_context[SharedValues ::APP_STORE_CONNECT_API_KEY ]
149+ )
140150
141- firebase_app_distribution(
142- app: firebase_app_id,
143- service_credentials_file: serviceCredsFile,
144- release_notes: r" New build from GitHub Actions" ,
145- groups: groups
146- )
147- end
151+ # === Increment build number from Firebase ===
152+ latest_release = firebase_app_distribution_get_latest_release(
153+ app: firebase_app_id,
154+ service_credentials_file: serviceCredsFile
155+ )
156+
157+ if latest_release
158+ increment_build_number(
159+ xcodeproj: " iosApp/iosApp.xcodeproj" ,
160+ build_number: latest_release[:buildVersion ].to_i + 1
161+ )
162+ else
163+ UI .important(" ⚠️ No existing Firebase release found. Skipping build number increment." )
164+ end
165+
166+ # === Build signed IPA ===
167+ build_ios_app(
168+ scheme: " iosApp" ,
169+ project: " iosApp/iosApp.xcodeproj" ,
170+ output_name: " DeployIosApp.ipa" ,
171+ output_directory: " iosApp/build" ,
172+ export_options: {
173+ provisioningProfiles: {
174+ app_identifier => provisioning_profile_name
175+ }
176+ },
177+ xcargs: " CODE_SIGN_STYLE=Manual CODE_SIGN_IDENTITY=\" Apple Distribution\" DEVELOPMENT_TEAM=L432S2FZP5 PROVISIONING_PROFILE_SPECIFIER=\" #{ provisioning_profile_name } \" "
178+ )
179+
180+ # === Generate release notes ===
181+ releaseNotes = changelog_from_git_commits(
182+ commits_count: 1
183+ )
184+
185+ # === Upload to Firebase ===
186+ firebase_app_distribution(
187+ app: firebase_app_id,
188+ service_credentials_file: serviceCredsFile,
189+ release_notes: releaseNotes,
190+ groups: groups
191+ )
192+ end
148193end
149194```
150195
@@ -153,60 +198,86 @@ end
153198Add the following workflow to your GitHub Actions:
154199
155200``` yaml
156- name : Deploy to Firebase
201+ name : Deploy iOS to Firebase
157202
158203on :
159204 push :
160205 branches : [ main ]
161- # Or trigger on release
162- release :
163- types : [created]
206+ # or:
207+ workflow_dispatch :
164208
165209jobs :
166- deploy :
210+ deploy_ios :
167211 runs-on : macos-latest
168212 steps :
169- - uses : actions/checkout@v3
170-
171- - name : Deploy to Firebase
172- uses : openMF/kmp-publish-ios-on-firebase-action@v1.0.0
213+ - name : Set Xcode version 16.2
214+ uses : maxim-lobanov/setup-xcode@v1
215+ with :
216+ xcode-version : latest-stable
217+
218+ - name : Checkout Repository
219+ uses : actions/checkout@v4
220+
221+ - name : Publish iOS App to Firebase
222+ uses : openMF/kmp-publish-ios-on-firebase-action@v1.0.2
173223 with :
174- app_identifier : ' org.mifos.kmp.template'
175- git_url : ' git@github.com:openMF/ios-provisioning-profile.git'
176- git_branch : ' master'
177- match_type : ' adhoc'
178- provisioning_profile_name : ' match AdHoc org.mifos.kmp.template'
179- match_password : ${{ secrets.MATCH_PASSWORD }}
180- match_ssh_private_key : ${{ secrets.MATCH_SSH_PRIVATE_KEY }}
181- ios_package_name : ' YourAppName'
182- firebase_creds : ${{ secrets.FIREBASE_CREDS }}
183- firebase_app_id : ' 1:xxxx:ios:xxxxxxxx'
184- tester_groups : ' qa-team,beta-testers'
224+ app_identifier : ' com.example.myapp'
225+ git_url : ' git@github.com:openMF/ios-provisioning-profile.git'
226+ git_branch : ' master'
227+ match_type : ' adhoc'
228+ provisioning_profile_name : ' match AdHoc com.example.myapp'
229+ match_password : ${{ secrets.MATCH_PASSWORD }}
230+ match_ssh_private_key : ${{ secrets.MATCH_SSH_PRIVATE_KEY }}
231+ appstore_key_id : ${{ secrets.APPSTORE_KEY_ID }}
232+ appstore_issuer_id : ${{ secrets.APPSTORE_ISSUER_ID }}
233+ appstore_auth_key : ${{ secrets.APPSTORE_AUTH_KEY }}
234+ ios_package_name : ' iosApp'
235+ firebase_app_id : ' 1:xxxxxx:ios:xxxxxxxx'
236+ firebase_creds : ${{ secrets.FIREBASE_CREDS }}
237+ tester_groups : ' qa-team,beta-testers'
185238` ` `
186239
187240## Inputs and Secrets
188241
189- | Input | Description | Required |
190- |--------------------|-----------------------------------------------------|----------|
191- | ` ios_package_name` | Name of your iOS app/scheme | Yes |
192- | `firebase_creds` | Base64 encoded Firebase service account credentials | Yes |
193- | `tester_groups` | Comma-separated list of Firebase tester groups | Yes |
194- | `MATCH_PASSWORD` | Used to encrypt/decrypt match certs | Yes |
195- | `MATCH_SSH_PRIVATE_KEY` | base64 encoded private SSH key used by Fastlane Match | Yes |
242+ | Input | Description | Required |
243+ |-----------------------------|-----------------------------------------------------|----------|
244+ | ` app_identifier` | The unique bundle identifier for the iOS app | ✅ |
245+ | `git_url` | Git URL to certificates/profiles repo | ✅ |
246+ | `git_branch` | Branch in certificates repo | ✅ |
247+ | `match_type` | Profile type (adhoc, appstore, development) | ✅ |
248+ | `provisioning_profile_name` | Name of provisioning profile | ✅ |
249+ | `match_password` | Password to decrypt Match certs | ✅ |
250+ | `match_ssh_private_key` | Base64-encoded SSH private key for Match | ✅ |
251+ | `appstore_key_id` | App Store Connect API key ID | ✅ |
252+ | `appstore_issuer_id` | App Store Connect issuer ID | ✅ |
253+ | `appstore_auth_key` | Base64-encoded `.p8` API key | ✅ |
254+ | `ios_package_name` | Name of the iOS package/module (Xcode scheme) | ✅ |
255+ | `firebase_app_id` | Firebase App ID | ✅ |
256+ | `firebase_creds` | Base64 encoded Firebase service account credentials | ✅ |
257+ | `tester_groups` | Firebase tester groups (comma-separated) | ✅ |
196258
197259# # Setting up Secrets
198260
1992611. Encode your Firebase credentials file to base64 :
262+
200263` ` ` bash
201264base64 -i path/to/firebase-credentials.json -o firebase-creds.txt
202265` ` `
203266
204- 2. Add the following secret to your GitHub repository :
267+ 2. Encode you r `.p8` API key to base64 :
268+ ` ` ` bash
269+ base64 -i path/to/AuthKey_XXXXXXXXXX.p8 | pbcopy
270+ ` ` `
271+
272+ 3. Add the following secret to your GitHub repository :
273+
205274- `FIREBASE_CREDS` : Content of firebase-creds.txt
275+ - `APPSTORE_AUTH_KEY` : Content of AuthKey_XXXXXXXXXX.p8
206276
207277# # Build Artifacts
208278
209279The action uploads the built IPA file as an artifact with :
280+
210281- Name : ' ios-app'
211282- Retention period : 1 day
212283- Maximum compression (level 9)
@@ -229,10 +300,10 @@ This helps reduce build times in subsequent runs.
229300Common issues and solutions :
230301
2313021. Build fails due to signing
232- - Ensure your Xcode project has proper signing configuration
233- - Verify the export method matches your provisioning profile
303+ - Ensure your Xcode project has proper signing configuration
304+ - Verify the export method matches your provisioning profile
234305
2353062. Firebase deployment fails
236- - Check if the service account has sufficient permissions
237- - Verify the Firebase app ID is correct
238- - Ensure tester groups exist in Firebase console
307+ - Check if the service account has sufficient permissions
308+ - Verify the Firebase app ID is correct
309+ - Ensure tester groups exist in Firebase console
0 commit comments