Aplicación de Android CI/CD con Flutter

Este artículo fue publicado hace más de un año. La información puede estar desactualizada.
CI/CD pipeline es una parte esencial del desarrollo de software. Este artículo explica cómo construir una CI/CD pipeline para crear una aplicación de Android Flutter . Construiremos una canalización basada en GitHub Actions y no usaremos fastlane .
1. Construye localmente
Antes de crear una canalización en GitHub , creemos una aplicación localmente.
Puede preparar el entorno de compilación con el sitio web oficial: Cree y publique una aplicación de Android.
Explicaremos algunos de los contenidos aquí.
1-1. key.jks y key.properties
La creación de una Android requiere la clave para firmar. key.jks contiene la clave y key.properties contiene la contraseña.
key.jks puede ser generado por los siguientes comandos.
Ventanas
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 keyCuando usa estos comandos, necesita key password y una store password .
Registre estas contraseñas en el archivo key.properties
1storePassword=<your password>2keyPassword=<your password>3keyAlias=key4storeFile=C:/Users/USER_NAME/key.jks // depends on your environmentLuego, modifique los Android para usar estos archivos para firmar su aplicación.
1-2. app / bundle.gradle
bundle.gradle archivo bundle.gradle. Tenga en cuenta que hay dos bundle.gradle en el proyecto.
Usamos el archivo en el directorio de la 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}El primer bloque permite que la aplicación lea el archivo key.properties
rootProject.file('key.properties') significa leer key.properties en el directorio raíz del proyecto.
Si desea cambiar el nombre del archivo o el directorio, puede modificar esta expresión.
A continuación, podemos ver dos bloques debajo del bloque de android
signingConfigs describe literalmente la configuración para firmar. Puede recuperar el contenido de key.properties aquí.
Por ejemplo, keystoreProperties['keyPassword'] proporciona keyPassword valor de key.properties archivo.
$System.env.KEY_PASSWORD significa que puede obtener el valor del entorno del sistema. file("../key.jks") significa que puede cargar datos clave directamente desde el archivo.
Puede elegir la expresión que desee.
A continuación, creemos la aplicación.
1flutter build apk --releasePuede generar un apk con este comando.
El comando puede advertirle que use el app bundle o la apk dividida para evitar la apk grasa, pero ignórelos ahora mismo. Te lo explicaré más tarde.
2. Registrar secretos en GitHub
Registre los secretos necesarios en GitHub . Necesitamos al menos los secretos a continuación. Consulte Secretos cifrados para aprender cómo registrar secretos en GitHub .
- contraseña clave
- almacenar contraseña
- key.jks
Cuando registra archivos binarios como key.jks , necesita codificación base64 para generar una cadena secreta. No olvide decodificarlo cuando lo use en la tubería.
También puede usar otros servicios de administración de secretos como Azure KeyVault y GCP SecretManager .
De todos modos, debe separar key.jks y cualquier otro secreto del repositorio de código fuente y mantenerlos seguros.
3. Agregar flujo de trabajo
Agregue flujo de trabajo usando el archivo yaml.
Cree un archivo yaml como .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 --releaseSaltaré la explicación sobre Java configuración de Java, la configuración de Flutter pub get , el analyze y la test .
La tarea de compilación consta de algunos comandos conectados con && . Veamos uno por uno.
1echo $KEY_JKS | base64 --decode --ignore-garbage > android/key.jksEste comando significa generar key.jks partir de secretos. Como expliqué, key.jks está registrado como una cadena codificada en base64, por lo que aquí debemos decodificarlo y volver a generar el archivo key.jks Al generar el archivo, la aplicación puede cargar información clave desde el archivo mientras configuramos el archivo de la tienda para que sea un file("../key.jks") .
En la tarea de compilación, no key.properties archivo key.properties. Esto se debe a que configuramos keyPassword y storePassword como $System.env.KEY_PASSWORD .
Hemos configurado el entorno como se muestra a continuación, por lo que la aplicación puede cargar la contraseña desde el entorno del sistema.
Si desea leer la contraseña de key.properties , puede modificar bundle.gradle y generar el key.properties aquí en la tarea de compilación.
1- name: build dev2 env:3 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}4 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}A continuación, verifiquemos el comando de compilación.
1flutter build apk --releaseEsto es completamente igual que la construcción local. Entonces verá advertencias que le indican que debe usar el app bundle o la apk dividida para evitar la apk grasa.
Según el sitio oficial Creación de la aplicación para su lanzamiento, se recomienda el app bundle Entonces, cuando lanza la aplicación, es mejor crear un app bundle , sin embargo, cuando prueba la aplicación en su dispositivo o usa App Distribute de Firebase , necesita apk .
Por lo que recomiendo elegir la forma de construir su aplicación de acuerdo con el propósito. Por ejemplo, como CI diaria, puede generar apk que le permite probar fácilmente en su dispositivo, y como compilación de lanzamiento, puede generar un app bundle y registrar la aplicación en Google Play Store .
Los ejemplos siguientes proporcionan ambos tipos de construcción.
Registre una aplicación 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: TesterRegistre una aplicación en 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. Otras opciones
4-1. Firebase google-services.json
1echo $GOOGLE_SERVICES > android/app/google-services.jsonPara conectarse con Firebase , necesita google-services.json .
Puede generar el json con el comando anterior.
Para obtener más detalles, consulte FlutterFire - instalación de Android.
4-2. App Distribution
App Distribution es una de las funciones que ofrece Firebase
Puede entregar la aplicación a los probadores mediante esta función. Si desea utilizar esta función, simplemente agregue la siguiente tarea a la canalización.
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: TesterConsulta Acción de Github de distribución de Firebase para obtener más información.
Tenga en cuenta que solo se permite el archivo apk App Distribution .
4-3. Product Flavor
Cuando use product flavor con Flutter , especifique --flavor <flavor name> en el comando de construcción.
Consulte variantes de compilación para conocer product flavor
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jpCon flavor , puede generar diferentes ID de aplicación con el mismo código.
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 , el ID de la aplicación será la combinación de applicationId y applicationIdSuffix .
Por ejemplo, si especifica --flavor jp , el ID de la aplicación será xxx.yyy.zzz.jp
También puede modificar el archivo de manifiesto con flavor .
Suponga que va a utilizar Google AdMob , luego debe agregar applicationId al archivo de manifiesto.
Puede hacer esto configurando archivos de manifiesto como se muestra a continuación.
1android/app/src2 |- main/AndroidManifest.xml3 |- jp/AndroidManifest.xml4 |- eu/AndroidManifest.xmlColoque el archivo de manifiesto base en android/app/src/main , luego coloque cada archivo de manifiesto dependiente del flavor android/app/src/<flavor name> .
Luego, la aplicación compilada contendrá tanto el manifiesto principal como el manifiesto de sabor.
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
Esta opción le permite pasar variables de entorno a la aplicación.
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jp --dart-define=REGION=asia-northeast1Este comando genera un entorno llamado REGION que describe la región del servidor backend, por ejemplo.
La aplicación puede leer el entorno mediante el siguiente código.
1String region = String.fromEnvironment('REGION'); // get 'asia-northeast1'4-5. Registra una aplicación en Google Play Store
Cuando registre su aplicación en Google Play Store , utilice esta tarea.
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 primera tarea genera service-account.json que es necesario para acceder a Play Store desde el servidor CI
Luego especifique este archivo en la segunda tarea.
Consulte upload google play para obtener más información.
Referencias
Cree y publique una aplicación de Android
Creación de la aplicación para su lanzamiento
FlutterFire - instalación de Android




