Applicazione Android CI/CD con Flutter

Questo articolo è stato pubblicato più di un anno fa. Le informazioni potrebbero essere obsolete.
CI/CD pipeline è una parte essenziale dello sviluppo del software. Questo articolo spiega come costruire una CI/CD pipeline per creare un'applicazione Android Flutter . Costruiremo la pipeline in base alle GitHub Actions e non useremo fastlane .
1. Crea localmente
Prima di creare una pipeline su GitHub , creiamo un'app in locale.
Puoi preparare l'ambiente di compilazione con il sito web ufficiale - Crea e rilascia un'app Android.
Spiegheremo alcuni dei contenuti qui.
1-1. key.jks e key.properties
La creazione di Android richiede la chiave per firmare. key.jks contiene la chiave e key.properties contiene la password.
key.jks può essere generato dai seguenti comandi.
finestre
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 keyQuando si utilizzano questi comandi, è necessaria la key password e la store password .
Registra queste password nel file key.properties
1storePassword=<your password>2keyPassword=<your password>3keyAlias=key4storeFile=C:/Users/USER_NAME/key.jks // depends on your environmentQuindi, modifica Android file gradle di Android per utilizzare questi file per firmare la tua app.
1-2. app / bundle.gradle
Correggi il file bundle.gradle Nota che ci sono due bundle.gradle nel progetto.
Usiamo il file nella directory 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}Il primo blocco consente all'app di leggere il file key.properties
rootProject.file('key.properties') significa leggere key.properties nella directory root del progetto.
Se si desidera modificare il nome del file o la directory, è possibile modificare questa espressione.
Successivamente possiamo vedere due blocchi sotto il blocco android
signingConfigs descrive letteralmente la configurazione per la firma. Puoi recuperare il contenuto di key.properties qui.
Ad esempio, keystoreProperties['keyPassword'] fornisce keyPassword valore dal key.properties file.
$System.env.KEY_PASSWORD significa che puoi ottenere il valore dall'ambiente di sistema. file("../key.jks") significa che puoi caricare direttamente i dati della chiave dal file.
Puoi scegliere qualunque espressione ti piaccia.
Successivamente, creiamo l'app.
1flutter build apk --releasePuoi generare un apk con questo comando.
Il comando potrebbe avvisarti di utilizzare app bundle o split apk per evitare fat apk , ma ignorali subito. Lo spiegherò più tardi.
2. Registra i segreti su GitHub
Registra i segreti necessari su GitHub . Abbiamo bisogno almeno di segreti sotto. Consulta Encrypted Secrets per scoprire come registrare i segreti su GitHub .
- password chiave
- memorizzare la password
- key.jks
Quando registri file binari come key.jks , hai bisogno della codifica base64 per generare una stringa segreta. Non dimenticare di decodificarlo quando lo usi nella pipeline.
Puoi anche usare altri servizi di gestione dei segreti come Azure KeyVault e GCP SecretManager .
Ad ogni modo, devi separare key.jks e qualsiasi altro segreto dal repository del codice sorgente e tenerli al sicuro.
3. Aggiungi flusso di lavoro
Aggiungi flusso di lavoro utilizzando il file yaml.
Crea file yaml come .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 --releaseSalterò la spiegazione sulla configurazione di Java , la configurazione di Flutter pub get , l' analyze , il test .
L'attività di compilazione consiste in alcuni comandi collegati a && . Vediamoli uno per uno.
1echo $KEY_JKS | base64 --decode --ignore-garbage > android/key.jksQuesto comando significa generare il key.jks dai segreti. Come ho spiegato, key.jks è registrato come stringa codificata in base64, quindi qui dobbiamo decodificarlo e key.jks file key.jks. Generando il file, l'app può caricare le informazioni chiave dal file mentre configuriamo il file di file("../key.jks") .
Nell'attività di compilazione, non ho generato il file key.properties Questo perché configuriamo keyPassword e storePassword come $System.env.KEY_PASSWORD .
Abbiamo impostato l'ambiente come di seguito, quindi l'app può caricare la password dall'ambiente di sistema.
Se vuoi leggere la password da key.properties , puoi modificare bundle.gradle e generare il key.properties qui nell'attività di compilazione.
1- name: build dev2 env:3 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}4 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}Quindi, controlliamo il comando di compilazione.
1flutter build apk --releaseQuesto è completamente lo stesso della build locale. Quindi vedrai avvisi che ti dicono che dovresti usare app bundle o split apk per evitare fat apk .
Secondo il sito ufficiale Building the app for release, si consiglia il app bundle Quindi, quando rilasci l'app, è meglio creare un app bundle , tuttavia quando provi l'app sul tuo dispositivo o usi App Distribute di Firebase , hai bisogno di apk .
Quindi consiglio di scegliere il modo in cui costruire la tua app in base allo scopo. Ad esempio, come CI giornaliera, puoi generare apk che ti consente di testare facilmente sul tuo dispositivo e, come build di rilascio, puoi generare app bundle e registrare l'app su Google Play Store .
Gli esempi seguenti forniscono entrambi i tipi di build.
Registra un'app con 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: TesterRegistra un'app con 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. Altre opzioni
4-1. Firebase google-services.json
1echo $GOOGLE_SERVICES > android/app/google-services.jsonPer connetterti con Firebase , hai bisogno di google-services.json .
Puoi generare il json con il comando sopra.
Per maggiori dettagli, vedere FlutterFire - installazione Android.
4-2. App Distribution
App Distribution è una delle funzionalità fornite da Firebase
Puoi consegnare l'app ai tester tramite questa funzione. Se si desidera utilizzare questa funzione, è sufficiente aggiungere l'attività seguente alla pipeline.
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: TesterPer ulteriori informazioni, vedere Azione Github di distribuzione Firebase.
Tieni presente che solo il apk è consentito per la App Distribution .
4-3. Product Flavor
Quando usi product flavor con Flutter , specifica --flavor <flavor name> nel comando build.
Vedi varianti di build per il product flavor
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jpCon flavor , puoi generare diversi ID app con lo stesso codice.
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 }Con flavor , l'id dell'app sarà la combinazione di applicationId e applicationIdSuffix .
Ad esempio, se specifichi --flavor jp , l'ID dell'app sarà xxx.yyy.zzz.jp
Puoi anche modificare il file manifest con flavor .
Supponi di utilizzare Google AdMob , quindi devi aggiungere applicationId al file manifest.
Puoi farlo impostando i file manifest come di seguito.
1android/app/src2 |- main/AndroidManifest.xml3 |- jp/AndroidManifest.xml4 |- eu/AndroidManifest.xmlMetti il file manifest di base in android/app/src/main , quindi metti ogni file manifest dipendente dal flavor android/app/src/<flavor name> .
Quindi l'app creata conterrà sia il manifest principale che il manifest dell'aroma.
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
Questa opzione consente di passare le variabili di ambiente all'app.
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jp --dart-define=REGION=asia-northeast1Questo comando genera un ambiente denominato REGION che descrive la regione del server di backend, ad esempio.
L'app può leggere l'ambiente tramite il codice sottostante.
1String region = String.fromEnvironment('REGION'); // get 'asia-northeast1'4-5. Registra un'app su Google Play Store
Quando registri la tua app su Google Play Store , utilizza questa attività.
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.txtLa prima attività genera service-account.json necessario per accedere a Play Store dal server CI
Quindi specificare questo file nella seconda attività.
Per ulteriori informazioni, vedere upload google play.
Riferimenti
Crea e rilascia un'app Android
Creazione dell'app per il rilascio
FlutterFire - installazione Android




