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

Эта статья была опубликована более года назад. Информация может быть устаревшей.
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 можно сгенерировать с помощью следующих команд.
Окна
1keytool -genkey -v -keystore c:\Users\USER_NAME\key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias keyMac / Linux
1keytool -genkey -v -keystore ~/key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias keyКогда вы используете эти команды, вам нужен key password и store password .
Запишите эти пароли в файл key.properties
1storePassword=<your password>2keyPassword=<your password>3keyAlias=key4storeFile=C:/Users/USER_NAME/key.jks // depends on your environmentЗатем измените Android Gradle, чтобы использовать эти файлы для подписи вашего приложения.
1-2. приложение / bundle.gradle
Исправьте файл bundle.gradle Обратите внимание, что в проекте bundle.gradle
Мы используем файл в каталоге app
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.release20 }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") означает, что вы можете напрямую загружать ключевые данные из файла.
Вы можете выбрать любое выражение, которое вам нравится.
Теперь давайте создадим приложение.
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 .
1name: CICD2 3on:4 pull_request:5 branches: [master]6 workflow_dispatch:7 8jobs:9 dev:10 runs-on: ubuntu-latest11 12 steps:13 # checkout source code14 - uses: actions/checkout@v215 # setup java16 - name: set up JDK 1.817 uses: actions/setup-java@v118 with:19 java-version: 1.820 # setup flutter21 - name: Setup flutter22 uses: subosito/flutter-action@v123 with:24 flutter-version: "2.0.1"25 # pub get26 - run: flutter pub get27 # analyze28 - run: flutter analyze29 # test30 - run: flutter test31 # build32 - name: build dev33 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 .
Задача сборки состоит из нескольких команд, связанных с && . Посмотрим по порядку.
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 здесь, в задаче сборки.
1- name: build dev2 env:3 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}4 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}Далее проверим команду сборки.
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
1name: CICD2 3on:4 pull_request:5 branches: [master]6 workflow_dispatch:7 8jobs:9 dev:10 runs-on: ubuntu-latest11 12 steps:13 # checkout source code14 - uses: actions/checkout@v215 # setup java16 - name: set up JDK 1.817 uses: actions/setup-java@v118 with:19 java-version: 1.820 # setup flutter21 - name: Setup flutter22 uses: subosito/flutter-action@v123 with:24 flutter-version: "2.0.1"25 # pub get26 - run: flutter pub get27 # analyze28 - run: flutter analyze29 # test30 - run: flutter test31 # build32 - name: build33 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 --release39 # app distribution40 - name: Firebase App Distribution41 uses: wzieba/Firebase-Distribution-Github-Action@v1.2.242 with:43 appId: ${{secrets.FIREBASE_APP_ID}}44 token: ${{secrets.FIREBASE_APP_TOKEN}}45 file: build/app/outputs/apk/release/app-release.apk46 groups: TesterЗарегистрируйте приложение в Google Play Store
1name: Release2 3on:4 workflow_dispatch:5 6jobs:7 dev:8 runs-on: ubuntu-latest9 10 steps:11 # checkout source code12 - uses: actions/checkout@v213 # setup java14 - name: set up JDK 1.815 uses: actions/setup-java@v116 with:17 java-version: 1.818 # setup flutter19 - name: Setup flutter20 uses: subosito/flutter-action@v121 with:22 flutter-version: "2.0.1"23 # pub get24 - run: flutter pub get25 # analyze26 - run: flutter analyze27 # test28 - run: flutter test29 # build30 - name: build31 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 --release37 # google play38 - name: Create service account json39 env:40 SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON}}41 run: echo $SERVICE_ACCOUNT_JSON > android/app/service-account.json42 - uses: r0adkll/upload-google-play@v143 with:44 serviceAccountJson: android/app/service-account.json45 packageName: your app id46 releaseFiles: build/app/outputs/bundle/release/app-release.aab47 track: alpha48 whatsNewDirectory: release-notes49 mappingFile: build/app/outputs/mapping/release/mapping.txt4. Другие варианты
4-1. Firebase google-services.json
1echo $GOOGLE_SERVICES > android/app/google-services.jsonЧтобы подключиться к Firebase , вам понадобится google-services.json .
Вы можете сгенерировать json с помощью приведенной выше команды.
Для получения дополнительных сведений см. FlutterFire - установка Android.
4-2. App Distribution
App Distribution - одна из функций, предоставляемых Firebase
Вы можете доставить приложение тестерам с помощью этой функции. Если вы хотите использовать эту функцию, вы просто добавляете следующую задачу в конвейер.
1- name: Firebase App Distribution2 uses: wzieba/Firebase-Distribution-Github-Action@v1.2.23 with:4 appId: your app id5 token: ${{secrets.FIREBASE_APP_TOKEN}}6 file: build/app/outputs/apk/release/app-release.apk7 groups: TesterСм. Действие Github распространения Firebase для получения дополнительной информации.
Обратите внимание, что для App Distribution apk .
4-3. Product Flavor
Когда вы используете product flavor с Flutter , укажите --flavor <flavor name> в команде сборки.
См. Варианты сборки для product flavor
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jpС flavor вы можете сгенерировать другой идентификатор приложения с одним и тем же кодом.
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 в файл манифеста.
Вы можете сделать это, установив файлы манифеста, как показано ниже.
1android/app/src2 |- main/AndroidManifest.xml3 |- jp/AndroidManifest.xml4 |- eu/AndroidManifest.xmlПоместите базовый файл манифеста в android/app/src/main , затем поместите каждый файл манифеста, зависящий от flavor android/app/src/<flavor name> .
Затем созданное приложение будет содержать как основной манифест, так и манифест вкуса.
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-data6 android:name="com.google.android.gms.ads.APPLICATION_ID"7 android:value="your admob app id"/>8 </application>9</manifest>4-4. --dart-define
Этот параметр позволяет передавать в приложение переменные среды.
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jp --dart-define=REGION=asia-northeast1Эти команды создают среду с именем REGION которая описывает, например, регион внутреннего сервера.
Приложение может считывать среду с помощью кода ниже.
1String region = String.fromEnvironment('REGION'); // get 'asia-northeast1'4-5. Зарегистрируйте приложение в Google Play Store
Когда вы регистрируете свое приложение в Google Play Store , используйте эту задачу.
1- name: Create service account json2 env:3 SERVICE_ACCOUNT_JSON: ${{ secrets.GOOGLE_PLAY_SERVICE_ACCOUNT_JSON}}4 run: echo $SERVICE_ACCOUNT_JSON > android/app/service-account.json5- uses: r0adkll/upload-google-play@v16 with:7 serviceAccountJson: android/app/service-account.json8 packageName: your app id9 releaseFiles: build/app/outputs/bundle/release/app-release.aab10 track: alpha11 whatsNewDirectory: release-notes12 mappingFile: build/app/outputs/mapping/release/mapping.txtПервая задача генерирует service-account.json который необходим для доступа к Play Store с сервера CI
Затем укажите этот файл во втором задании.
Дополнительную информацию см. В разделе загрузка Google Play.
Рекомендации
Создайте и выпустите приложение для Android
FlutterFire - установка Android