Utilisation des fonctionnalités gratuites des actions Github pour CI / CD sur un projet Flutter

GitHub Actions est un outil pour automatiser les actions de routine avec un référentiel et vous aider à créer un CI / CD pour votre projet.



Les utilisateurs de GitHub disposent de 2000 minutes par mois pour exécuter des actions GitHub sur l'infrastructure de service. Faisons bon usage de ce temps libre.



Je donne des instructions aux développeurs d'applications Flutter: comment exécuter des tests et un analyseur de code pour chaque pull request à l'aide d'actions GitHub, créer un artefact et le déployer pour le tester dans Firebase.







Une personne s'habitue trĂšs rapidement aux bonnes choses. Et il s'y habitue tellement qu'il ne pense mĂȘme pas au fait qu'il n'en a pas toujours Ă©tĂ© ainsi. Comme le dit la vieille anecdote sur un homme et une chĂšvre, la rĂ©alisation de tous les dĂ©lices de la vie se produit au moment mĂȘme oĂč vous ĂȘtes privĂ© de ces mĂȘmes dĂ©lices.



Si votre projet de travail se porte bien avec CI / CD, vous ĂȘtes une personne chanceuse.

Peut-ĂȘtre que vous travaillez dans une startup et avez soigneusement configurĂ© tous les pipelines et hooks de vos propres mains.



Il se peut que toute une Ă©quipe DevOps s'occupe de votre bien-ĂȘtre: chaque mois, elle vous plait avec de nouvelles intĂ©grations, la fusion du temps de construction sous nos yeux et des techniques avancĂ©es pour dĂ©ployer des assemblages dans tous les endroits imaginables et inconcevables.



Ce n'est pas important. L'essentiel est que vous soyez toujours sĂ»r que vos builds sont viables, et en mĂȘme temps vous ĂȘtes vous-mĂȘme soulagĂ© de beaucoup de tĂąches routiniĂšres trĂšs ennuyeuses, dont la pensĂ©e plonge toujours les dĂ©veloppeurs dans la mĂ©lancolie et le dĂ©couragement. En passant, je serai heureux si vous Ă©crivez dans les commentaires Ă  quand remonte la derniĂšre fois que vous avez modifiĂ© manuellement le statut d'un problĂšme dans Jira?



Quitter votre zone de confort



OĂč quittent-ils la zone de confort et, surtout, pourquoi? Beaucoup de raisons. Une connaissance m'a demandĂ© de vous aider Ă  rĂ©diger une petite application pour votre propre bar, vous avez enfin trouvĂ© le temps de mettre en Ɠuvre un projet animalier de vos rĂȘves ou avez dĂ©cidĂ© de libĂ©rer une bibliothĂšque nĂ©e accidentellement dans le cadre du projet. Enfin, vous et votre collĂšgue venez de dĂ©cider d'Ă©crire un petit exemple de projet pour l'atelier.



Je parie que dans n'importe lequel des scénarios, votre inspiration à partir de nouvelles tùches intéressantes se heurtera rapidement à la dure réalité du développement logiciel dans un "environnement sans air" (oui, à un moment donné, vous aurez besoin d'un collecteur intelligent comme l'air).



"CI / CD est difficile ..."



Que vous dites-vous habituellement dans de tels moments? «Je ne comprends pas ça! J'écris juste un frontend / téléphone portable et je ne sais rien de votre Jenkins! " Et si je vous disais que vous n'avez pas besoin de savoir quoi que ce soit de ce genre?



Oui, il vous suffit de pouvoir crĂ©er votre projet Ă  l'aide des commandes de la console - et c'est tout. Vous pouvez grandement simplifier votre vie, mĂȘme s'il s'agit d'un petit projet personnel, et non d'un monstre gĂ©ant multi-modules dĂ©jĂ  difficile Ă  digĂ©rer un IDE.



Github Actions est si simple que mĂȘme votre grand-mĂšre le mettrait en place sans trop de difficultĂ©.



Alors, de quoi parle le post?



