Verwenden der kostenlosen Funktionen von Github Actions für CI / CD in einem Flutter-Projekt

GitHub Actions ist ein Tool zum Automatisieren von Routineaktionen mit einem Repository und zum Erstellen einer CI / CD für Ihr Projekt.



GitHub-Benutzer erhalten 2000 Minuten pro Monat, um GitHub-Aktionen in der Dienstinfrastruktur auszuführen. Lassen Sie uns diese Freizeit gut nutzen.



Ich gebe Entwicklern von Flutter-Anwendungen Anweisungen: Ausführen von Tests und eines Code-Analysators für jede Pull-Anforderung mithilfe von GitHub-Aktionen, Erstellen eines Artefakts und Bereitstellen zum Testen in Firebase.







Ein Mensch gewöhnt sich sehr schnell an gute Dinge. Und er gewöhnt sich so sehr daran, dass er nicht einmal daran denkt, dass es nicht immer so war. Wie die alte Anekdote über einen Mann und eine Ziege sagt, erfolgt die Verwirklichung aller Freuden des Lebens in dem Moment, in dem Ihnen diese Freuden vorenthalten werden.



Wenn Ihr Arbeitsprojekt mit CI / CD gut läuft, sind Sie ein Glückspilz.

Vielleicht arbeiten Sie in einem Startup und haben alle Pipelines und Hooks sorgfältig mit Ihren eigenen Händen konfiguriert.



Es kann sein, dass sich ein ganzes DevOps-Team um Ihr Wohlbefinden kümmert: Jeden Monat erfreut es Sie mit neuen Integrationen, vor unseren Augen schmelzenden Bauzeiten und fortschrittlichen Techniken für den Einsatz von Baugruppen an allen erdenklichen und unvorstellbaren Orten.



Unwichtig. Die Hauptsache ist, dass Sie immer sicher sind, dass Ihre Builds rentabel sind, und gleichzeitig selbst von vielen sehr langweiligen Routineaufgaben befreit sind, deren Gedanke Entwickler immer wieder in Melancholie und Niedergeschlagenheit stürzt. Übrigens, ich würde mich freuen, wenn Sie in die Kommentare schreiben, wann Sie den Status eines Problems in Jira das letzte Mal manuell geändert haben.



Verlassen Sie Ihre Komfortzone



Wo verlassen sie die Komfortzone und vor allem warum? Viele Gründe. Ein Bekannter bat mich, beim Schreiben einer kleinen Anwendung für Ihre eigene Bar zu helfen. Sie fanden endlich die Zeit, ein Haustierprojekt Ihrer Träume umzusetzen, oder beschlossen, eine Bibliothek freizugeben, die versehentlich als Teil des Projekts geboren wurde. Schließlich haben Sie und Ihr Kollege beschlossen, ein kleines Beispielprojekt für den Workshop zu schreiben.



Ich wette, dass in jedem der Szenarien Ihre Inspiration aus neuen interessanten Aufgaben schnell mit der harten Realität der Softwareentwicklung in einer "luftlosen Umgebung" kollidiert (ja, irgendwann benötigen Sie einen intelligenten Sammler wie Luft).



"CI / CD ist schwierig ..."



Was sagst du dir normalerweise in solchen Momenten? „Ich verstehe das nicht! Ich schreibe gerade ein Frontend / Handy und weiß nichts über deine Jenkins! " Was wäre, wenn ich dir sagen würde, dass du so etwas nicht wissen musst?



Ja, Sie müssen nur in der Lage sein, Ihr Projekt mithilfe von Konsolenbefehlen zu erstellen - und das war's. Sie können Ihr Leben erheblich vereinfachen, selbst wenn es sich um ein persönliches kleines Projekt handelt und nicht um ein riesiges Monster mit mehreren Modulen, das bereits schwer zu verdauen ist.



Github Actions ist so einfach, dass selbst Ihre Großmutter es ohne große Schwierigkeiten einrichten würde.



