CI/CD приложения для Android с Flutter

⏱️5 мин
Поделиться:

CI/CD pipeline является неотъемлемой частью разработки программного обеспечения. В этой статье объясняется, как построить CI/CD pipeline для создания Android с использованием Flutter . Мы построим конвейер на основе GitHub Actions и не fastlane использовать fastlane.

1. Создавайте локально

Прежде чем создавать конвейер на GitHub , давайте создадим приложение локально. Вы можете подготовить среду сборки на официальном веб-сайте - Сборка и выпуск приложения для Android. Мы объясним здесь часть содержания.

1-1. key.jks и key.properties

Для создания Android требуется ключ для подписи. key.jks содержит ключ, а key.properties содержит пароль. key.jks можно сгенерировать с помощью следующих команд.

Окна

bash
1keytool -genkey -v -keystore c:\Users\USER_NAME\key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias key

Mac / Linux

bash
1keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias key

Когда вы используете эти команды, вам нужен key password и store password . Запишите эти пароли в файл key.properties

properties
1storePassword=<your password>
2keyPassword=<your password>
3keyAlias=key
4storeFile=C:/Users/USER_NAME/key.jks // depends on your environment

Затем измените Android Gradle, чтобы использовать эти файлы для подписи вашего приложения.

1-2. приложение / bundle.gradle

Исправьте файл bundle.gradle Обратите внимание, что в проекте bundle.gradle Мы используем файл в каталоге app

gradle
1def keystoreProperties = new Properties()
2def keystorePropertiesFile = rootProject.file('key.properties')
3if (keystorePropertiesFile.exists()) {
4 keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
5}
6
7android {
8 signingConfigs {
9 release {
10 keyAlias keystoreProperties['keyAlias'] ? keystoreProperties['keyAlias'] : "key"
11 keyPassword keystoreProperties['keyPassword'] ? keystoreProperties['keyPassword'] : "$System.env.KEY_PASSWORD"
12 storeFile file("../key.jks")
13 storePassword keystoreProperties['storePassword'] ? keystoreProperties['storePassword'] : "$System.env.STORE_PASSWORD"
14 }
15 }
16
17 buildTypes {
18 release {
19 signingConfig signingConfigs.release
20 }
21 }
22}

Первый блок позволяет приложению читать файл key.properties rootProject.file('key.properties') означает чтение key.properties из корневого каталога проекта. Если вы не хотите изменять имя файла или каталог, вы можете изменить это выражение.

Далее мы видим два блока под блоком android

signingConfigs буквально описывает конфигурацию для подписи. Здесь вы можете получить содержимое key.properties . Например, keystoreProperties['keyPassword'] обеспечивает keyPassword значение из key.properties файла. $System.env.KEY_PASSWORD означает, что вы можете получить значение из системной среды. file("../key.jks") означает, что вы можете напрямую загружать ключевые данные из файла. Вы можете выбрать любое выражение, которое вам нравится.

Теперь давайте создадим приложение.

bash
1flutter build apk --release

Вы можете сгенерировать apk файл с помощью этой команды. Команда может предупредить вас об использовании app bundle или разделении apk чтобы избежать жирного apk , но, пожалуйста, игнорируйте их прямо сейчас. Я объясню это позже.

2. Зарегистрируйте секреты на GitHub

Зарегистрируйте необходимые секреты на GitHub . Нам нужны как минимум нижеприведенные секреты. См. Зашифрованные секреты, чтобы узнать, как регистрировать секреты на GitHub .

  • ключевой пароль
  • пароль магазина
  • key.jks

Когда вы регистрируете двоичные файлы, такие как key.jks , вам нужна кодировка base64 для генерации секретной строки. Не забывайте декодировать его, когда используете его в конвейере.

Вы также можете использовать другие службы управления Azure KeyVault такие как Azure KeyVault и GCP SecretManager . В любом случае, вы должны отделить key.jks и любые другие секреты от репозитория исходного кода и сохранить их в безопасности.

3. Добавить рабочий процесс

Добавьте рабочий процесс с помощью файла yaml. Создайте файл yaml, например .github/workflows/xxx.yaml .

yaml
1name: CICD
2
3on:
4 pull_request:
5 branches: [master]
6 workflow_dispatch:
7
8jobs:
9 dev:
10 runs-on: ubuntu-latest
11
12 steps:
13 # checkout source code
14 - uses: actions/checkout@v2
15 # setup java
16 - name: set up JDK 1.8
17 uses: actions/setup-java@v1
18 with:
19 java-version: 1.8
20 # setup flutter
21 - name: Setup flutter
22 uses: subosito/flutter-action@v1
23 with:
24 flutter-version: "2.0.1"
25 # pub get
26 - run: flutter pub get
27 # analyze
28 - run: flutter analyze
29 # test
30 - run: flutter test
31 # build
32 - name: build dev
33 env:
34 KEY_JKS: ${{ secrets.KEY_JKS }}
35 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
36 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
37 GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES }}
38 run: echo $KEY_JKS | base64 --decode --ignore-garbage > android/key.jks && flutter build apk --release