Si tout est si simple, pourquoi perdre du temps à lire cet opus? Je répondrai avec une liste à puces:



  • Flutter. CI . , Flutter- . ,

  • . Github Actions —  . 2 000 ( ). - , .

  • . Flutter Android iOS , - . , , , .



c CI/CD , . . ($).


Github Actions, !



Github Actions est un service qui vous permet d'automatiser le flux de travail de votre référentiel. Tout ce que vous faites manuellement avec votre projet - autre que l'écriture directe de code - vous pouvez déléguer aux actions Github. Si vous préférez vous familiariser immédiatement avec les sources primaires, consultez la documentation officielle .



Souvent, nous ne savons mĂȘme pas du tout ce dont nous avons besoin pour automatiser. L'Ă©quipe n'a pas le temps de comprendre l'API complexe du service, puis d'Ă©crire et de dĂ©boguer la solution Ă  partir de zĂ©ro. Marketplace rĂ©sout ce problĂšme : prĂšs de 5000 actions prĂȘtes Ă  l'emploi y sont publiĂ©es, qui rĂ©solvent de nombreuses tĂąches typiques (par exemple, l' envoi de notifications sur les Ă©vĂ©nements dans Telegram , l' analyse des sources du projet pour la dette technique ,dĂ©finir des Ă©tiquettes sur PR en fonction des fichiers modifiĂ©s ). Mauvaise nouvelle: beaucoup d'entre eux sont des shareware - avec des limites d'utilisation assez strictes.



Le processus de travail



Tout dans Github Actions tourne autour des flux de travail . Chaque flux de travail répond à deux questions: que faire et quand le faire.



Que faire . Il existe d'innombrables options ici: vous pouvez crĂ©er, tester et dĂ©ployer vos builds Ă  l'aide de scripts prĂȘts Ă  l'emploi ou créés par vous-mĂȘme. En savoir plus sur la configuration du flux de travail



Quand le faire . Vous pouvez dĂ©clencher des workflows sur des Ă©vĂ©nements qui se produisent dans le rĂ©fĂ©rentiel. CrĂ©er une pull request, pousser une balise de validation ou mĂȘme ajouter une nouvelle Ă©toile Ă  votre projet. Liste complĂšte des hooks



Si le workflow doit ĂȘtre exĂ©cutĂ© non pas sur un Ă©vĂ©nement, mais Ă  un certain moment ou Ă  une certaine frĂ©quence, la syntaxe cron POSIX est Ă  votre disposition .En savoir plus sur les Ă©vĂ©nements rĂ©guliers



Le rĂ©fĂ©rentiel peut avoir autant de flux de travail diffĂ©rents que vous le souhaitez en mĂȘme temps. Chaque flux de travail est dĂ©crit dans un fichier YAML distinct, dont chacun doit ĂȘtre stockĂ© dans le rĂ©pertoire .github / workflows Ă  la racine de votre rĂ©fĂ©rentiel. En savoir plus sur la syntaxe des workflows



Environnement d'exécution



Github Actions propose deux options pour exécuter vos workflows:



  • Github-hosted runners — , . Windows, Linux macOS. , Codemagic, ( ). , , ;

  • Self-hosted runners — , . Github , .



Dans mon article, je me concentrerai sur la premiĂšre option. Nous suivons le chemin le plus simple possible, non?



Configuration du flux de travail de base pour Flutter



Avant de commencer Ă  configurer le flux de travail, nous devons nous mettre d'accord sur deux choses.



PremiÚrement, le rÎle principal du workflow sera de compliquer la décomposition de la base de code. Le code qui ne se construit pas, qui contient des problÚmes potentiels ou qui rompt les tests ne devrait pas entrer dans le courant dominant.



DeuxiÚmement: il peut y avoir des subtilités dans ma configuration qui ne seront pas pertinentes pour votre projet. Je vais essayer de les expliquer. Cependant, si vous utilisez cet article comme guide, empruntez soigneusement.