Worum geht es dann in der Post?



Wenn alles so einfach ist, warum Zeit damit verschwenden, dieses Werk zu lesen? Ich werde mit einer Liste mit Aufzählungszeichen antworten:



  • Flutter. CI . , Flutter- . ,

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

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



c CI/CD , . . ($).


Github Actions, !



Github Actions ist ein Dienst, mit dem Sie Ihren Repository-Workflow automatisieren können. Alles, was Sie manuell mit Ihrem Projekt tun - außer Code direkt zu schreiben - können Sie an Github-Aktionen delegieren. Wenn Sie die primären Quellen sofort kennenlernen möchten, lesen Sie die offizielle Dokumentation .



Oft wissen wir gar nicht, was wir überhaupt automatisieren müssen. Das Team hat keine Zeit, die komplexe API des Dienstes zu verstehen und die Lösung dann von Grund auf neu zu schreiben und zu debuggen. Der Marktplatz löst dieses Problem : Dort werden fast 5.000 vorgefertigte Aktionen veröffentlicht, die viele typische Aufgaben lösen (z. B. Senden von Benachrichtigungen über Ereignisse in Telegramm , Analysieren von Projektquellen auf technische Schulden ,Festlegen von Beschriftungen für PR in Abhängigkeit von den darin geänderten Dateien ). Schlechte Nachrichten: Viele von ihnen sind Shareware - mit ziemlich strengen Nutzungsbeschränkungen.



Der Arbeitsprozess



Alles in Github Actions dreht sich um Workflows . Jeder Workflow beantwortet zwei Fragen: Was ist zu tun und wann?



Was ist zu tun . Hier gibt es unzählige Möglichkeiten: Sie können Ihre Builds mit vorgefertigten oder selbst erstellten Skripten erstellen, testen und bereitstellen. Erfahren Sie mehr über Workflow - Konfiguration



Wenn es zu tun . Sie können Workflows für Ereignisse auslösen, die im Repository auftreten. Erstellen einer Pull-Anfrage, Drücken eines Commit-Tags oder Hinzufügen eines neuen Sterns zu Ihrem Projekt. Vollständige Liste der Hooks



Wenn der Workflow nicht für ein Ereignis, sondern zu einem bestimmten Zeitpunkt oder mit einer bestimmten Häufigkeit ausgeführt werden soll, steht Ihnen die POSIX-Cron- Syntax zur Verfügung .Weitere Informationen zu regulären Ereignissen



Im Repository können so viele verschiedene Workflows gleichzeitig vorhanden sein. Jeder Workflow wird in einer separaten YAML-Datei beschrieben, die jeweils im Verzeichnis .github / workflows im Stammverzeichnis Ihres Repositorys gespeichert werden sollte. Erfahren Sie mehr über die Syntax von Workflows



Laufzeitumgebung



Github Actions bietet zwei Optionen zum Ausführen Ihrer Workflows:



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

  • Self-hosted runners — , . Github , .



In meinem Artikel werde ich mich auf die erste Option konzentrieren. Wir sind auf dem einfachsten Weg, oder?



Einrichten des grundlegenden Workflows für Flutter



Bevor wir mit der Konfiguration des Workflows beginnen, müssen wir uns auf zwei Dinge einigen.



Erstens besteht die Hauptaufgabe des Workflows darin, die Aufschlüsselung der Codebasis zu erschweren. Code, der nicht erstellt wird, potenzielle Probleme enthält oder Tests unterbricht, sollte nicht in den Mainstream gelangen.



Zweitens: In meiner Konfiguration sind möglicherweise einige Feinheiten enthalten, die für Ihr Projekt nicht relevant sind. Ich werde versuchen, sie zu erklären. Wenn Sie diesen Artikel jedoch als Leitfaden verwenden, leihen Sie ihn sorgfältig aus.



Lassen Sie uns abschließend entscheiden, was unser Workflow überhaupt tun soll. Wir brauchen einen Plan, der uns hilft, in die richtige Richtung zu gehen.



