Aplicativo Android CI/CD com Flutter

Este artigo foi publicado há mais de um ano. As informações podem estar desatualizadas.
CI/CD pipeline é parte essencial do desenvolvimento de software. Este artigo explica como construir um CI/CD pipeline para construir um Android usando o Flutter . Construiremos pipeline com base em GitHub Actions e não usaremos fastlane .
1. Construir localmente
Antes de criar um pipeline no GitHub , vamos construir um aplicativo localmente.
Você pode preparar o ambiente de construção com o site oficial - Construir e lançar um aplicativo Android.
Explicaremos alguns dos conteúdos aqui.
1-1. key.jks e key.properties
Construir um Android requer a chave para assinar. key.jks contém a chave e key.properties contém a senha.
key.jks pode ser gerado pelos comandos abaixo.
janelas
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 keyAo usar esses comandos, você precisa da key password e da store password .
Registre essas senhas no arquivo key.properties
1storePassword=<your password>2keyPassword=<your password>3keyAlias=key4storeFile=C:/Users/USER_NAME/key.jks // depends on your environmentEm seguida, modifique os Android para usar esses arquivos para assinar seu aplicativo.
1-2. app / bundle.gradle
Corrija o arquivo bundle.gradle Observe que há dois bundle.gradle no projeto.
Usamos o arquivo no diretório do 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}O primeiro bloco permite que o aplicativo leia o arquivo key.properties
rootProject.file('key.properties') significa ler key.properties no diretório raiz do projeto.
Se desejar alterar o nome do arquivo ou diretório, você pode modificar esta expressão.
Em seguida, podemos ver dois blocos sob o bloco android
signingConfigs descreve literalmente a configuração para assinatura. Você pode recuperar o conteúdo de key.properties aqui.
Por exemplo, keystoreProperties['keyPassword'] fornece keyPassword valor de key.properties arquivo.
$System.env.KEY_PASSWORD significa que você pode obter o valor do ambiente do sistema. file("../key.jks") significa que você pode carregar dados-chave diretamente do arquivo.
Você pode escolher qualquer expressão que quiser.
A seguir, vamos construir o aplicativo.
1flutter build apk --releaseVocê pode gerar o apk por este comando.
O comando pode avisá-lo para usar o app bundle ou o apk dividido para evitar um apk , mas ignore-os agora. Eu explicarei isso mais tarde.
2. Registre segredos no GitHub
Registre os segredos necessários no GitHub . Precisamos de pelo menos segredos abaixo. Consulte Segredos criptografados para saber como registrar segredos no GitHub .
- senha chave
- senha de armazenamento
- key.jks
Ao registrar arquivos binários como key.jks , você precisa da codificação base64 para gerar a string secreta. Não se esqueça de decodificá-lo ao usá-lo no pipeline.
Você também pode usar outros serviços de gerenciamento de segredo, como Azure KeyVault e GCP SecretManager .
De qualquer forma, você deve separar o key.jks e quaisquer outros segredos do repositório de código-fonte e mantê-los seguros.
3. Adicionar fluxo de trabalho
Adicione o fluxo de trabalho usando o arquivo yaml.
Crie um arquivo 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 --releaseVou pular a explicação sobre a configuração do Java , configuração do Flutter pub get , analyze , test .
A tarefa de construção consiste em alguns comandos conectados com && . Vamos ver um por um.
1echo $KEY_JKS | base64 --decode --ignore-garbage > android/key.jksEste comando significa gerar o key.jks partir de segredos. Como expliquei, key.jks é registrado como string codificada em base64, portanto, precisamos decodificá-lo e key.jks arquivo key.jks. Ao gerar o arquivo, o aplicativo pode carregar informações importantes do arquivo conforme configuramos o arquivo de armazenamento para ser o file("../key.jks") .
Na tarefa de construção, não key.properties arquivo key.properties. Isso ocorre porque configuramos keyPassword e storePassword como $System.env.KEY_PASSWORD .
Definimos o ambiente conforme abaixo, para que o aplicativo possa carregar a senha do ambiente do sistema.
Se desejar ler a senha de key.properties , você pode modificar bundle.gradle e gerar o key.properties aqui na tarefa de construção.
1- name: build dev2 env:3 KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}4 STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}A seguir, vamos verificar o comando build.
1flutter build apk --releaseIsso é completamente igual à construção local. Portanto, você verá avisos que informam que você deve usar o app bundle ou o apk dividido para evitar um apk .
De acordo com o site oficial Construindo o aplicativo para lançamento, o app bundle é recomendado. Portanto, ao lançar o aplicativo, é melhor criar um app bundle ; no entanto, ao testar o aplicativo em seu dispositivo ou usar o App Distribute do Firebase , você precisa do apk .
Portanto, recomendo escolher a forma de construir seu aplicativo de acordo com o propósito. Por exemplo, como CI diária, você pode gerar apk que permite testar facilmente em seu dispositivo, e como compilação de lançamento, você pode gerar app bundle e registrar o aplicativo na Google Play Store .
Os exemplos abaixo fornecem os dois tipos de construção.
Registrar um aplicativo no 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 um aplicativo na 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. Outras opções
4-1. Firebase google-services.json
1echo $GOOGLE_SERVICES > android/app/google-services.jsonPara se conectar ao Firebase , você precisa do google-services.json .
Você pode gerar o json pelo comando acima.
Para obter mais detalhes, consulte FlutterFire - instalação do Android.
4-2. App Distribution
App Distribution é um dos recursos que o Firebase oferece.
Você pode entregar o aplicativo para testadores por meio deste recurso. Se você quiser usar este recurso, basta adicionar a tarefa abaixo ao 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: TesterConsulte Firebase Distribution Github Action para obter mais informações.
Observe que apenas o apk é permitido para o App Distribution .
4-3. Product Flavor
Ao usar o product flavor com Flutter , especifique --flavor <flavor name> no comando de construção.
Consulte variantes de compilação para ver o product flavor
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jpCom o flavor , você pode gerar IDs de aplicativos diferentes com o mesmo 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 }Com o flavor , o id do aplicativo será a combinação de applicationId e applicationIdSuffix .
Por exemplo, se você especificar --flavor jp , o id do aplicativo será xxx.yyy.zzz.jp
Você também pode modificar o arquivo de manifesto com flavor .
Suponha que você usará o Google AdMob , em seguida, precisará adicionar applicationId ao arquivo de manifesto.
Você pode fazer isso definindo arquivos de manifesto como abaixo.
1android/app/src2 |- main/AndroidManifest.xml3 |- jp/AndroidManifest.xml4 |- eu/AndroidManifest.xmlColoque o arquivo de manifesto de base em android/app/src/main , em seguida, coloque cada arquivo de manifesto dependente de flavor android/app/src/<flavor name> .
Em seguida, o aplicativo construído conterá o manifesto principal e o manifesto 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 opção permite que você passe variáveis de ambiente para o aplicativo.
1flutter build appbundle --obfuscate --split-debug-info=build/app/outputs/symbols --release --flavor jp --dart-define=REGION=asia-northeast1Este comando gera um ambiente denominado REGION que descreve a região do servidor backend, por exemplo.
O aplicativo pode ler o ambiente pelo código abaixo.
1String region = String.fromEnvironment('REGION'); // get 'asia-northeast1'4-5. Registre um aplicativo na Google Play Store
Ao registrar seu aplicativo na Google Play Store , use esta tarefa.
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.txtA primeira tarefa gera service-account.json que é necessário para acessar a Play Store do servidor CI
Em seguida, especifique esse arquivo na segunda tarefa.
Consulte upload google play para obter mais informações.
Referências
Construir e lançar um aplicativo Android
Construindo o aplicativo para lançamento
FlutterFire - instalação do Android