Enfin, décidons ce que notre flux de travail doit faire. Nous avons besoin d'un plan pour nous aider à avancer dans la bonne direction.



Étape par Ă©tape pour terminer l'assemblage



Le plan ci-dessus peut ĂȘtre utilisĂ© comme liste de contrĂŽle lors de la configuration de votre propre flux de travail. Nous devons:



  1. donnez au flux de travail un nom significatif;
  2. indiquer à quel événement notre workflow commencera;
  3. décider de la machine avec quelle configuration elle démarrera;
  4. Décidez des étapes de notre flux de travail:


  • vĂ©rifier le projet,
  • installer Java;
  • installer Flutter (comme vous vous en souvenez, chaque fois que nous avons une instance propre Ă  notre disposition),
  • tĂ©lĂ©charger des packages de projets,
  • dĂ©marrer un analyseur statique,
  • exĂ©cution de tests,
  • l'assemblage de construction lui-mĂȘme,
  • dĂ©ployez la build Ă  un endroit oĂč les testeurs peuvent l'obtenir.


Maintenant, notre travail a pris une forme tangible. Passons à la mise en Ɠuvre.



À quoi ressemblera notre flux de travail à la toute fin
— . , , .



name: Flutter PR

on:
 pull_request:
   branches:
     - "dev/sprint-**"
   paths-ignore:
     - "docs/**"
     - "openapi/**"
     - ".vscode/**"

jobs:
 build:
   runs-on: ubuntu-latest
   steps:
     - uses: actions/checkout@v1
     - uses: actions/setup-java@v1
       with:
         java-version: "12.x"

     - uses: subosito/flutter-action@v1
       with:
         channel: "stable"

     - run: sh ./scripts/flutter_pub_get.sh

     - run: sh ./scripts/flutter_analyze.sh

     - run: flutter test

     - run: flutter build apk --release

     - uses: actions/upload-artifact@v1
       with:
         name: APK for QA
         path: build/app/outputs/apk/dev/debug/apk_name.apk

     - name: Upload artifact to Firebase App Distribution
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{ secrets.FIREBASE_ANDROID_PROD_APP_ID }}
          token: ${{ secrets.FIREBASE_TOKEN }}
          groups: testers
          file: build/app/outputs/apk/dev/debug/apk_name.apk
          debug: true




Nom



De toute évidence, nous devons nommer notre flux de travail afin que le nom reflÚte son essence aussi précisément que possible. Le nom ( docs ) est la premiÚre chose que nous voyons dans la console Actions lorsque le workflow est exécuté. Pourquoi j'ai nommé mon flux de travail de cette façon, vous le découvrirez dans un instant.







name: Flutter PR


ÉvĂ©nement dĂ©clencheur



Le bloc "on" ( docs ) nous permet de spĂ©cifier un ou plusieurs Ă©vĂ©nements, lors de l'enregistrement desquels nous voulons dĂ©marrer notre workflow. De plus, certains Ă©vĂ©nements peuvent ĂȘtre affinĂ©s. 



Quel événement choisir? Afin de ne pas manquer une ventilation, vous pouvez spécifier au moins tous les événements existants. Ensuite, l'assemblage se déroulera presque continuellement, mais voulons-nous cela? Non, car dans ce cas, la limite de notre plan tarifaire gratuit se terminera fantastiquement rapidement. Nous chercherons la solution optimale.



Supposons que notre projet adhĂšre aux accords selon lesquels le code ne peut pas ĂȘtre poussĂ© directement dans la branche principale du projet - uniquement via la crĂ©ation d'une pull request. Il est logique que notre workflow rĂ©ponde Ă  la crĂ©ation d'une pull request et construise un projet Ă  partir d'une base de code modifiĂ©e:



on: pull_request


$ Cela suffit pour travailler, mais la solution n'est pas encore trÚs optimale. La compilation sera déclenchée à chaque pull request créé. Ceci est redondant, car nous ne sommes intéressés que par les pull requests dirigées vers la branche principale du projet. La syntaxe des actions Github nous permet de spécifier les noms (ou masques) des branches qui nous intéressent.



