+3

[Android] Setup CI-CD for Android app using Fastlane and GitHub Actions

GitHub Actions là 1 nền tảng miễn phí do GitHub cung cấp để giúp chúng ta tự động hoá quá trình CI/CD, cho phép người dùng định nghĩa các workflow tự động hoá các hoạt động trong phát triển phần mềm.

Fastlane là một công cụ giúp cho việc release sản phẩm của chúng ta trở nên dễ dàng hơn , nhanh hơn .

Việc sử dụng Fastlane và Github Action giúp cho chúng ta setup CI/CD nhanh gọn và dễ hiểu.

Trong bài này, ta sẽ thực hiện theo các bước sau:

  1. Install fastlane and set up your Fastfile
  2. Configure your secrets in GitHub’s encrypted secrets
  3. Set up a basic GitHub Actions workflow .yml file
  4. Run your build!

1.Install fastlane and set up your Fastfile

  • Tuỳ thuộc vào OS mà bạn sử dụng mà có cách setup fastlane riêng. Ở đây mình dùng Homebrew (macOS), ngoài ra bạn có thể đọc tài liệu Fastlane để tham khảo cách setup khác.
  • Chạy lệnh sau trong Termial ở thư mục main app project:brew install fastlane
  • Sau đó chạy lệnh khởi tạo:fastlane init. Terminal sẽ show ra màn hình cài đặt, làm theo các bước trên đó là xong nha. (Điền package name, json secret file,... mấy cái này điền cũng đc, k điền thì sau này mở file Appfile lên điền sau cũng được). Khi chạy xong thì nó sẽ báo thành công, có thể chạy lệnh fastlane test để kiểm thử.
  • Sau lệnh khởi tạo trên thì project của mình sẽ có thư mục fastlane mới và một số file mới như Gemfile, Gemfile.lock:

Thư mục fastlane sẽ chứa những file: Appfile, Fastfile, Pluginfile. + Appfile: chứa thông tin config cho toàn app gồm json_key_file và package_name. Để hỗ trợ việc connect tới Google Play thì mình cần điền 2 thông tin này vào file. Lấy các thông tin này ở đâu và bằng cách nào thì các bạn tham khảo mục Setting up supply trong bài viết này nhé!

  • Fastfile: nơi định nghĩa các lane - điều khiển các hành vi của fastlane.
  • Pluginfile: nơi chứa các plugin cần thiết để định nghĩa ra các lane. Nếu muốn install plugin nào thì ta chỉ cần chạy lệnh sau:fastlane add_plugin [name]. Ví dụ: fastlane add_plugin fastlane-plugin-increment_version_code.

Rồi, bây giờ ta sẽ đi sâu vào file Fastfile để xem cách định nghĩa các lane. Dưới đây là ví dụ một file Fastfile:

default_platform(:android)

platform :android do
  desc "Build To App Distribution"
    before_all do
          clear_derived_data
        end

  lane :build_develop do
      gradle(
        task: 'assemble',
        flavor: 'Develop',
        build_type: 'Debug'
      )
  end

  lane :distribute_firebase do
      build_develop
      firebase_app_distribution(
        app: "1:xxx:android:xxx",
        groups: "test-group-name",
        apk_path: "app/build/outputs/apk/develop/debug/xxx.apk",
        release_notes_file: ".github/workflows/release-notes.txt",
        firebase_cli_token: "1//xxx-xxx-xxx"
      )
  end
  
end

-Fastfile trên gồm các lane build và distribute lên Firebase. Trong một lane, mở đầu bằng từ khoá lane và kết thúc bằng từ khoá end, ở đây có lane :distribute_firebase do thì cái distribute_firebase này là do mình tự đặt, tiếp đến là build_develop thì cái này là mình gọi tới một lane khác đã được định nghĩa bên trên, sau đó là firebase_app_distribution, đây là một action của lane đó, tham khảo thêm cho action firebase_app_distribution này tại đây.

2. Configure your secrets in GitHub’s encrypted secrets

  • Để lưu trữ các biến trong quá trình config CI/CD thì mình sẽ sử dụng GitHub’s encrypted secrets. Đây là một mục trong Setting của Github. Để truy cập vào mục này thì bạn phải có quyền Admin trên Github nhé. Mục setting đó trông như sau:

Để tạo một key mới thì bạn nhấn nút New repository secret, rồi sau đó nhập key-value vào thôi:

Github secrets chỉ chấp nhận value dạng string, với những value dạng file .json hoặc .jks thì mình phải chuyển nó về dạng base64-encoded string trước khi import lên nhé. Sử dụng cú pháp sau: base64 in_file_path | pbcopy.

Sau này khi muốn sử dụng các biến này cho fastlane hay Github Action thì ta sẽ gọi ra bằng cách:

Fastlane:

APP_ID = ENV['APP_ID']
lane :distribute_firebase do
        build_develop
        firebase_app_distribution(
          app: "#{APP_ID}",
          groups: "xxx",
          apk_path: "xxx",
          release_notes_file: "xxx",
          firebase_cli_token: "1//xxx"
        )
    end

Hoặc:

lane :distribute_firebase do
        build_develop
        firebase_app_distribution(
          app: ENV["APP_ID"],
          groups: "xxx",
          apk_path: "xxx",
          release_notes_file: "xxx",
          firebase_cli_token: "1//xxx"
        )
    end

Github Action:

