Application Android CI/CD avec Flutter

⏱️6 min
Partager :

CI/CD pipeline est une partie essentielle du développement logiciel. Cet article explique comment construire un CI/CD pipeline pour créer une Android aide de Flutter . Nous allons construire un pipeline basé sur des GitHub Actions et n'utiliserons pas fastlane .

1. Construisez localement

Avant de créer un pipeline sur GitHub , créons une application localement. Vous pouvez préparer l'environnement de construction avec le site Web officiel - Créer et publier une application Android. Nous allons expliquer certains des contenus ici.

1-1. key.jks et key.properties

La création d'une Android nécessite la signature de la clé. key.jks contient la clé et key.properties contient le mot de passe. key.jks peut être généré par les commandes ci-dessous.

les fenêtres

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

Lorsque vous utilisez ces commandes, vous avez besoin d' key password et d' store password . Enregistrez ces mots de passe dans le fichier key.properties

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

Ensuite, modifiez Android fichiers de graduation Android pour utiliser ces fichiers pour signer votre application.

1-2. app / bundle.gradle

Correction bundle.gradle fichier bundle.gradle. Notez qu'il existe deux bundle.gradle dans le projet. Nous utilisons le fichier sous le répertoire de l' 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}

Le premier bloc permet à l'application de lire le fichier key.properties rootProject.file('key.properties') signifie lire key.properties sous le répertoire racine du projet. Si vous ne voulez pas changer le nom du fichier ou le répertoire, vous pouvez modifier cette expression.

Ensuite, nous pouvons voir deux blocs sous le bloc android

signingConfigs décrit littéralement la configuration pour la signature. Vous pouvez récupérer le contenu de key.properties ici. Par exemple, keystoreProperties['keyPassword'] fournit keyPassword valeur de key.properties fichier. $System.env.KEY_PASSWORD signifie que vous pouvez obtenir la valeur de l'environnement système. file("../key.jks") signifie que vous pouvez directement charger les données clés du fichier. Vous pouvez choisir l'expression que vous aimez.

Ensuite, construisons l'application.

bash
1flutter build apk --release

Vous pouvez générer un apk par cette commande. La commande peut vous avertir d'utiliser un app bundle ou un apk fractionné pour éviter les gros apk , mais veuillez les ignorer maintenant. Je vous l'expliquerai plus tard.

2. Enregistrer les secrets sur GitHub

Enregistrez les secrets nécessaires sur GitHub . Nous avons besoin d'au moins ci-dessous des secrets. Veuillez consulter Encrypted Secrets pour savoir comment enregistrer des secrets sur GitHub .

  • mot de passe clé
  • stocker le mot de passe
  • key.jks

Lorsque vous enregistrez des fichiers binaires comme key.jks , vous avez besoin d'un encodage base64 pour générer une chaîne secrète. N'oubliez pas de le décoder lorsque vous l'utilisez dans le pipeline.

Vous pouvez également utiliser d'autres services de gestion des secrets tels Azure KeyVault et GCP SecretManager . Quoi qu'il en soit, vous devez séparer le key.jks et tous les autres secrets du référentiel de code source et les garder en sécurité.

3. Ajouter un flux de travail

Ajoutez un flux de travail à l'aide d'un fichier yaml. Créez un fichier yaml comme .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

Je vais sauter l'explication sur la configuration de Java Flutter , pub get , analyze , test . La tâche de construction se compose de quelques commandes connectées avec && . Voyons un par un.

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

Cette commande signifie générer key.jks fichier key.jks à partir de secrets. Comme je l'ai expliqué, key.jks est enregistré en tant que chaîne encodée en base64, nous devons donc le décoder et générer à key.jks fichier key.jks. En générant le fichier, l'application peut charger des informations clés à partir du fichier lorsque nous configurons le fichier de stockage en file("../key.jks") .

Dans la tâche de construction, je n'ai pas généré de fichier key.properties C'est parce que nous configurons keyPassword et storePassword comme $System.env.KEY_PASSWORD . Nous avons défini l'environnement comme ci-dessous, afin que l'application puisse charger le mot de passe à partir de l'environnement système. Si vous souhaitez lire le mot de passe à partir de key.properties , vous pouvez modifier bundle.gradle et générer le key.properties ici dans la tâche de construction.

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

Ensuite, vérifions la commande de construction.

bash
1flutter build apk --release

C'est complètement la même chose que la version locale. Ainsi, vous verrez des avertissements vous indiquant que vous devez utiliser un app bundle ou un apk fractionné pour éviter les gros apk . Selon le site officiel Création de l'application pour la publication, le app bundle est recommandé. Ainsi, lorsque vous publiez l'application, il est préférable de créer un app bundle , mais lorsque vous testez l'application sur votre appareil ou utilisez App Distribute of Firebase , vous avez besoin d' apk .

Je recommande donc de choisir la manière de créer votre application en fonction de son objectif. Par exemple, en tant que CI , vous pouvez générer un apk qui vous permet de tester facilement sur votre appareil, et en tant que version de version, vous pouvez générer un app bundle et enregistrer l'application sur Google Play Store . Les exemples ci-dessous fournissent les deux types de build.

Enregistrer une application avec 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

Enregistrer une application sur 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. Autres options

4-1. Firebase google-services.json

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

Pour vous connecter à Firebase , vous avez besoin de google-services.json . Vous pouvez générer le json par la commande ci-dessus. Pour plus de détails, voir FlutterFire - installation Android.

4-2. App Distribution

App Distribution est l'une des fonctionnalités fournies par Firebase Vous pouvez fournir l'application aux testeurs grâce à cette fonctionnalité. Si vous souhaitez utiliser cette fonctionnalité, il vous suffit d'ajouter la tâche ci-dessous au pipeline.

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

Voir Action Github de Firebase Distribution, pour plus d'informations. Notez que seul le apk est autorisé pour la App Distribution .

4-3. Product Flavor

Lorsque vous utilisez product flavor avec Flutter , spécifiez --flavor <flavor name> dans la commande de construction. Voir variantes de construction pour product flavor

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

Avec la flavor , vous pouvez générer différents identifiants d'application avec le même 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 }

Avec la flavor , l'identifiant de l'application sera la combinaison de applicationId et applicationIdSuffix . Par exemple, si vous spécifiez --flavor jp , l'ID de l'application sera xxx.yyy.zzz.jp

Vous pouvez également modifier le fichier manifeste avec la flavor . Supposons que vous Google AdMob , vous devez alors ajouter applicationId au fichier manifeste. Vous pouvez le faire en définissant des fichiers manifestes comme ci-dessous.

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

Placez le fichier manifeste de base dans android/app/src/main , puis placez chaque fichier manifeste dépendant de la flavor android/app/src/<flavor name> . Ensuite, l'application construite contiendra à la fois le manifeste principal et le manifeste de saveur.

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

Cette option vous permet de transmettre des variables d'environnement à l'application.

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

Cette commande génère un environnement nommé REGION qui décrit la région du serveur backend, par exemple. L'application peut lire l'environnement par le code ci-dessous.

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

4-5. Enregistrer une application sur Google Play Store

Lorsque vous enregistrez votre application sur Google Play Store , utilisez cette tâche.

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

La première tâche génère service-account.json qui est nécessaire pour accéder au Play Store partir du serveur CI Spécifiez ensuite ce fichier dans la deuxième tâche. Veuillez consulter upload google play pour plus d'informations.

Les références

Créer et publier une application Android

Secrets cryptés

Création de l'application pour publication

FlutterFire - installation Android

CLI Firebase

Action Github de Firebase Distribution

variantes de construction

télécharger google play

Partager :

Articles connexes