on:
 pull_request:
   branches:
     - "dev/sprint-**"


$ Et encore une fois, nous cherchons des moyens d'optimiser le processus. Il existe des fichiers qui, mĂȘme en thĂ©orie, ne peuvent pas nuire Ă  votre projet: la documentation du projet, Swagger, le style de code gĂ©nĂ©ral et les paramĂštres IDE. Heureusement, nous avons la possibilitĂ© d'ignorer ces fichiers par le masque de chemin. En consĂ©quence, le bloc «on» ressemblera Ă  ceci:



on:
 pull_request:
   branches:
     - "dev/sprint-**"
   paths-ignore:
     - "docs/**"
     - "drz-swagger/**"
     - ".vscode/**"


Important : ne faites une pull request que si vous ĂȘtes prĂȘt Ă  la fusionner. Chaque poussĂ©e suivante vers une demande d'extraction dĂ©jĂ  créée redĂ©marrera le flux de travail.

Configuration du travail







Enfin, nous sommes prĂȘts Ă  configurer le travail ( docs ). Il est maintenant temps de clarifier le rĂŽle du travail dans le flux de travail.



Chaque flux de travail doit inclure au moins un travail. C'est un travail qui contient une description étape par étape des étapes que nous effectuons avec notre projet. Le nombre de travaux dans un flux de travail n'est pas limité, ainsi que le nombre d'étapes dans un travail. Par défaut, tous les travaux sont exécutés en parallÚle, sauf si la dépendance d'un travail sur les résultats d'un autre est spécifiée. Notre projet aura le seul travail qui sera responsable de la construction du projet.



Mise en place de l'environnement



Chaque fois que le flux de travail s'exécute sur une instance de machine virtuelle propre. La seule chose que nous pouvons choisir est le systÚme d'exploitation qui sera installé sur cette machine. Que choisir?



Il est tentant de choisir macOS, car nous prévoyons de construire une application Flutter pour les plateformes cibles: Android et iOS. Mauvaises nouvelles. Une minute d'utilisation d'une instance avec macOS équivaut à dix (10 !!!) minutes d'utilisation d'une instance avec Ubuntu. Dans une instance avec Windows, dans notre cas, cela ne sert à rien, car il ne sera toujours pas possible d'assembler un assembly iOS là-bas, et son temps d'utilisation est deux fois plus cher qu'une instance avec Ubuntu. En savoir plus sur la facturation



$Comment pouvons-nous nous assurer que nos 2000 minutes gratuites ne se transforment pas en 200? Il n'y a pas de bonne solution. J'ai dĂ©cidĂ© de ne pas crĂ©er de build sur iOS lors de la crĂ©ation d'une pull request. Cela affectera potentiellement la stabilitĂ© de la version iOS. Il existe Ă©galement une option de compromis: crĂ©er une version iOS sur macOS uniquement lors de la modification de pubspec.yaml ou de tout fichier du rĂ©pertoire / ios , dans d'autres cas, ne crĂ©er qu'une version Android sur une instance avec Ubuntu. Cela peut ĂȘtre fait par analogie avec la façon dont nous configurons les fichiers ignorĂ©s pour le bloc «on» .



jobs:
 build:
   runs-on: ubuntu-latest


Vous pouvez voir les spĂ©cifications techniques , ainsi qu'une liste des logiciels installĂ©s «prĂȘts Ă  l'emploi» . Flutter et Java, malheureusement, ne sont pas inclus dans cette liste. Ils devront ĂȘtre installĂ©s manuellement chaque fois que le workflow est exĂ©cutĂ©.



Ne soyez pas pressĂ© de vous fĂącher. Des actions prĂȘtes viendront Ă  notre secours, que nous pouvons utiliser dans les Ă©tapes de notre travail. Nous en utiliserons deux:



  • actions / setup-java - l'action officielle de configuration de l'environnement Java;

  • subosito / flutter-action est une action non officielle de tĂ©lĂ©chargement et d'installation du SDK Flutter. Il a bien fait ses preuves: il vous permet de faire tout ce dont vous avez besoin - par exemple, spĂ©cifier le canal de framework souhaitĂ© ou passer Ă  une version spĂ©cifique du SDK.



