Android Anwendung CI/CD mit Flutter

⏱️5 Min
Teilen:

CI/CD pipeline ist ein wesentlicher Bestandteil der Softwareentwicklung. In diesem Artikel wird erläutert, wie Sie eine CI/CD pipeline erstellen, um eine Android Anwendung mit Flutter zu erstellen. Wir werden eine Pipeline basierend auf GitHub Actions erstellen und keine fastlane .

1. Lokal erstellen

Bevor wir eine Pipeline auf GitHub erstellen, erstellen wir eine App lokal. Sie können die Build-Umgebung mit der offiziellen Website Erstellen und Freigeben einer Android-App vorbereiten. Wir werden hier einige Inhalte erklären.

1-1. key.jks und key.properties

Android App muss der Schlüssel signiert werden. key.jks enthält den Schlüssel und key.properties enthält das Passwort. key.jks kann mit den folgenden Befehlen generiert werden.

Windows

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

Wenn Sie diese Befehle verwenden, benötigen Sie ein key password und ein store password . key.properties diese Passwörter in der Datei key.properties.

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

Ändern Sie dann die Android Gradle-Dateien, um diese Dateien zum Signieren Ihrer App zu verwenden.

1-2. app / bundle.gradle

bundle.gradle Datei bundle.gradle. Beachten Sie, dass bundle.gradle Dateien enthält. Wir verwenden die Datei im app Verzeichnis.

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}

Der erste Block ermöglicht es der App, die Datei key.properties rootProject.file('key.properties') bedeutet, key.properties im Projektstammverzeichnis zu lesen. Wenn Sie den Dateinamen oder das Verzeichnis nicht ändern möchten, können Sie diesen Ausdruck ändern.

Als nächstes sehen wir zwei Blöcke unter android Block.

signingConfigs beschreibt wörtlich die Konfiguration für das Signieren. Hier können Sie den Inhalt von key.properties . Beispielsweise liefert keystoreProperties['keyPassword'] keyPassword Wert aus der Datei key.properties $System.env.KEY_PASSWORD bedeutet, dass Sie den Wert aus der Systemumgebung abrufen können. file("../key.jks") bedeutet, dass Sie Schlüsseldaten direkt aus der Datei laden können. Sie können einen beliebigen Ausdruck auswählen.

Als nächstes erstellen wir die App.

bash
1flutter build apk --release

Mit diesem Befehl können Sie eine apk Der Befehl kann Sie warnen, app bundle oder Split apk zu verwenden, um Fat apk zu vermeiden, aber bitte ignorieren Sie sie jetzt. Ich werde es später erklären.

2. Registrieren Sie Geheimnisse auf GitHub

Registrieren Sie die erforderlichen Geheimnisse auf GitHub . Wir brauchen zumindest unter Geheimnisse. GitHub Sie unter Verschlüsselte Geheimnisse.

  • Schlüsselpasswort
  • Passwort speichern
  • key.jks

Wenn Sie Binärdateien wie key.jks , benötigen Sie eine base64-Codierung, um eine geheime Zeichenfolge zu generieren. Vergessen Sie nicht, es zu dekodieren, wenn Sie es in der Pipeline verwenden.

Sie können auch andere geheime Verwaltungsdienste wie Azure KeyVault und GCP SecretManager . Auf jeden Fall müssen Sie die key.jks und alle anderen Geheimnisse vom Quellcode-Repository trennen und sicher aufbewahren.

3. Fügen Sie den Workflow hinzu

Fügen Sie den Workflow mithilfe der Yaml-Datei hinzu. Erstellen Sie eine Yaml-Datei wie .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

Ich werde Erklärungen zu Java Setup, Flutter Setup, pub get , analyze und test überspringen. Die Build-Aufgabe besteht aus einigen Befehlen, die mit && . Mal sehen, eins nach dem anderen.

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

Dieser Befehl bedeutet, die key.jks aus Geheimnissen zu generieren. Wie ich bereits erklärt habe, ist key.jks als base64-codierte Zeichenfolge registriert. Daher müssen wir sie hier dekodieren und die Datei key.jks Durch Generieren der Datei kann die App Schlüsselinformationen aus der Datei laden, während wir die Speicherdatei als file("../key.jks") .

In der Build-Aufgabe habe ich keine Datei key.properties Dies liegt daran, dass wir keyPassword und storePassword als $System.env.KEY_PASSWORD . Wir haben die Umgebung wie folgt eingestellt, damit die App das Kennwort aus der Systemumgebung laden kann. Wenn Sie zum Lesen Passwort wollen key.properties , können Sie ändern bundle.gradle und erzeugen key.properties Datei hier in Build - Task.

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

Als nächstes überprüfen wir den Build-Befehl.

bash
1flutter build apk --release

Dies ist völlig das gleiche wie beim lokalen Build. Sie sehen also Warnungen, die Sie darauf app bundle oder Split apk , um Fat apk zu vermeiden. Laut der offiziellen Website Erstellen der App zur Veröffentlichung wird ein app bundle empfohlen. Wenn Sie die App freigeben, ist es besser, ein app bundle zu erstellen. Wenn Sie jedoch die App auf Ihrem Gerät testen oder App Distribute of Firebase , benötigen Sie apk .

Daher empfehle ich, den Weg zu wählen, um Ihre App entsprechend dem Zweck zu erstellen. Als täglicher CI Build können Sie beispielsweise apk generieren, mit dem Sie problemlos auf Ihrem Gerät testen können. Als Release-Build können Sie ein app bundle generieren und die App im Google Play Store registrieren. Die folgenden Beispiele enthalten beide Arten von Builds.

Registrieren Sie eine App bei 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

Registrieren Sie eine App im 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. Andere Optionen

4-1. Firebase google-services.json

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

Um eine Verbindung mit Firebase , benötigen Sie google-services.json . Sie können den json mit dem obigen Befehl generieren. Weitere Informationen finden Sie unter FlutterFire - Android-Installation.

4-2. App Distribution

App Distribution ist eine der Funktionen, die Firebase bietet. Mit dieser Funktion können Sie die App an Tester liefern. Wenn Sie diese Funktion verwenden möchten, fügen Sie der Pipeline einfach die folgende Aufgabe hinzu.

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

Weitere Informationen finden Sie unter Firebase Distribution Github-Aktion. Beachten Sie, dass App Distribution apk Dateien zulässig sind.

4-3. Product Flavor

Wenn Sie mit Flutter product flavor , geben Sie --flavor <flavor name> im Build-Befehl. product flavor Sie unter Build-Varianten

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

Mit flavor können Sie unterschiedliche App-IDs mit demselben Code generieren.

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 }

Mit flavor ist die App-ID die Kombination aus applicationId und applicationIdSuffix . Wenn Sie beispielsweise --flavor jp angeben, lautet die App-ID xxx.yyy.zzz.jp

Sie können die Manifestdatei auch mit flavor ändern. Angenommen, Sie verwenden Google AdMob und müssen der Manifestdatei applicationId Sie können dies tun, indem Sie Manifestdateien wie unten festlegen.

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

Legen Sie die Basismanifestdatei unter android/app/src/main und legen Sie dann jede flavor Manifestdatei unter android/app/src/<flavor name> . Die dann erstellte App enthält sowohl das Hauptmanifest als auch das Geschmacksmanifest.

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

Mit dieser Option können Sie Umgebungsvariablen an die App übergeben.

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

Dieser Befehl generiert eine Umgebung mit dem Namen REGION die beispielsweise die Region des Backend-Servers beschreibt. Die App kann die Umgebung anhand des folgenden Codes lesen.

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

4-5. Registrieren Sie eine App im Google Play Store

Verwenden Sie diese Aufgabe, wenn Sie Ihre App im 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

Die erste Aufgabe generiert service-account.json das für den Zugriff auf den Play Store vom CI Server erforderlich ist. Geben Sie dann diese Datei in der zweiten Aufgabe an. Weitere Informationen finden Sie unter Google Play hochladen.

Verweise

Erstellen und Freigeben einer Android-App

Verschlüsselte Geheimnisse

Erstellen der App zur Veröffentlichung

FlutterFire - Android-Installation

Firebase CLI

Firebase Distribution Github-Aktion

Build-Varianten

Google Play hochladen

Teilen:

Verwandte Artikel

So lokalisieren Sie die App in Flutter
Guides

So lokalisieren Sie die App in Flutter

Erfahren Sie, wie Sie Ihre Flutter-App mit arb-Dateien lokalisieren. Dieser Artikel basiert auf Flutter 2.0.1.

mark241