Я пропущу объяснения о настройке Java Flutter , pub get , analyze , test . Задача сборки состоит из нескольких команд, связанных с && . Посмотрим по порядку.

bash
1echo $KEY_JKS | base64 --decode --ignore-garbage > android/key.jks

Эта команда означает создание key.jks из секретов. Как я объяснил, key.jks зарегистрирован как строка в кодировке base64, поэтому здесь нам нужно декодировать ее и повторно сгенерировать файл key.jks Создав файл, приложение может загружать ключевую информацию из файла, поскольку мы настраиваем файл хранилища как file("../key.jks") .

В задаче сборки я не создавал файл key.properties Это потому, что мы настраиваем keyPassword и storePassword как $System.env.KEY_PASSWORD . Мы установили среду, как показано ниже, поэтому приложение может загружать пароль из системной среды. Если вы хотите прочитать пароль из key.properties , вы можете изменить bundle.gradle и сгенерировать key.properties здесь, в задаче сборки.

yaml
1- name: build dev
2 env:
3 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
4 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}

Далее проверим команду сборки.

bash
1flutter build apk --release

Это полностью то же самое, что и локальная сборка. Таким образом, вы увидите предупреждения, которые говорят вам, что вы должны использовать app bundle или разделить apk чтобы избежать жирного apk . Согласно официальному сайту Создание приложения для выпуска, рекомендуется app bundle Поэтому, когда вы выпускаете приложение, лучше создать app bundle , однако, когда вы тестируете приложение на своем устройстве или используете App Distribute of Firebase , вам понадобится apk .

Поэтому я рекомендую выбирать способ создания приложения в зависимости от цели. Например, в качестве ежедневной CI вы можете сгенерировать apk который позволяет вам легко тестировать на своем устройстве, а в качестве сборки выпуска вы можете создать app bundle и зарегистрировать приложение в Google Play Store . Ниже приведены примеры обоих типов сборки.

Зарегистрируйте приложение в App Distribution

yaml
1name: CICD
2
3on:
4 pull_request:
5 branches: [master]
6 workflow_dispatch:
7
8jobs:
9 dev:
10 runs-on: ubuntu-latest
11
12 steps:
13 # checkout source code
14 - uses: actions/checkout@v2
15 # setup java
16 - name: set up JDK 1.8
17 uses: actions/setup-java@v1
18 with:
19 java-version: 1.8
20 # setup flutter
21 - name: Setup flutter
22 uses: subosito/flutter-action@v1
23 with:
24 flutter-version: "2.0.1"
25 # pub get
26 - run: flutter pub get
27 # analyze
28 - run: flutter analyze
29 # test
30 - run: flutter test
31 # build
32 - name: build
33 env:
34 KEY_JKS: ${{ secrets.KEY_JKS }}
35 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
36 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
37 GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES }}
38 run: echo $KEY_JKS | base64 --decode --ignore-garbage > android/key.jks && echo $GOOGLE_SERVICES > android/app/google-services.json && flutter build apk --release
39 # app distribution
40 - name: Firebase App Distribution
41 uses: wzieba/Firebase-Distribution-Github-Action@v1.2.2
42 with:
43 appId: ${{secrets.FIREBASE_APP_ID}}
44 token: ${{secrets.FIREBASE_APP_TOKEN}}
45 file: build/app/outputs/apk/release/app-release.apk
46 groups: Tester

Зарегистрируйте приложение в Google Play Store

yaml
1name: Release
2
3on:
4 workflow_dispatch:
5
6jobs:
7 dev:
8 runs-on: ubuntu-latest
9
10 steps:
11 # checkout source code
12 - uses: actions/checkout@v2
13 # setup java
14 - name: set up JDK 1.8
15 uses: actions/setup-java@v1
16 with:
17 java-version: 1.8
18 # setup flutter
19 - name: Setup flutter
20 uses: subosito/flutter-action@v1
21 with:
22 flutter-version: "2.0.1"
23 # pub get
24 - run: flutter pub get
25 # analyze
26 - run: flutter analyze
27 # test
28 - run: flutter test
29 # build
30 - name: build
31 env:
32 KEY_JKS: ${{ secrets.KEY_JKS }}
33 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
34 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
35 GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES }}
36 run: echo $KEY_JKS | base64 --decode --ignore-garbage > android/key.jks && flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release
37 # google play
38 - name: Create service account json
39 env:
40 SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON}}
41 run: echo $SERVICE_ACCOUNT_JSON > android/app/service-account.json
42 - uses: r0adkll/upload-google-play@v1
43 with:
44 serviceAccountJson: android/app/service-account.json
45 packageName: your app id
46 releaseFiles: build/app/outputs/bundle/release/app-release.aab
47 track: alpha
48 whatsNewDirectory: release-notes
49 mappingFile: build/app/outputs/mapping/release/mapping.txt

