SanAn Blog

Part 1: Optimizing the Application Delivery Process: A Journey to Implementing CI/CD for Small Mobile Teams

Written by SanAn | Jul 9, 2025
Optimizing the Application Delivery Process: A Journey to Implementing CI/CD for Small Mobile Teams

 

I. Why do we need to implement CI/CD?

 

Our mobile team is small and operates in-house. All work from development, testing to distribution is discussed and handled directly between team members. App building is done on the local machine and then installed directly to testers via cable.

However, as the project expands, the need to share the build with testers in many regions, or send it to customers for review/accept before release becomes important. Continuing to build locally by installing directly becomes manual, does not guarantee the version, code quality and is time-consuming.

Therefore, setting up CI/CD to deliver builds quickly, synchronously, with clear versioning and easy to send to testers/customers is a must.

 

II. Actual conditions & implementation solutions

1. Current team conditions

Criteria

Describe

Source code management system

GitLab Enterprise

Development Platform

iOS and Android

Number of developers

< 5 people

Resource CI/CD

No private server to build

Current needs

Need to share quick build with tester/customer

Target

Build automation, reduce manual operations, versioning management, easy to maintain

2. Compare popular CI/CD options

Criteria

GitLab CI (self-hosted)

Bitrise

GitHub Actions + Fastlane

Mobile friendly

Have

Very easy

Good

Initial configuration

Difficult (requires separate runner)

Very suitable

Easy

Free Package

Unclear (self-hosting is resource intensive)

Limit 200 min/month

2000 minutes/month for private repo

Easy to maintain

Difficult if the team is small

Very easy

Easy

Automate cert/profile

Need to do it manually

Integrated

Good support with Match

TestFlight/Firebase Integration

Depending on configuration

Available

Can be easily integrated

3. Reasons to choose GitHub Actions + Fastlane


- With or without runner is fine → flexible for future scaling
- Easy to understand interface, full documentation
- Support detailed workflow, easy to customize
- Good integration with Fastlane → sync cert, build, upload TestFlight
- Free package is suitable for small teams, no need to upgrade in the first stage


4. Scope of implementation
- iOS: distribute builds to TestFlight
- Android: distribute builds to Firebase App Distribution

 

III. Overview of architecture and workflow with iOS

iOS CI/CD workflow with GitHub Actions:

Prerequisites for implementation:

  • Apple develop account Admin or Account Holder
  • GitLab repository to deploy has maintainer rights
  • GitHub repository (repository mirror) has maintainer permission
  • GitHub repository (repository lưu certificates) has maintainer permission

Detailed implementation steps:

1. Developer pushes code to gitLab