Schritt für Schritt zur fertigen Baugruppe



Der obige Plan kann als Checkliste beim Einrichten Ihres eigenen Workflows verwendet werden. Wir müssen:



  1. Geben Sie dem Workflow einen aussagekräftigen Namen.
  2. Geben Sie an, bei welchem ​​Ereignis unser Workflow gestartet wird.
  3. Entscheiden Sie, mit welcher Konfiguration die Maschine gestartet werden soll.
  4. Entscheiden Sie, aus welchen Schritten unser Workflow bestehen soll:


  • Kasse das Projekt,
  • Java installieren;
  • Installieren von Flutter (wie Sie sich erinnern, jedes Mal, wenn wir eine saubere Instanz zur Verfügung haben),
  • Projektpakete herunterladen,
  • Starten eines statischen Analysators,
  • Tests durchführen,
  • die Build-Assembly selbst,
  • Stellen Sie den Build an einem Ort bereit, an dem Tester ihn erhalten können.


Jetzt hat unsere Arbeit eine greifbare Form angenommen. Fahren wir mit der Implementierung fort.



Wie unser Workflow am Ende aussehen wird
— . , , .



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




Name



Natürlich müssen wir unseren Workflow benennen, damit der Name seine Essenz so genau wie möglich widerspiegelt. Der Name ( docs ) ist das erste, was wir in der Aktionskonsole sehen, wenn der Workflow ausgeführt wird. Warum ich meinen Workflow so benannt habe, werden Sie gleich herausfinden.







name: Flutter PR


Auslöseereignis



Mit dem Block "Ein" ( Dokumente ) können wir ein oder mehrere Ereignisse angeben, bei deren Registrierung wir unseren Workflow starten möchten. Darüber hinaus können einige Ereignisse genau abgestimmt werden. 



Welche Veranstaltung soll ich wählen? Um eine Aufschlüsselung nicht zu verpassen, können Sie mindestens alle vorhandenen Ereignisse angeben. Dann wird die Montage fast ununterbrochen verlaufen, aber wollen wir das? Nein, da in diesem Fall die Begrenzung unseres kostenlosen Tarifplans fantastisch schnell endet. Wir werden nach der optimalen Lösung suchen.



Nehmen wir an, unser Projekt hält sich an die Vereinbarungen, nach denen der Code nicht direkt in den Hauptzweig des Projekts verschoben werden kann - nur durch die Erstellung einer Pull-Anfrage. Es ist logisch, ob unser Workflow auf die Erstellung einer Pull-Anforderung reagiert und ein Projekt aus einer geänderten Codebasis erstellt:



on: pull_request


$ Dies ist genug für die Arbeit, aber die Lösung ist noch nicht sehr optimal. Der Build wird bei jeder erstellten Pull-Anforderung ausgelöst. Dies ist überflüssig, da wir nur an Pull-Anfragen interessiert sind, die an den Hauptzweig des Projekts gerichtet sind. Mit der Syntax für Github-Aktionen können wir die Namen (oder Masken) der Zweige angeben, an denen wir interessiert sind.



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


$ Und wieder suchen wir nach Möglichkeiten, den Prozess zu optimieren. Es gibt Dateien, die Ihrem Projekt auch theoretisch keinen Schaden zufügen können: Projektdokumentation, Swagger, allgemeine Codestil- und IDE-Einstellungen. Glücklicherweise haben wir die Möglichkeit, solche Dateien über die Pfadmaske zu ignorieren. Infolgedessen sieht der "Ein" -Block folgendermaßen aus:



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


Wichtig : Stellen Sie eine Pull-Anfrage nur, wenn Sie bereit sind, sie zusammenzuführen. Bei jedem nächsten Push auf eine bereits erstellte Pull-Anforderung wird der Workflow neu gestartet.

Jobkonfiguration







Schließlich können wir den Job ( Dokumente ) konfigurieren . Jetzt ist es an der Zeit zu klären, welche Rolle der Job im Workflow spielt.



Jeder Workflow muss mindestens einen Job enthalten. Es ist ein Job, der eine schrittweise Beschreibung der Schritte enthält , die wir mit unserem Projekt ausführen. Die Anzahl der Jobs in einem Workflow ist nicht begrenzt, ebenso wie die Anzahl der Schritte in einem Job. Standardmäßig werden alle Jobs parallel ausgeführt, es sei denn, die Abhängigkeit eines Jobs von den Ergebnissen eines anderen ist angegeben. Unser Projekt wird einen einzigen Job haben, der für den Aufbau des Projekts verantwortlich ist.



Umgebung einrichten



Jedes Mal, wenn der Workflow auf einer sauberen Instanz einer virtuellen Maschine ausgeführt wird. Wir können nur das Betriebssystem auswählen, das auf diesem Computer installiert wird. Was auszusuchen?



Es ist verlockend, sich für macOS zu entscheiden, da wir planen, eine Flutter-Anwendung für Zielplattformen zu erstellen: Android und iOS. Schlechte Nachrichten. Eine Minute für die Verwendung einer Instanz mit macOS wird als zehn (10 !!!) Minuten für die Verwendung einer Instanz mit Ubuntu berechnet. In einer Instanz mit Windows macht es in unserem Fall überhaupt keinen Sinn, da eine iOS-Assembly dort immer noch nicht funktioniert und die Nutzungsdauer doppelt so hoch ist wie bei einer Instanz mit Ubuntu. Weitere Informationen zur Abrechnung von



US-DollarWie stellen wir sicher, dass aus unseren 2.000 Freiminuten keine 200 werden? Es gibt keine gute Lösung. Ich habe beschlossen, das Erstellen des Builds auf iOS zu überspringen, wenn eine Pull-Anforderung erstellt wird. Dies wird möglicherweise die Stabilität des iOS-Builds beeinträchtigen. Es gibt auch eine Kompromissoption: Sie können einen iOS-Build nur unter macOS erstellen, wenn Sie pubspec.yaml oder eine beliebige Datei aus dem Verzeichnis / ios ändern. In anderen Fällen erstellen Sie nur einen Android-Build auf einer Instanz mit Ubuntu. Dies kann analog dazu erfolgen, wie wir die Ignorierdateien für den "on" -Block einrichten .



jobs:
 build:
   runs-on: ubuntu-latest


Sie sehen die technischen Daten sowie eine Liste der installierten "Out-of-the-Box" -Software . Flutter und Java sind leider nicht in dieser Liste enthalten. Sie müssen bei jeder Ausführung des Workflows manuell installiert werden.



Beeilen Sie sich nicht, sich aufzuregen. Zu unserer Rettung werden geeignete Maßnahmen kommen, die wir in den Schritten unserer Arbeit einsetzen können. Wir werden zwei verwenden:



  • Actions / Setup-Java - die offizielle Aktion zum Einrichten der Java-Umgebung;

  • subosito / flutter-action ist eine inoffizielle Aktion zum Herunterladen und Installieren des Flutter SDK. Es hat sich bewährt: Sie können alles tun, was Sie benötigen - beispielsweise den gewünschten Framework-Kanal angeben oder zu einer bestimmten SDK-Version wechseln.



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

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


Klonen eines Repositorys



Wir haben eine saubere Instanz einer Maschine, die für einige Minuten bei Github gemietet wurde. Im vorherigen Schritt haben wir die gesamte erforderliche Software darauf installiert. Jetzt müssen wir das Quell-Repository für unser Projekt klonen. Dazu verwenden wir ein fertiges Tool:



  • Actions / Checkout ist die offizielle Aktion zum Klonen eines Repositorys mit einer Vielzahl von Einstellungen, die wir in den meisten Fällen nicht benötigen. Da der Workflow direkt auf dem Repository ausgeführt wird, das wir klonen, müssen wir ihn nicht explizit angeben.



- uses: actions/checkout@v1


Abhängigkeiten laden



Bis zu diesem Punkt haben wir keine Schritte mit unseren eigenen Händen implementiert, sondern nur das verwendet, was ich vorgefertigte Aktionen anbiete. Jetzt gehen wir zur Implementierung der aktiven Phase des Aufbaus unseres Projekts über, daher ist es Zeit, die Implementierung des Schritts selbst zu schreiben.



Vor dem Erstellen müssen wir alle Pakete herunterladen, die im Abhängigkeitsblock unserer Datei pubspec.yaml angegeben sind, sowie alle ihre transitiven Abhängigkeiten. Zu diesem Zweck bietet das Flutter SDK einen einfachen sofort einsatzbereiten Befehl flutter pub get. Die Implementierung von step kann darin bestehen, einen Terminalbefehl aufzurufen. In diesem Fall wird der nächste Schritt erst nach Abschluss dieses Befehls aufgerufen.



- run: flutter pub get


Wenn Ihr Projekt eine komplexe Struktur hat und eine Reihe von Dart-Paketen enthält, die lokal verbunden sind, treten Probleme auf. Es ist flutter pub getunmöglich, ein Projekt ohne einen expliziten Aufruf für jedes dieser Pakete zu erstellen. In meinem Projekt werden solche Pakete im Ordner / core im Stammverzeichnis gesammelt. Unten finden Sie ein Skript, das dieses Problem löst. Es wird in der Datei flutter_pub_get.sh im Ordner / scripts im selben Stammverzeichnis beschrieben.



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


Da die Implementierung von step ein beliebiger Terminalbefehl sein kann, hindert uns nichts daran, unser Shell-Skript auszuführen.



- run: sh ./scripts/flutter_pub_get.sh


Statische Code-Analyse



Flutter lädt uns ein, einen integrierten Befehl flutter analyzezum Ausführen eines statischen Analysators zu verwenden. Dies wird dazu beitragen, potenzielle Probleme mit unserer Codebasis frühzeitig zu erkennen: Bevor ein Fehler die Produktion erreicht oder unser Code zu einem unlesbaren und nicht unterstützten Durcheinander wird.



Wir hätten die Box-Funktion ohne Änderungen nutzen können, aber leider weist das Standard-Teamverhalten flutter analyzeeinen Fehler auf, der unseren Workflow zur falschen Zeit ruiniert. 



Vom Analysator festgestellte Probleme werden in drei Schweregrade eingeteilt: Info, Warnung, Fehler. In dieser AusgabeEs wird beschrieben, dass der Befehl den Code "1" zurückgibt, selbst wenn während der Analyse nur Probleme mit Infoklassen gefunden werden (und es sich nicht immer lohnt, Zeit damit zu verbringen, diese hier und jetzt zu beheben), wodurch Ihre Assembly abstürzt.



Ich schlage vor, das folgende Skript als temporäre Lösung zu verwenden. Von nun an stürzt die Assembly nur ab, wenn Probleme mit der Fehlerstufe auftreten :



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


Wir führen das Shell-Skript im nächsten Schritt unseres Workflows aus:



- run: sh ./scripts/flutter_analyze.sh


Ausführen von Tests



Wenn Sie Tests in Ihrem Projekt haben, sind Sie auf dem richtigen Weg! Damit die Tests funktionieren, reicht es nicht aus, sie zu schreiben. Sie müssen regelmäßig ausgeführt werden, um Implementierungsfehler rechtzeitig zu beheben oder gegebenenfalls zu aktualisieren. Daher werden wir im nächsten Schritt implementieren



- run: flutter test


Seien Sie vorsichtig: Leere Testklassen, die keine implementierten Tests enthalten, stürzen den gesamten Workflow ab. Es gibt nur einen Ausweg: Deklarieren Sie keine Testklassen, bis Sie bereit sind, mindestens einen Test darin zu implementieren.



Bauen und unterschreiben



Alle Vorarbeiten sind beendet. Wir haben überprüft, dass der Code höchstwahrscheinlich keine offensichtlichen Probleme enthält. Nun kommen wir zur wichtigsten Phase - der Herstellung des Artefakts. Mit anderen Worten, wir werden die APK bauen.



Die Baugruppe selbst ist äußerst einfach zu implementieren. Wir verfügen über den Terminal Command Flutter Build, der extrem tief konfigurierbar ist und es Ihnen ermöglicht, ein Artefakt für eine bestimmte Geschmacksrichtung, Hauptdatei, ABI, zu erstellen. Wir behandeln diese Nuancen im Artikel nicht. Verwenden Sie daher bei Bedarf zusätzliche Befehlsflags.



- run: flutter build apk --release


Unser Ziel ist es, eine Baugruppe mit einem Freigabeschlüssel signieren zu lassen. In diesem Stadium müssen wir das Sicherheitsproblem lösen, da wir den Release-Keystore sowie alle seine Aliase und Kennwörter irgendwo speichern müssen.



Mit Github können Sie Zeichenfolgenwerte sicher in einem dedizierten Secrets- Repository speichern . Die hier verfügbaren Daten werden im entsprechenden Repository gespeichert und können programmgesteuert aus jedem Schritt Ihres Workflows gelesen werden. Gleichzeitig können die Werte nicht über die Github-Weboberfläche angezeigt werden. Nur Löschen oder Überschreiben ist zulässig.







Dies scheint eine gute Lösung für Aliase und Kennwörter zu sein, insbesondere wenn Sie Ihr eigener Sicherheitsdienst sind, aber was ist mit der * .jks-Datei selbst? Das Verschieben in das Repository scheint keine gute Idee zu sein, selbst wenn Ihr Repository privat ist. Leider bietet Github keine sichere Möglichkeit zum Speichern von Dateien, sodass Sie ausweichen müssen.



Es wäre schön, unsere Keystore-Datei als Zeichenfolge darzustellen. Und es ist real - Sie müssen es nur in base64 codieren. Öffnen Sie dazu ein Terminal in dem Verzeichnis, das unsere * .jks-Datei enthält, und führen Sie den folgenden Befehl aus. Als nächstes wird eine Textdatei erstellt, aus der Sie die base64-Darstellung unseres Keystores kopieren und dann ... in Github Secrets speichern können.



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


Nachdem wir alle erforderlichen Komponenten für eine erfolgreiche Baugruppensignatur eingerichtet haben, fahren wir mit der Konfiguration des Schritts fort. Im env-Block deklarieren wir alle Umgebungsvariablen für diesen bestimmten Schritt. Wir werden die Werte dieser Variablen aus Secrets übernehmen.



- 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 }}


In unserem Android-Host müssen wir die Assemblykonfiguration so beschreiben, dass wir die * .apk-Datei in CI signieren können, ohne die Möglichkeit zu verlieren, die signierte Assembly lokal zu erstellen. Die Datei keystoreConfig.gradle ist für diesen Moment verantwortlich .



Wenn die Datei keystore_release.properties gefunden wird, ist bekannt, dass der Build lokal erfolgt. Dies bedeutet, dass Sie alle Eigenschaften der keystoreConfig initialisieren können, indem Sie sie einfach aus der Datei lesen. Andernfalls erfolgt die Montage in CI. Dies bedeutet, dass die einzige Quelle für vertrauliche Daten Github Secrets ist.



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"
}


Und so sieht die Datei keystore_release.properties aus :



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


Der letzte Schritt in der build.gradle- Datei unseres Android-Hosts besteht darin, die keystoreConfig-Datei auf unsere Release-Build-Signaturkonfiguration anzuwenden:



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

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


Die unterschriebene Versammlung ist bereits in unseren Händen! Aber wie erweitern Sie es auf Ihre Kollegen zum Testen?