4. Другие варианты

4-1. Firebase google-services.json

bash
1echo $GOOGLE_SERVICES > android/app/google-services.json

Чтобы подключиться к Firebase , вам понадобится google-services.json . Вы можете сгенерировать json с помощью приведенной выше команды. Для получения дополнительных сведений см. FlutterFire - установка Android.

4-2. App Distribution

App Distribution - одна из функций, предоставляемых Firebase Вы можете доставить приложение тестерам с помощью этой функции. Если вы хотите использовать эту функцию, вы просто добавляете следующую задачу в конвейер.

yaml
1- name: Firebase App Distribution
2 uses: wzieba/Firebase-Distribution-Github-Action@v1.2.2
3 with:
4 appId: your app id
5 token: ${{secrets.FIREBASE_APP_TOKEN}}
6 file: build/app/outputs/apk/release/app-release.apk
7 groups: Tester

См. Действие Github распространения Firebase для получения дополнительной информации. Обратите внимание, что для App Distribution apk .

4-3. Product Flavor

Когда вы используете product flavor с Flutter , укажите --flavor <flavor name> в команде сборки. См. Варианты сборки для product flavor

bash
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jp

С flavor вы можете сгенерировать другой идентификатор приложения с одним и тем же кодом.

gradle
1android {
2
3 defaultConfig {
4 applicationId "xxx.yyy.zzz"
5 ...
6 }
7
8 flavorDimensions "targetArea"
9 productFlavors {
10 jp {
11 applicationIdSuffix ".jp"
12 dimension "targetArea"
13 }
14 eu {
15 applicationIdSuffix ".eu"
16 dimension "targetArea"
17 }
18 }

Со flavor идентификатор приложения будет представлять собой комбинацию applicationId и applicationIdSuffix . Например, если вы укажете --flavor jp , идентификатор приложения будет xxx.yyy.zzz.jp

Вы также можете изменить файл манифеста с помощью flavor . Предположим, вы будете использовать Google AdMob , тогда вам нужно добавить applicationId в файл манифеста. Вы можете сделать это, установив файлы манифеста, как показано ниже.

txt
1android/app/src
2 |- main/AndroidManifest.xml
3 |- jp/AndroidManifest.xml
4 |- eu/AndroidManifest.xml

Поместите базовый файл манифеста в android/app/src/main , затем поместите каждый файл манифеста, зависящий от flavor android/app/src/<flavor name> . Затем созданное приложение будет содержать как основной манифест, так и манифест вкуса.

xml
1<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2 package="xxx.yyy.zzz">
3 <uses-permission android:name="android.permission.INTERNET" />
4 <application>
5 <meta-data
6 android:name="com.google.android.gms.ads.APPLICATION_ID"
7 android:value="your admob app id"/>
8 </application>
9</manifest>

4-4. --dart-define

Этот параметр позволяет передавать в приложение переменные среды.

bash
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jp --dart-define=REGION=asia-northeast1

Эти команды создают среду с именем REGION которая описывает, например, регион внутреннего сервера. Приложение может считывать среду с помощью кода ниже.

dart
1String region = String.fromEnvironment('REGION'); // get 'asia-northeast1'

4-5. Зарегистрируйте приложение в Google Play Store

Когда вы регистрируете свое приложение в Google Play Store , используйте эту задачу.

yaml
1- name: Create service account json
2 env:
3 SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON}}
4 run: echo $SERVICE_ACCOUNT_JSON > android/app/service-account.json
5- uses: r0adkll/upload-google-play@v1
6 with:
7 serviceAccountJson: android/app/service-account.json
8 packageName: your app id
9 releaseFiles: build/app/outputs/bundle/release/app-release.aab
10 track: alpha
11 whatsNewDirectory: release-notes
12 mappingFile: build/app/outputs/mapping/release/mapping.txt

Первая задача генерирует service-account.json который необходим для доступа к Play Store с сервера CI Затем укажите этот файл во втором задании. Дополнительную информацию см. В разделе загрузка Google Play.

Рекомендации

Создайте и выпустите приложение для Android

Зашифрованные секреты

Сборка приложения для выпуска

FlutterFire - установка Android

Firebase CLI

Действие Github для распространения Firebase

варианты сборки

загрузить игру в Google

Поделиться:

Связанные статьи