steps:
      - uses: actions/setup-java@v1
        with:
          java-version: "12.x"

      - uses: subosito/flutter-action@v1
        with:
          channel: "stable"


Cloner un référentiel



Nous avons une instance propre d'une machine louĂ©e Ă  Github pendant quelques minutes. À l'Ă©tape prĂ©cĂ©dente, nous avons installĂ© tous les logiciels nĂ©cessaires dessus. Nous devons maintenant cloner le rĂ©fĂ©rentiel source de notre projet. Pour ce faire, nous utiliserons un outil prĂȘt Ă  l'emploi:



  • actions / checkout est l'action officielle pour cloner un rĂ©fĂ©rentiel avec une tonne de paramĂštres dont nous n'aurons pas besoin dans la plupart des cas. Puisque le flux de travail s'exĂ©cute directement sur le rĂ©fĂ©rentiel que nous clonons, nous n'avons pas besoin de le spĂ©cifier explicitement.



- uses: actions/checkout@v1


Chargement des dépendances



Jusqu'Ă  prĂ©sent, nous n'avons pas mis en Ɠuvre d'Ă©tapes de nos propres mains, mais uniquement utilisĂ© ce que j'offre des actions toutes faites. Nous passons maintenant Ă  la mise en Ɠuvre de la phase de construction active de notre projet, il est donc temps d'Ă©crire vous-mĂȘme la mise en Ɠuvre de l'Ă©tape.



Avant de construire, nous devons tĂ©lĂ©charger tous les packages rĂ©pertoriĂ©s dans le bloc des dĂ©pendances de notre fichier pubspec.yaml, ainsi que toutes leurs dĂ©pendances transitives. Pour ce faire, le SDK Flutter propose une commande simple prĂȘte Ă  l'emploi flutter pub get. La mise en Ɠuvre de l'Ă©tape peut consister Ă  appeler une commande de terminal. Dans ce cas, l'Ă©tape suivante ne sera appelĂ©e qu'Ă  la fin de cette commande.



- run: flutter pub get


Si votre projet a une structure complexe et contient un certain nombre de packages de flĂ©chettes connectĂ©s localement, vous serez confrontĂ© Ă  un problĂšme. Il est flutter pub getimpossible de construire un projet sans un appel explicite pour chacun de ces packages. Dans mon projet, ces packages sont collectĂ©s dans le dossier / core situĂ© dans le rĂ©pertoire racine. Vous trouverez ci-dessous un script qui rĂ©sout ce problĂšme. Il est dĂ©crit dans le fichier flutter_pub_get.sh dans le dossier / scripts du mĂȘme rĂ©pertoire racine.



flutter pub get
cd core
for dir in */ ; do

    echo ${dir}
    cd ${dir}
    pwd
    flutter pub get
    cd ..
    pwd
    if [ "$#" -gt 0 ]; then shift; fi
    # shift
done


Puisque l'implĂ©mentation de step peut ĂȘtre n'importe quelle commande de terminal, rien ne nous empĂȘche d'exĂ©cuter notre script shell.



- run: sh ./scripts/flutter_pub_get.sh


Analyse de code statique



Flutter nous invite à utiliser une commande intégrée flutter analyzepour exécuter un analyseur statique. Cela aidera à identifier les problÚmes potentiels avec notre base de code à un stade précoce: avant qu'un bogue n'atteigne la production, ou que notre code ne se transforme en un désordre illisible et non pris en charge.



Nous aurions pu profiter de la fonctionnalitĂ© prĂȘte Ă  l'emploi, mais hĂ©las, le comportement d'Ă©quipe par dĂ©faut flutter analyzeprĂ©sente un dĂ©faut qui ruine notre flux de travail au mauvais moment. 