- name: Build & deploy Android release
     run: bundle exec fastlane android deploy
     env:
       KEYSTORE_FILE: ${{ steps.android_keystore.outputs.filePath }}
       KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
       KEY_ALIAS: ${{ secrets.KEY_ALIAS}}
       KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
       ANDROID_JSON_KEY_FILE: ${{ steps.service_account_json_file.outputs.filePath }}

3. Set up a basic GitHub Actions workflow .yml file

Để tạo ra file workflow.yml file thì mình sẽ vào repo của github, sau đó vào mục Action trên thanh công cụ, tiếp theo là chọn button New workflow:

Sau đó màn hình tạo workflow mới được mở ra, mình chọn workflow tương ứng rồi click vào button Configure:

Sau đó thì một màn hình config được tạo ra, nó chứa file workflow.yml, file này được generate tự động, bạn cần chỉnh sửa nội dung bên trong sao cho phù hợp với flow của dự án của bạn:

Sau khi chỉnh sửa nội dung xong thì bạn có thể tạo commit vào nhánh mà bạn muốn.

Mình đi sâu vào nội dung của file này nhé. Dưới đây là ví dụ một file workflow mà mình tạo ra:

name: Android CI

on:
  push:
    branches:
      - '**'        # matches every branch

jobs:
  test:
    name: Run Unit Tests
    runs-on: ubuntu-latest

    steps:
      - name: Checkout the code
        uses: actions/checkout@v1

      - name: set up JDK 11
        uses: actions/setup-java@v1
        with:
          java-version: '11'

      - name: Unit tests
        run: ./gradlew testDevelopDebugUnitTest --stacktrace

  build:
    name: Build
    needs: [ test ]
    runs-on: ubuntu-latest

    steps:
      - run: echo "The job was automatically triggered by a ${{ github.event_name }} event."
      - uses: actions/checkout@v3

      - name: set up JDK 11
        uses: actions/setup-java@v3
        with:
          java-version: '11'
          distribution: 'temurin'
          cache: gradle

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Build with Gradle (dev debug)
        run: ./gradlew buildDevelopDebugPreBundle --stacktrace

      - run: echo "Build status report=${{ job.status }}."

  develop:
    name: Deploy on branch Develop
    needs: [ build ]
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    container: alvrme/alpine-android:android-33-jdk11

    steps:
      - uses: actions/checkout@v2

      - name: Cache Gradle
        uses: actions/cache@v1
        with:
          path: ~/.gradle/caches/
          key: cache-clean-gradle-${{ matrix.os }}-${{ matrix.jdk }}

      - name: Cache Gradle Wrapper
        uses: actions/cache@v1
        with:
          path: ~/.gradle/wrapper/
          key: cache-clean-wrapper-${{ matrix.os }}-${{ matrix.jdk }}

      - name: Grant Permission to Execute
        run: chmod +x gradlew

      - name: Build debug APK
        run: ./gradlew assembleDevelopDebug

      - name: Upload APK
        run: |
          extras fastlane
          fastlane android distribute_firebase
  • name: Android CI: Cục này là đặt tên cho flow thôi, không có gì.
  • on: : Cục này là dành cho config nhánh, xem các jobs của workflow này sẽ chạy trên nhánh nào của github. Ở đây mình để nó chạy trên tất cả các nhánh. Vậy để chạy trên một nhánh nào đó thì config như nào, như này nhé:

on:
  push:
    branches: [ "develop" ]
  pull_request:
    branches: [ "develop" ]
  • jobs: : Cục này bao gồm tất cả các jobs của workflow. Ở đây có các jobs là test, build, develop. Trong mỗi jobs sẽ có các thành phần khác nhau, ví dụ nhìn vào job develop, ta có:
  • name: tên của jobs. Ở đây tự đặt là Deploy on branch Develop.
  • needs: cái này là điều kiện cần thiết để jobs này có thể chạy được, ở đây ví dụ job develop needs job build: thì khi chạy xong job build thì job develop mới được chạy.
  • if: cái này dùng để lọc điều kiện cho jobs. Ở đây mình lọc điều kiện với nhánh là develop, nghĩa là chỉ áp dụng job này cho nhanh develop, còn những nhánh khác thì sẽ không chạy job này.
  • runs-on: chạy trên môi trường nào.
  • container: cái này mình không rõ lắm :v.
  • steps: Các bước cần thực hiện để hoàn thành jobs. Trong steps này ta gọi cái fastlane để chạy các lane tương ứng cho jobs.

Hết, trong một file workflow chỉ gồm các thành phần như vậy thôi.

4. Run your build!

  • Sau khi config xong các file Fastfile và workflow.yml thì mình có thể chạy được. Hoặc là chạy dưới local để kiểm tra kết quả, hoặc là đẩy code lên github để chạy.
  • Để chạy dưới local thì mình có thể sd các câu lệnh của fastlane để chạy các lane. Ví dụ: để chạy distribute_firebase thì mình chạy lệnh sau trong Terminal: fastlane distribute_firebase. Rồi để nó chạy xong sẽ có thông báo kết quả.
  • Để chạy trên github thì mình cứ đẩy code lên, sau đó nó chạy từng jobs một. Để kiểm tra kết quả chạy + trình tự thì mình có thể vào Action trong Github, workflow sẽ kiểu như sau:

Đó, vậy là mình đã setup xong CI/CD cho một app Android sử dụng Fastlane và Github Actions. Chúc các bạn setup thành công nhé!

Tài liệu tham khảo: https://www.runway.team/blog/ci-cd-pipeline-android-app-fastlane-github-actions


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí