Android applicatie CI/CD met Flutter

⏱️5 min
Delen:

CI/CD pipeline is een essentieel onderdeel van softwareontwikkeling. In dit artikel wordt uitgelegd hoe u een CI/CD pipeline construeert om een Android applicatie Flutter . We bouwen een pijplijn op basis van GitHub Actions en gebruiken geen fastlane .

1. Bouw lokaal

Laten we lokaal een app bouwen voordat we een pijplijn op GitHub U kunt een build-omgeving voorbereiden met de officiële website - Build and release an Android app. We zullen hier een deel van de inhoud uitleggen.

1-1. key.jks en key.properties

Voor het bouwen van een Android app is de sleutel nodig om te ondertekenen. key.jks bevat de sleutel en key.properties bevat het wachtwoord. key.jks kan worden gegenereerd door de onderstaande commando's.

ramen

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

Als u deze opdrachten gebruikt, heeft u een key password en store password . Noteer deze wachtwoorden in het bestand key.properties

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

Pas vervolgens de Android aan om deze bestanden te gebruiken om uw app te ondertekenen.

1-2. app / bundle.gradle

Fix bundle.gradle bestand. Merk op dat er twee bundle.gradle bestanden in het project zijn. We gebruiken het bestand onder de app directory.

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}

Met het eerste blok kan de app het key.properties bestand lezen. rootProject.file('key.properties') betekent het lezen van key.properties in de hoofdmap van het project. Als u de bestandsnaam of directory niet wilt wijzigen, kunt u deze uitdrukking wijzigen.

Vervolgens kunnen we twee blokken zien onder het android blok.

signingConfigs beschrijft letterlijk de configuratie voor ondertekenen. U kunt hier de inhoud van key.properties . keystoreProperties['keyPassword'] levert bijvoorbeeld keyPassword waarde uit het key.properties bestand. $System.env.KEY_PASSWORD betekent dat u de waarde uit de systeemomgeving kunt halen. file("../key.jks") betekent dat u de belangrijkste gegevens rechtstreeks uit het bestand kunt laden. U kunt elke gewenste uitdrukking kiezen.

Laten we vervolgens de app bouwen.

bash
1flutter build apk --release

U kunt met deze opdracht apk De opdracht kan u waarschuwen om app bundle of split apk te gebruiken om dikke apk te vermijden, maar negeer ze nu alstublieft. Ik zal het later uitleggen.

2. Registreer geheimen op GitHub

Registreer de nodige geheimen op GitHub . We hebben in ieder geval onderstaande geheimen nodig. Zie Encrypted Secrets om te zien hoe je geheimen registreert op GitHub .

  • key wachtwoord
  • wachtwoord opslaan
  • key.jks

Als u binaire bestanden zoals key.jks , hebt u base64-codering nodig om een geheime string te genereren. Vergeet niet om het te decoderen wanneer u het in de pijplijn gebruikt.

U kunt ook andere geheime beheerservices gebruiken, zoals Azure KeyVault en GCP SecretManager . Hoe dan ook, je moet de key.jks en alle andere geheimen scheiden van de broncode-repository en ze veilig bewaren.

3. Workflow toevoegen

Voeg een workflow toe met behulp van een yaml-bestand. Maak een yaml-bestand zoals .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

Ik sla de uitleg over Java instellingen, Flutter instellingen, pub get , analyze , test . De bouwtaak bestaat uit een paar opdrachten die verband houden met && . Laten we een voor een kijken.

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

Dit commando houdt in dat het key.jks bestand uit geheimen wordt gegenereerd. Zoals ik heb uitgelegd, is key.jks geregistreerd als base64-gecodeerde tekenreeks, dus hier moeten we het decoderen en het key.jks bestand opnieuw genereren. Door het bestand te genereren, kan de app sleutelinformatie uit het bestand laden terwijl we het winkelbestand configureren als file("../key.jks") .

In de bouwtaak heb ik geen key.properties bestand gegenereerd. Dit komt doordat we keyPassword en storePassword configureren als $System.env.KEY_PASSWORD . We hebben de omgeving ingesteld zoals hieronder, zodat de app het wachtwoord uit de systeemomgeving kan laden. Als u het wachtwoord van key.properties wilt lezen, kunt u bundle.gradle wijzigen en het key.properties bestand hier in de build-taak genereren.

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

Laten we vervolgens de build-opdracht bekijken.

bash
1flutter build apk --release

Dit is volledig hetzelfde als lokale build. U ziet dus waarschuwingen die u vertellen dat u app bundle of split apk moet gebruiken om dikke apk te vermijden. Volgens de officiële site Building the app for release wordt een app bundle aanbevolen. Dus wanneer u de app vrijgeeft, is het beter om een app bundle te bouwen, maar wanneer u de app op uw apparaat test of App Distribute van Firebase , heeft u apk nodig.

Dus ik raad aan om de manier te kiezen om uw app te bouwen op basis van het doel. Als dagelijkse CI build kunt u bijvoorbeeld apk genereren waarmee u eenvoudig op uw apparaat kunt testen, en als release-build kunt u een app bundle genereren en de app registreren in Google Play Store . Onderstaande voorbeelden bieden beide typen build.

Registreer een app bij 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

Registreer een app bij 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 opties

4-1. Firebase google-services.json

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

Om verbinding te maken met Firebase , heeft u google-services.json . U kunt de json genereren met het bovenstaande commando. Voor meer details, zie FlutterFire - Android-installatie.

4-2. App Distribution

App Distribution is een van de functies die Firebase biedt. Met deze functie kunt u de app aan testers leveren. Als u deze functie wilt gebruiken, voegt u gewoon de onderstaande taak toe aan de pijplijn.

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

Zie Firebase Distribution Github Action voor meer informatie. Merk op dat alleen apk bestanden zijn toegestaan voor App Distribution .

4-3. Product Flavor

Als u product flavor met Flutter , specificeer dan --flavor <flavor name> in build-opdracht. Zie build varianten voor product flavor

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

Met flavor kun je verschillende app-id's genereren met dezelfde code.

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 }

Met flavor is de app-ID de combinatie van applicationId en applicationIdSuffix . Als u bijvoorbeeld --flavor jp opgeeft, is de app-ID xxx.yyy.zzz.jp

U kunt het manifestbestand ook wijzigen met flavor . Stel dat u Google AdMob gaat gebruiken, dan moet u applicationId aan het manifestbestand toevoegen. U kunt dit doen door manifestbestanden in te stellen zoals hieronder.

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

Zet het basismanifestbestand op android/app/src/main , en plaats vervolgens elk flavor manifestbestand op android/app/src/<flavor name>​ De vervolgens gebouwde app bevat zowel het hoofdmanifest als het smaakmanifest.

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-definiëren

Met deze optie kunt u omgevingsvariabelen doorgeven aan de app.

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

Deze opdracht genereert een omgeving met de naam REGION die bijvoorbeeld de regio van de backend-server beschrijft. De app kan de omgeving lezen aan de hand van onderstaande code.

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

4-5. Registreer een app bij Google Play Store

Gebruik deze taak als u uw app bij 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

De eerste taak genereert service-account.json die nodig is om toegang te krijgen tot de Play Store vanaf de CI server. Specificeer vervolgens dit bestand in de tweede taak. Zie upload google play voor meer informatie.

Referenties

Een Android-app bouwen en vrijgeven

Versleutelde geheimen

De app bouwen voor release

FlutterFire - Android-installatie

Firebase CLI

Firebase Distribution Github Action

build varianten

upload google play

Delen:

Gerelateerde artikelen

Hoe de app in Flutter te lokaliseren
Guides

Hoe de app in Flutter te lokaliseren

Leer hoe u uw Flutter-app lokaliseert met arb-bestanden. Dit artikel is gebaseerd op Flutter 2.0.1.

mark241
Beschrijf Azure-resources als ARM Template
Guides

Beschrijf Azure-resources als ARM Template

ARM Template is een json-bestand dat Azure-resources definieert. Leer hoe u efficiënt ARM Templates kunt maken.

mark241