Entladung



Mit Github Actions können Sie das Hochladen von Artefakten in fast jedes bekannte Tool für die Verteilung von Assemblys konfigurieren. Wir werden jedoch nur zwei Optionen in Betracht ziehen:



  • Github-Speicher - der einfachste Weg, Assemblys in Ihren eigenen Github-Speicher hochzuladen. Dies funktioniert sofort, weist jedoch einige Einschränkungen auf.

  • Firebase App Distribution ist ein Dienst aus dem Firebase-Ökosystem, der Beta durch Crashlytics ersetzt hat. Die Integration ist etwas schwieriger zu konfigurieren, aber der Dienst selbst ist viel bequemer zu verwenden.





Github Storage

Github Storage lässt sich durch die offizielle Aktion problemlos integrieren. Sie müssen nur den Namen der Assembly angeben, wie ihn Ihre Kollegen in der Weboberfläche sehen, sowie den Pfad zur zusammengestellten * .apk-Datei in CI.



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


Das Hauptproblem ist der begrenzte Speicherplatz. Auf dem kostenlosen Plan stehen Ihnen nur 500 MB zur Verfügung. Das Seltsamste ist, dass ich keine Möglichkeit gefunden habe, den gesamten Speicher auf einmal manuell über die Weboberfläche zu löschen. Ich bin aus der Situation herausgekommen, indem ich ... einen separaten Workflow geschrieben habe, der nur für das Löschen des Speichers von moosigen Artefakten verantwortlich ist.



Der Workflow wird täglich um 1 Uhr morgens ausgeführt und entfernt alle Artefakte, die älter als eine Woche sind:



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

Wie bei der Firebase App Distribution habe ich die vorgefertigte Aktion wzieba / Firebase-Distribution-Github-Action verwendet, um sie zu integrieren



- 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


Damit die Aktion ordnungsgemäß funktioniert, müssen folgende Parameter übergeben werden:



  • appId - Anwendungskennung, die in den Einstellungen des Firebase-Projekts zu finden ist;







  • Token - Ein Token zur Authentifizierung bei Ihrem FIrebase-Projekt, das zum Laden der Assembly in den Service erforderlich ist. Sie können ein Token nur über die Firebase-CLI erhalten, für die Sie in der offiziellen Dokumentation mehr lesen können .

  • Datei - Pfad zur kompilierten * .apk-Datei auf CI;

  • Gruppen - Dieser Parameter ist optional. Sie können jedoch den Alias ​​der Testergruppe angeben, für die die hochgeladene Assembly automatisch freigegeben wird.



Start und Beobachtung



Unser einfachster Workflow ist fertig! Wir müssen nur noch das auslösende Ereignis auslösen und den Fortschritt des Workflows verfolgen.



Ratschläge und Abschiedswörter



Jetzt können Sie alle Vorteile eines einfachen CI / CD-Mechanismus in Ihrem Flutter-Projekt nutzen, unabhängig von seiner Größe, Entwicklungsintensität oder Ihrem Geldbeutel.



Abschließend einige Tipps und Beobachtungen, die ich mir bei der Arbeit an diesem Workflow ausgedacht habe:



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

  • step’ shell-. workflow . . .

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



Es liegen noch unzählige potenzielle Verbesserungen und Verbesserungen vor uns. Schreiben Sie in die Kommentare Ihre Ideen zur Verbesserung des Workflows. Was fehlt Ihnen persönlich daran? Vielleicht bildet die Umsetzung der interessantesten Ideen der Leser die Grundlage für den nächsten Artikel zum Thema.



Alle Skripte und Workflows finden Sie im Repository mit der Testanwendung .



Vielen Dank für Ihre Aufmerksamkeit.



PS Unser Surf- Team veröffentlicht viele nützliche Bibliotheken für Flutter. Wir laden sie in das SurfGear-Repository hoch .



All Articles