Les problĂšmes dĂ©tectĂ©s par l'analyseur sont classĂ©s en trois niveaux de gravitĂ©: info, avertissement, erreur. Dans ce numĂ©roil est dĂ©crit que mĂȘme si lors de l'analyse seuls des problĂšmes de classe info sont trouvĂ©s (et qu'il ne vaut pas toujours la peine de passer du temps Ă  les rĂ©soudre ici et maintenant), la commande renvoie le code "1", Ă  la suite de quoi votre assembly plantera.



Je suggùre d'utiliser le script suivant comme solution temporaire. À partir de maintenant, l'assemblage ne plantera que s'il y a des problùmes avec le niveau d' erreur :



OUTPUT="$(flutter analyze)"
echo "$OUTPUT"
echo
if grep -q "error ‱" echo "$OUTPUT"; then
    echo "flutter analyze found errors"
    exit 1
else
    echo "flutter analyze didn't find any errors"
    exit 0
fi


Nous exécutons le script shell à l'étape suivante de notre workflow:



- run: sh ./scripts/flutter_analyze.sh


Exécution de tests



Si vous avez des tests dans votre projet, vous ĂȘtes sur la bonne voie! Pour que les tests fonctionnent, il ne suffit pas de les Ă©crire - ils doivent ĂȘtre exĂ©cutĂ©s rĂ©guliĂšrement afin de corriger les failles d'implĂ©mentation Ă  temps ou de les mettre Ă  jour si nĂ©cessaire. Par consĂ©quent, Ă  l'Ă©tape suivante, nous mettrons en Ɠuvre



- run: flutter test


Attention: les classes de test vides ne contenant aucun test implĂ©mentĂ© planteront l'ensemble du workflow. Il n'y a qu'une seule solution: ne dĂ©clarez pas de classes de test tant que vous n'ĂȘtes pas prĂȘt Ă  implĂ©menter au moins un test en son sein.



Construire et signer



Tous les travaux préparatoires sont terminés. Nous avons vérifié que le code ne contient probablement pas de problÚmes évidents. Nous passons maintenant à l'étape la plus importante - la production de l'artefact. En d'autres termes, nous allons créer l'APK.



L'assemblage lui-mĂȘme est extrĂȘmement simple Ă  mettre en Ɠuvre. Nous avons Ă  notre disposition la commande de terminal flutter build, qui est extrĂȘmement configurable et vous permet de crĂ©er un artefact pour une saveur spĂ©cifique, le fichier principal, ABI. Nous ne couvrons pas ces nuances dans l'article, utilisez donc des indicateurs de commande supplĂ©mentaires si nĂ©cessaire.



- run: flutter build apk --release


Notre objectif est de faire signer un assembly avec une clé de version. Et à ce stade, nous devrons résoudre le problÚme de sécurité, car nous devons stocker le keystore de version quelque part, ainsi que tous ses alias et mots de passe.



Github vous permet de stocker en toute sĂ©curitĂ© des valeurs de chaĂźne dans un rĂ©fĂ©rentiel Secrets dĂ©diĂ© . Les donnĂ©es disponibles ici sont stockĂ©es dans le rĂ©fĂ©rentiel appropriĂ© et peuvent ĂȘtre lues par programme Ă  partir de n'importe quelle Ă©tape de votre flux de travail. Dans le mĂȘme temps, les valeurs ne peuvent pas ĂȘtre vues via l'interface Web Github. Seule la suppression ou l'Ă©crasement est autorisĂ©e.







Cela semble ĂȘtre une bonne solution pour les alias et les mots de passe, surtout si vous ĂȘtes votre propre service de sĂ©curitĂ©, mais qu'en est-il du fichier * .jks lui-mĂȘme? Le pousser vers le rĂ©fĂ©rentiel ne semble pas ĂȘtre une bonne idĂ©e, mĂȘme si votre rĂ©fĂ©rentiel est privĂ©. Malheureusement, Github ne fournit aucun moyen sĂ©curisĂ© de stocker des fichiers, vous devez donc Ă©viter.



Ce serait bien de représenter notre fichier keystore sous forme de chaßne. Et c'est réel - il vous suffit de l'encoder en base64. Pour ce faire, ouvrez un terminal dans le répertoire contenant notre fichier * .jks et exécutez la commande suivante. Ensuite, un fichier texte sera créé à partir duquel vous pourrez copier la représentation base64 de notre keystore, puis ... l'enregistrer dans Github Secrets.



openssl base64 < key_store_filename.jks | tr -d '\n' | tee keystore.jks.base64.txt


Maintenant que nous avons tous les composants nécessaires à une signature d'assemblage réussie en place, nous allons procéder à la configuration de l'étape. Dans le bloc env, nous déclarons toutes les variables d'environnement pour cette étape particuliÚre. Nous prendrons les valeurs de ces variables dans Secrets.



- run: flutter build apk --release
        env:
          STORE_PASSWORD: ${{ secrets.STORE_PASSWORD }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
          STORE_FILE: ${{ secrets.STORE_FILE }}


Dans notre hÎte Android, nous devons décrire la configuration de l'assembly de telle sorte que nous ayons la possibilité de signer le fichier * .apk dans CI sans perdre la possibilité de créer l'assembly signé localement. Le fichier keystoreConfig.gradle est responsable de ce moment .



Si le fichier keystore_release.properties est trouvé, on sait que la construction se produit localement, ce qui signifie que vous pouvez initialiser toutes les propriétés keystoreConfig en les lisant simplement à partir du fichier. Sinon, l'assemblage a lieu dans CI, ce qui signifie que la seule source de données sensibles est Github Secrets.



ext {
   def releaseKeystorePropsFile = rootProject.file("keystore/keystore_release.properties")
   if (releaseKeystorePropsFile.exists()) {
       println "Start extract release keystore config from keystore_release.properties"
       def keystoreProps = new Properties()
       keystoreProps.load(new FileInputStream(releaseKeystorePropsFile))
       keystoreConfig = [
               storePassword: keystoreProps['storePassword'],
               keyPassword  : keystoreProps['keyPassword'],
               keyAlias     : keystoreProps['keyAlias'],
               storeFile    : keystoreProps['storeFile']
       ]
   } else {
       println "Start extract release keystore config from global vars"
       keystoreConfig = [
               storePassword: "$System.env.STORE_PASSWORD",
               keyPassword  : "$System.env.KEY_PASSWORD",
               keyAlias     : "$System.env.KEY_ALIAS",
               storeFile    : "$System.env.STORE_FILE"
       ]
   }
   println "Extracted keystore config: $keystoreConfig"
}


Et voici Ă  quoi ressemble le fichier keystore_release.properties :



storePassword={___}
keyPassword={___}
keyAlias={___}
storeFile=../keystore/keystore.jks


La derniÚre étape du fichier build.gradle de notre hÎte Android consiste à appliquer le fichier keystoreConfig à notre configuration de signature de build de version:



android {
   signingConfigs {
       release {
           apply from: '../keystore/keystoreConfig.gradle'

           keyAlias keystoreConfig.keyAlias
           keyPassword keystoreConfig.keyPassword
           storeFile file(keystoreConfig.storeFile)
           storePassword keystoreConfig.storePassword
       }
   }
}


L'assemblage signé est déjà entre nos mains! Mais comment l'étendre à vos collÚgues pour les tests?



Déchargement



Github Actions vous permet de configurer le téléchargement d'artefacts vers presque tous les outils connus pour la distribution d'assemblys, mais nous n'envisagerons que deux options:



  • Stockage Github - le moyen le plus simple de tĂ©lĂ©charger des assemblys sur votre propre stockage Github, qui fonctionne hors de la boĂźte, mais prĂ©sente certaines limitations;

  • Firebase App Distribution est un service de l'Ă©cosystĂšme Firebase qui a remplacĂ© la version bĂȘta par Crashlytics. L'intĂ©gration est un peu plus difficile Ă  configurer, mais le service lui-mĂȘme est beaucoup plus pratique Ă  utiliser.





Github Storage

Github Storage s'intÚgre facilement grùce à l'action officielle. Il vous suffit de spécifier le nom de l'assembly tel que vos collÚgues le verront dans l'interface Web, ainsi que le chemin du fichier * .apk compilé dans CI.



- uses: actions/upload-artifact@v1
        with:
          name: APK for QA
          path: build/app/outputs/apk/dev/debug/apk_name.apk


Le problÚme principal est l'espace de stockage limité. Sur le plan gratuit, vous ne disposez que de 500 Mo. Le plus étrange, c'est que je n'ai trouvé aucun moyen d'effacer manuellement tout le stockage en une seule fois via l'interface Web, alors je me suis sorti de la situation en ... écrivant un flux de travail séparé chargé uniquement de nettoyer le stockage des artefacts moussus.



Le workflow s'exécute tous les jours à 1 h du matin et supprime tous les artefacts de plus d'une semaine:



name: Github Storage clear

on:
  schedule:
    - cron: '0 1 * * *'

jobs:
  remove-old-artifacts:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Remove old artifacts
        uses: c-hive/gha-remove-artifacts@v1
        with:
          age: '1 week'


Firebase App Distribution

Comme pour Firebase App Distribution, j'ai utilisĂ© l'action prĂȘte Ă  l'emploi wzieba / Firebase-Distribution-Github-Action pour l'intĂ©grer



- name: Upload artifact to Firebase App Distribution
        uses: wzieba/Firebase-Distribution-Github-Action@v1
        with:
          appId: ${{ secrets.FIREBASE_ANDROID_PROD_APP_ID }}
          token: ${{ secrets.FIREBASE_TOKEN }}
          groups: testers
          file: build/app/outputs/apk/dev/debug/apk_name.apk
          debug: true


Pour que l'action fonctionne correctement, elle doit passer des paramĂštres:



  • appId - identificateur d'application, qui peut ĂȘtre trouvĂ© dans les paramĂštres du projet Firebase;







  • token - un jeton pour l'authentification Ă  votre projet FIrebase, qui est requis pour charger l'assembly dans le service. Vous pouvez obtenir un jeton uniquement via la CLI Firebase, pour laquelle vous pouvez en savoir plus dans la documentation officielle ;

  • file - chemin vers le fichier * .apk compilĂ© sur CI;

  • groups - ce paramĂštre est facultatif, mais vous permet de spĂ©cifier l'alias du groupe de testeurs vers lequel l'assembly tĂ©lĂ©chargĂ© sera automatiquement partagĂ©.



Lancement et observation



Notre flux de travail le plus simple est prĂȘt! Il ne nous reste plus qu'Ă  dĂ©clencher l'Ă©vĂ©nement dĂ©clencheur et Ă  observer la progression du flux de travail.



Conseils et mots d'adieu



Vous pouvez désormais profiter de tous les avantages d'un simple mécanisme CI / CD sur votre projet Flutter, quelle que soit sa taille, son intensité de développement ou votre portefeuille.



Enfin, voici quelques conseils et observations que j'ai formulés en travaillant sur ce flux de travail:



  • workflow . workflow , , . - . , - workflow , .

  • step’ shell-. workflow . . .

  • Run Duration workflow. workflow , . workflow , step’. . Flutter SDK . — 5-6 .



Il y a encore des tonnes d'amĂ©liorations et d'amĂ©liorations potentielles Ă  venir. Écrivez dans les commentaires vos idĂ©es pour amĂ©liorer le flux de travail. Que vous manque-t-il personnellement? Peut-ĂȘtre que la mise en Ɠuvre des idĂ©es les plus intĂ©ressantes des lecteurs constituera la base du prochain article sur le sujet.



Tous les scripts et flux de travail se trouvent dans le référentiel avec l'application de test .



Merci de votre attention.



PS Notre équipe Surf publie de nombreuses bibliothÚques utiles pour Flutter. Nous les téléchargeons dans le référentiel SurfGear .



All Articles