带有Flutter Android应用程序CI/CD

⏱️约3分钟
分享:

CI/CD pipeline是软件开发必不可少的部分。本文介绍了如何Flutter CI/CD pipeline以构建Android应用程序。我们将基于GitHub Actions构建管道,而不使用fastlane

1. 在本地构建

GitHub创建管道之前,让我们在本地构建一个应用程序。 您可以通过官方网站-构建并发布Android应用准备构建环境。 我们将在这里解释一些内容。

1-1. key.jkskey.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 passwordstore 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. app / 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']提供key.properties文件中的keyPassword值。 $System.env.KEY_PASSWORD意味着您可以从系统环境中获取值。 file("../key.jks")意味着您可以直接从文件中加载密钥数据。 您可以选择任何喜欢的表情。

接下来,让我们构建应用程序。

bash
1flutter build apk --release

您可以通过此命令apk 该命令可能会警告您使用app bundle或拆分apk以避免胖apk ,但请立即忽略它们。稍后再解释。

2.在GitHub

GitHub注册必要的秘密。我们至少需要以下秘密。请参阅加密的机密以了解如何在GitHub注册机密。

  • 关键密码
  • 储存密码

key.jks类的二进制文件时,您需要base64编码来生成秘密字符串。在管道中使用它时,请不要忘记对其进行解码。

您还可以使用其他密钥管理服务,例如Azure KeyVaultGCP 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 getanalyzetest 。 构建任务由一些与&&连接的命令组成。让我们一一看。

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

此命令意味着根据机密信息key.jks正如我所解释的, key.jks被注册为base64编码的字符串,因此在这里我们需要对其进行解码并重新生成key.jks文件。通过生成文件,当我们将存储文件配置为file("../key.jks") ,应用程序可以从文件中加载密钥信息。

在构建任务中,我没有生成key.properties文件。这是因为我们将keyPasswordstorePassword配置为$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 ,但是在设备上测试应用程序或使用Firebase App Distribute时,则需要apk

因此,我建议根据目的选择构建应用程序的方式。例如,作为日常CI构建,您可以生成apk ,该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

有关更多信息,请参见Firebase Distribution Github Action。 请注意, App Distribution仅允许使用apk文件。

4-3. Product Flavor

当您在Flutter product flavor时,请指定--flavor <flavor name>在构建命令中。 参见build variants了解product flavor

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

使用flavor ,您可以使用相同的代码生成不同的应用程序ID。

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 ,应用程序ID将是applicationIdapplicationIdSuffix的组合。 例如,如果指定--flavor jp ,则应用程序ID为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 ,这是从CI服务器Play Store 然后在第二个任务中指定此文件。 有关更多信息,请参见上传google play

参考

构建并发布Android应用

加密的秘密

构建要发布的应用

FlutterFire-Android安装

Firebase CLI

Firebase分发Github操作

构建变体

上传Google Play

分享:

相关文章

如何在Flutter中本地化应用
Guides

如何在Flutter中本地化应用

了解如何使用arb文件在Flutter中本地化应用程序。本文基于Flutter 2.0.1版本。

mark241
将Azure资源描述为ARM Template
Guides

将Azure资源描述为ARM Template

ARM Template是定义Azure资源的json文件。本文介绍如何高效创建ARM Template来部署新资源。

mark241