Devs push code as usual to GitLab (the project's main source control). This is the first step in the CI/CD chain without changing the team's habits.

2. GitLab triggers mirror code to GitHub


GitLab is configured to automatic mirror source code to GitHub repository every time there is a new commit.

  • First, setup the github project to mirror
    • On GitHub, create an empty repo (no README). In this article, I create the repo as follows: https://github.com/thevx-sanan/cicd.git
    • Generate Personal Access Token on GitHub

 

 

Copy the token you just created (note, this token is only displayed once so remember to save it carefully)
  • Add GITHUB_TOKEN to GitLab CI/CD variables
    • Access the GitLab repository to deploy CI/CD
    • Select Settings -> CI/CD -> Variables
    • Add variable:
- Key: GITHUB_TOKEN
- Value: Token from GitHub in the step above
- Select Masked
  • Next, setup .gitlab-ci.yml
    • Create .gitlab-ci.yml file

 

Finally, setup runner to run mirror
  • Install GitLab Runner (if not already present)
  • Register runner with GitLab

Runs on server or local (macOS, Linux, Windows are all fine):

 

You will be asked:

Prompt

Example answer

Enter the GitLab instance URL:

https://gitlab.comor self-hosted GitLab URL

Enter the registration token:

Get token inSettings > CI/CD > Runners

Enter a description for the runner:

mirror-runner

Enter tags for the runner (comma-separated):

mirror 

Enter executor:

shell

 

Note:

  • Mirror is an important tag, job in.gitlab-in.yml Using this tag to filter runners.
  • If you use multiple runners, this tag helps specify where the job should run.

Now, you can test by pushing the code on GitLab and monitor if there are updates on GitHub.

3. Fastlane runtest and build iOS app

After successfully mirroring the code from GitLab to GitHub, the GitHub Actions workflow will build and upload the iOS app to TestFlight via Fastlane. The entire process is divided into the following steps:

  • Create GitHub/GitLab repository to store certificates
    • Create a private repository named like: ios-certificates, In this example, we create the following repository: git@github.com:thevx-sanan/ios-certificates.git

  • This repo will be used by fastlane match to save:
    • Distribution certificate
    • Provisioning profile

Create bundle ID for test app, for example: com.sanan.setup.cicd

 

 

 

4. Distribute build to testers

Install Fastlane for project (macos):
  • If you haven't installed fastlane before, open up your terminal and type:

brew install fastlane

  • Then, open your GitLab repository folder and run terminal:

fastlane init

  • After init fastlane, we setup fastlane match:

fastlane match init

  • Then, select 1 (git), then enter the git url of the repository you have set up to save the config (remember to copy it in SSH format): git@github.com:thevx-sanan/ios-certificates.git

  • Next, in the folder create the fastlane folder and the files Gemfile, Gemfile.lock. Now, open the fastfile folder, then open Fastfile:

Next, open Appfile:

Next, open Matchfile: Edit your respective git_url and username

 

  • Test Fastlane on your local machine (macos):
    • Replace the corresponding values ​​and open the terminal in the setup folder:

export ASC_KEY_ID="<key_id_appstore_connect_api>"
export ASC_ISSUER_ID="<isuer_id_appstore_connect_api>"
export ASC_KEY_CONTENT="<content_appstore_connect_api>"
export MATCH_PASSWORD="<your_setup>"
export FASTLANE_APPLE_ID="<your_apple_id>"
export DEVELOPER_PORTAL_TEAM_ID="<your_apple_developer_account_id>"
export TEMP_KEYCHAIN_USER="<your_setup>"
export TEMP_KEYCHAIN_PASSWORD="<your_setup>"
 
Then run: bundle exec fastlane beta_prod
Successful run result:
Successfully uploaded the new binary to App Store Connect

  • Set the corresponding variables on GitHub:
    • Go to Settings -> Secrets and variables -> Actions and set the variables as above:

1. ACTIONS_DEPLOY_KEY
2. ASC_KEY_ID
3. ASC_ISSUER_ID
4. ASC_KEY_CONTENT
5. MATCH_PASSWORD
6. FASTLANE_APPLE_ID
7. DEVELOPER_PORTAL_TEAM_ID
8. TEMP_KEYCHAIN_USER
9. TEMP_KEYCHAIN_PASSWORD
Note: ACTIONS_DEPLOY_KEY is the private key when you setup regular ssh for the ios-certificates repository

  • Tạo Github Actions Workflow:
    • You can create directly on github or create directly from the repository folder:
      • Tạo thư folder .github -> tạo folder workflows -> new terminal at workflows -> touch fastlane.yml
      • Then, open the fastlane.yml file and we setup with this test project as follows:

Finally, push the code and test it. Go to the Actions tab.

 

IV. End of part 1

With the detailed instructions above, we have gradually implemented the CI/CD process for iOS projects from the real situation of a small team:

  • Maintain existing workflows on GitLab without changing dev habits
  • Automate TestFlight builds and distributions via GitHub Actions
  • Use Fastlane to manage certificates, build and upload apps securely and efficiently
  • Ensure testers and customers receive builds quickly, without manual operations

Adopting CI/CD not only saves time, but also increases stability and better control over distributions – especially as the number of members and testing environments starts to expand.

______________________________________________________________

Next?

In part 2, we will continue to extend this process to Android, use Firebase App Distribution Combine with Fastlane and GitHub Actions to create a similar distribution process for your Android app.

If you have never implemented CI/CD before, try applying this iOS part right in your personal project or staging – you will see a clear difference in your daily workflow.