Usando os recursos gratuitos do Github Actions for CI / CD em um projeto Flutter

O GitHub Actions é uma ferramenta para automatizar ações de rotina com um repositório e ajudá-lo a criar um CI / CD para seu projeto.



Os usuários do GitHub têm 2.000 minutos por mês para executar ações do GitHub na infraestrutura de serviço. Vamos aproveitar bem esse tempo livre.



Dou instruções para desenvolvedores de aplicativos Flutter: como executar testes e um analisador de código para cada solicitação de pull usando GitHub Actions, construir um artefato e implantá-lo para teste no Firebase.







A pessoa se acostuma muito rapidamente com as coisas boas. E ele se acostuma tanto que nem pensa no fato de que nem sempre foi assim. Como diz a velha anedota sobre um homem e uma cabra, a realização de todas as delícias da vida ocorre no exato momento em que você é privado dessas mesmas delícias.



Se o seu projeto de trabalho está indo bem com CI / CD, você é uma pessoa de sorte.

Talvez você trabalhe em uma startup e tenha configurado cuidadosamente todos os pipelines e ganchos com suas próprias mãos.



Pode ser que uma equipe DevOps inteira cuide do seu bem-estar: a cada mês, isso o deixa feliz com novas integrações, tempo de construção que está derretendo diante de nossos olhos e técnicas avançadas para implantar montagens em todos os lugares imagináveis ​​e inconcebíveis.



Isso não importa. O principal é que você sempre tenha certeza de que suas compilações são viáveis ​​e, ao mesmo tempo, você mesmo é poupado de muitas tarefas de rotina chatas, cujo pensamento sempre mergulha os desenvolvedores em depressão e desânimo. A propósito, ficarei feliz se você escrever nos comentários quando foi a última vez que alterou manualmente o status de um problema no Jira?



Saindo da sua zona de conforto



De onde eles saem da zona de conforto e, o mais importante, por quê? Muitos motivos. Um conhecido me pediu para ajudar a escrever um pequeno aplicativo para o seu próprio bar, você finalmente encontrou tempo para implementar um projeto favorito dos seus sonhos ou decidiu lançar uma biblioteca que nasceu acidentalmente como parte do projeto. Finalmente, você e seu colega decidiram escrever um pequeno projeto de amostra para o workshop.



Aposto que em qualquer um dos cenários, sua inspiração de novas tarefas interessantes irá rapidamente colidir com a dura realidade do desenvolvimento de software em um "ambiente sem ar" (sim, em algum momento você precisará de um coletor inteligente como o ar).



"CI / CD é difícil ..."



O que você costuma dizer a si mesmo nesses momentos? “Eu não entendo isso! Estou apenas escrevendo um frontend / telefone móvel e não sei nada sobre o seu Jenkins! " E se eu dissesse que você não precisa saber nada disso?



Sim, sim, você só precisa ser capaz de construir seu projeto usando comandos de console - e é isso. Você pode simplificar muito a sua vida, mesmo que seja um pequeno projeto pessoal, e não um monstro gigante de vários módulos que já é difícil de digerir IDE.



O Github Actions é tão simples que até sua avó o configuraria sem muita dificuldade.



Então sobre o que é a postagem?



Se tudo é tão simples, por que perder tempo lendo esta obra? Vou responder com uma lista com marcadores:



  • Flutter. CI . , Flutter- . ,

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

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



c CI/CD , . . ($).


Github Actions, !



O Github Actions é um serviço que permite automatizar o fluxo de trabalho do repositório. Qualquer coisa que você fizer manualmente com seu projeto - diferente de escrever o código diretamente - você pode delegar para Ações do Github. Se você preferir se familiarizar imediatamente com as fontes primárias, consulte a documentação oficial .



Freqüentemente, nem mesmo sabemos o que precisamos automatizar. A equipe não tem tempo para entender a API complexa do serviço e, em seguida, escrever e depurar a solução do zero. O Marketplace resolve esse problema : quase 5 mil Actions prontas são publicadas lá e resolvem muitas tarefas típicas (por exemplo, envio de notificações sobre eventos no Telegram , análise de fontes de projeto para débito técnico ,definição de rótulos no PR, dependendo dos arquivos alterados nele ). Más notícias: muitos deles são shareware - com limites de uso bastante rígidos.



O processo de trabalho



Tudo em Github Actions gira em torno de fluxos de trabalho . Cada fluxo de trabalho responde a duas perguntas: o que fazer e quando fazer.



O que fazer . Existem inúmeras opções aqui: você pode construir, testar e implantar suas compilações usando scripts prontos ou criados por você mesmo. Saiba mais sobre a configuração do fluxo de trabalho.



Quando fazer . Você pode acionar fluxos de trabalho em eventos que ocorrem no repositório. Criar uma solicitação de pull, enviar uma tag de confirmação ou até mesmo adicionar uma nova estrela ao seu projeto. Lista completa de ganchos



Se o fluxo de trabalho deve ser executado não em um evento, mas em um determinado momento ou com uma certa frequência, você tem à sua disposição a sintaxe cron POSIX .Mais sobre eventos regulares



No repositório, muitos fluxos de trabalho diferentes podem coexistir simultaneamente. Cada fluxo de trabalho é descrito em um arquivo YAML separado, cada um dos quais deve ser armazenado no diretório .github / workflows na raiz do seu repositório. Saiba mais sobre a sintaxe de fluxos de trabalho



Ambiente de execução



O Github Actions oferece duas opções para executar seus fluxos de trabalho:



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

  • Self-hosted runners — , . Github , .



Em meu artigo, vou me concentrar na primeira opção. Estamos seguindo o caminho mais simples possível, certo?



Configurando o fluxo de trabalho básico para Flutter



Antes de começarmos a configurar o fluxo de trabalho, precisamos concordar em duas coisas.



Primeiro, a principal função do fluxo de trabalho será complicar a divisão da base de código. O código que não é compilado, contém problemas em potencial ou interrompe os testes não deve entrar no mainstream.



Segundo: pode haver algumas sutilezas em minha configuração que não serão relevantes para o seu projeto. Vou tentar explicá-los. No entanto, se você estiver usando este artigo como um guia, peça emprestado cuidadosamente.



Finalmente, vamos decidir o que nosso fluxo de trabalho deve fazer. Precisamos de um plano que nos ajude a seguir na direção certa.



Passo a passo para a montagem acabada



O plano acima pode ser usado como uma lista de verificação ao configurar seu próprio fluxo de trabalho. Nós temos que:



  1. dê ao fluxo de trabalho um nome significativo;
  2. indique em qual evento nosso workflow será iniciado;
  3. decida em qual máquina será iniciada a configuração;
  4. decidir sobre as etapas em que nosso fluxo de trabalho consistirá:


  • verifique o projeto,
  • instalar o Java;
  • instalando o Flutter (como você se lembra, sempre que temos uma instância limpa à nossa disposição),
  • baixando pacotes de projeto,
  • iniciando um analisador estático,
  • executando testes,
  • a própria montagem de construção,
  • implante a construção em algum lugar onde os testadores possam obtê-la.


Agora, nosso trabalho ganhou uma forma tangível. Vamos prosseguir para a implementação.



Qual será a aparência do nosso fluxo de trabalho no final
— . , , .



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




Nome



Obviamente, precisamos nomear nosso fluxo de trabalho para que o nome reflita sua essência com a maior precisão possível. O nome ( documentos ) é a primeira coisa que vemos no Console de ações quando o fluxo de trabalho é executado. Por que nomeei meu fluxo de trabalho dessa forma, você descobrirá em instantes.







name: Flutter PR


Evento desencadeador



O bloco "on" ( docs ) permite-nos especificar um ou mais eventos, dos quais queremos iniciar o nosso workflow no momento do registo. Além disso, alguns dos eventos podem ser ajustados. 



Qual evento escolher? Para não perder nenhuma divisão, você pode especificar pelo menos todos os eventos existentes. Aí a montagem vai quase ininterruptamente, mas queremos isso? Não, como neste caso o limite do nosso plano de tarifa gratuita terminará fantasticamente rápido. Procuraremos a solução ótima.



Vamos supor que nosso projeto cumpra os acordos segundo os quais o código não pode ser enviado diretamente para o branch principal do projeto - somente por meio da criação de uma solicitação pull. É lógico se nosso fluxo de trabalho responde à criação de uma solicitação pull e constrói um projeto a partir de uma base de código modificada:



on: pull_request


$ Isso é suficiente para o trabalho, mas a solução ainda não é muito ideal. A construção será acionada em cada solicitação pull criada. Isso é redundante, uma vez que estamos interessados ​​apenas em solicitações pull direcionadas ao branch principal do projeto. A sintaxe para Ações do Github nos permite especificar os nomes (ou máscaras) dos branches nos quais estamos interessados.



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


$ E novamente estamos procurando maneiras de otimizar o processo. Existem arquivos que, mesmo em teoria, não podem prejudicar seu projeto: documentação do projeto, Swagger, estilo de código geral e configurações IDE. Felizmente, temos a capacidade de ignorar esses arquivos pela máscara de caminho. Como resultado, o bloco “ligado” ficará assim:



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


Importante : só faça uma solicitação pull se estiver pronto para mesclá-la. Cada próximo push para uma solicitação pull já criada reiniciará o fluxo de trabalho.

Configuração de trabalho







Finalmente, estamos prontos para configurar o trabalho ( docs ). Agora é a hora de esclarecer qual função a tarefa desempenha no fluxo de trabalho.



Cada fluxo de trabalho deve incluir pelo menos um trabalho. É um trabalho que contém uma descrição passo a passo das etapas que executamos com nosso projeto. O número de trabalhos em um fluxo de trabalho não é limitado, assim como o número de etapas em um trabalho. Por padrão, todos os trabalhos são executados em paralelo, a menos que seja especificada a dependência de um trabalho dos resultados de outro. Nosso projeto terá o único trabalho que será responsável pela construção do projeto.



Configurando o ambiente



Cada vez que o fluxo de trabalho é executado em uma instância de máquina virtual limpa. A única coisa que podemos escolher é o sistema operacional que será instalado nesta máquina. O que escolher?



É tentador escolher o macOS, porque estamos planejando construir um aplicativo Flutter para plataformas de destino: Android e iOS. Más notícias. Um minuto de uso de uma instância com macOS é cobrado como dez (10 !!!) minutos de uso de uma instância com Ubuntu. Em uma instância com Windows, no nosso caso, não adianta nada, já que um assembly iOS ainda não funcionará lá, e seu tempo de uso é duas vezes mais caro do que uma instância com Ubuntu. Mais sobre faturamento



$Como podemos garantir que nossos 2.000 minutos grátis não se transformem em 200? Não existe uma boa solução. Decidi pular a compilação no iOS ao criar uma solicitação pull. Isso potencialmente afetará a estabilidade da construção do iOS. Também há uma opção de compromisso - construir uma versão do iOS no macOS apenas ao alterar o pubspec.yaml ou qualquer arquivo do diretório / ios ; em outros casos, construir apenas uma versão do Android em uma instância com o Ubuntu. Isso pode ser feito por analogia com a forma como configuramos os arquivos para ignorar para o bloco "on" .



jobs:
 build:
   runs-on: ubuntu-latest


Você pode ver as especificações técnicas , bem como uma lista de softwares "prontos para uso" instalados . Flutter e Java, infelizmente, não estão incluídos nesta lista. Eles deverão ser instalados manualmente sempre que o fluxo de trabalho for executado.



Não tenha pressa em ficar chateado. Ações prontas virão em nosso auxílio, as quais podemos usar nas etapas de nosso trabalho. Usaremos dois:



  • actions / setup-java - a ação oficial para configurar o ambiente Java;

  • subosito / flutter-action é uma ação não oficial para baixar e instalar o Flutter SDK. Ele se provou bem: permite que você faça tudo o que for necessário - por exemplo, especificar o canal de estrutura desejado ou mudar para uma versão específica do SDK.



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

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


Clonando um repositório



Temos uma instância limpa de uma máquina alugada no Github por alguns minutos. Na etapa anterior, instalamos todo o software necessário nele. Agora precisamos clonar o repositório de origem do nosso projeto. Para fazer isso, usaremos uma ferramenta pronta:



  • actions / checkout é a ação oficial para clonar um repositório com uma tonelada de configurações que não precisaremos na maioria dos casos. Como o fluxo de trabalho é executado diretamente no repositório que estamos clonando, não precisamos especificá-lo explicitamente.



- uses: actions/checkout@v1


Carregando dependências



Até agora, não implementamos etapas com as nossas próprias mãos, mas apenas usamos o que eu ofereço ações prontas. Agora estamos avançando para a implementação da fase ativa da construção de nosso projeto, então é hora de escrever a implementação da etapa você mesmo.



Antes de construir, precisamos baixar todos os pacotes especificados no bloco de dependências do nosso arquivo pubspec.yaml, bem como todas as suas dependências transitivas. Para fazer isso, o Flutter SDK oferece um comando simples pronto para uso flutter pub get. A implementação da etapa pode consistir em chamar um comando de terminal. Nesse caso, a próxima etapa só será chamada após a conclusão deste comando.



- run: flutter pub get


Se o seu projeto tiver uma estrutura complexa e contiver vários pacotes de dardos conectados localmente, você enfrentará um problema. É flutter pub getimpossível construir um projeto sem uma chamada explícita para cada um desses pacotes. Em meu projeto, esses pacotes são coletados na pasta / core localizada no diretório raiz. Abaixo está um script que resolve esse problema. Ele é descrito no arquivo flutter_pub_get.sh na pasta / scripts no mesmo diretório raiz.



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


Uma vez que a implementação de step pode ser qualquer comando de terminal, nada nos impede de executar nosso script de shell.



- run: sh ./scripts/flutter_pub_get.sh


Análise de código estático



Flutter nos convida a usar um comando embutido flutter analyzepara executar um analisador estático. Isso ajudará a identificar possíveis problemas com nossa base de código em um estágio inicial: antes que um bug chegue à produção ou nosso código se transforme em uma bagunça ilegível e sem suporte.



Poderíamos ter aproveitado o recurso out-of-the-box, mas, infelizmente, o comportamento padrão da equipe flutter analyzetem uma falha que estraga nosso fluxo de trabalho na hora errada. 



Os problemas encontrados pelo analisador são classificados em três níveis de gravidade: informações, aviso, erro. Nesta ediçãoé descrito que mesmo se durante a análise apenas problemas da classe info forem encontrados (e nem sempre vale a pena gastar tempo consertando-os aqui e agora), o comando retorna o código "1", como resultado do qual seu assembly irá travar.



Eu sugiro usar o seguinte script como uma solução temporária. A partir de agora, a montagem travará apenas se houver problemas com o nível de erro :



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


Executamos o script de shell na próxima etapa de nosso fluxo de trabalho:



- run: sh ./scripts/flutter_analyze.sh


Executando testes



Se você tem testes em seu projeto, está no caminho certo! Para que os testes funcionem, não é suficiente escrevê-los - eles precisam ser executados regularmente para corrigir falhas de implementação a tempo ou atualizá-los se necessário. Portanto, na próxima etapa, implementaremos



- run: flutter test


Cuidado: as classes de teste vazias que não contêm nenhum teste implementado irão travar todo o fluxo de trabalho. Há apenas uma saída: não declare classes de teste até que você esteja pronto para implementar pelo menos um teste dentro delas.



Construir e assinar



Todo o trabalho preparatório acabou. Verificamos que o código provavelmente não contém problemas óbvios. Agora, passamos para o estágio mais importante - a produção do artefato. Em outras palavras, estaremos construindo o APK.



A montagem em si é extremamente simples de implementar. Temos à nossa disposição o comando flutter build do terminal, que é extremamente configurável e permite construir um artefato para um tipo específico, arquivo principal, ABI. Não cobrimos essas nuances no artigo, portanto, use sinalizadores de comando adicionais, se necessário.



- run: flutter build apk --release


Nosso objetivo é fazer com que um assembly seja assinado com uma chave de liberação. E, neste estágio, teremos que resolver o problema de segurança, porque precisamos armazenar o keystore de liberação em algum lugar, bem como todos os seus aliases e senhas.



O Github permite que você armazene com segurança valores de string em um repositório de Segredos dedicado . Os dados disponíveis aqui são armazenados no repositório apropriado e podem ser lidos programaticamente em qualquer etapa do fluxo de trabalho. Ao mesmo tempo, os valores não podem ser vistos na interface da web do Github. Apenas excluir ou substituir é permitido.







Esta parece ser uma boa solução para aliases e senhas, especialmente se você for seu próprio serviço de segurança, mas e o próprio arquivo * .jks? Empurrá-lo para o repositório não parece uma boa ideia, mesmo se o seu repositório for privado. Infelizmente, o Github não oferece nenhuma maneira segura de armazenar arquivos, então você deve se esquivar.



Seria bom representar nosso arquivo de armazenamento de chaves como uma string. E é real - você só precisa codificá-lo em base64. Para fazer isso, abra um terminal no diretório que contém nosso arquivo * .jks e execute o seguinte comando. Em seguida, um arquivo de texto será criado a partir do qual você pode copiar a representação em base64 de nosso armazenamento de chaves, então ... salve-o em Github Secrets.



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


Agora que temos todos os ingredientes necessários para assinar com sucesso a montagem, procederemos com a configuração da etapa. No bloco env, declaramos todas as variáveis ​​de ambiente para essa etapa específica. Vamos pegar os valores dessas variáveis ​​em Segredos.



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


Em nosso host Android, temos que descrever a configuração do assembly de forma que possamos assinar o arquivo * .apk no CI também, sem perder a capacidade de construir o assembly assinado localmente. O arquivo keystoreConfig.gradle é o responsável por esse momento .



Se o arquivo keystore_release.properties for localizado, sabe-se que a construção ocorre localmente, o que significa que você pode inicializar todas as propriedades keystoreConfig simplesmente lendo-as do arquivo. Caso contrário, a montagem ocorre em CI, o que significa que a única fonte de dados confidenciais são os segredos do Github.



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


E é assim que o arquivo keystore_release.properties se parece :



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


A etapa final no arquivo build.gradle de nosso host Android é aplicar o arquivo keystoreConfig à nossa configuração de assinatura de versão de versão:



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

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


A assembleia assinada já está em nossas mãos! Mas como você o estende a seus colegas para teste?



Descarregando



O Github Actions permite que você configure o upload de artefatos para quase qualquer ferramenta conhecida para distribuição de assemblies, mas consideraremos apenas duas opções:



  • Armazenamento Github - a maneira mais fácil de carregar assemblies para seu próprio armazenamento Github, que funciona imediatamente, mas tem algumas limitações;

  • O Firebase App Distribution é um serviço do ecossistema Firebase que substituiu o Beta pelo Crashlytics. A integração é um pouco mais difícil de configurar, mas o serviço em si é muito mais conveniente de usar.





Github Storage O

Github Storage integra-se facilmente por meio da ação oficial. Você só precisa especificar o nome do assembly, pois seus colegas verão na interface da web, bem como o caminho para o arquivo * .apk montado no CI.



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


O principal problema é o espaço de armazenamento limitado. Em um plano gratuito, você tem apenas 500 MB. O mais estranho é que não encontrei nenhuma maneira de limpar manualmente todo o armazenamento de uma vez por meio da interface da web, então saí da situação por meio de ... escrever um fluxo de trabalho separado responsável apenas por limpar o armazenamento de artefatos musgosos.



O fluxo de trabalho é executado diariamente à 1h e remove todos os artefatos com mais de uma semana:



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

Quanto ao Firebase App Distribution, usei a ação pronta wzieba / Firebase-Distribution-Github-Action para integrar com ele



- 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


Para que a ação funcione corretamente, ela precisa passar os parâmetros:



  • appId - identificador do aplicativo, que pode ser encontrado nas configurações do projeto Firebase;







  • token - um token para autenticação em seu projeto FIrebase, que é necessário para carregar o assembly no serviço. Você pode obter um token apenas por meio da Firebase CLI, para a qual você pode ler mais na documentação oficial ;

  • arquivo - caminho para o arquivo * .apk compilado no CI;

  • groups - este parâmetro é opcional, mas permite que você especifique o alias do grupo de testadores, para o qual o assembly carregado será compartilhado automaticamente.



Lançamento e observação



Nosso fluxo de trabalho mais simples está pronto! Tudo o que resta fazer é disparar o evento de disparo e observar o andamento do fluxo de trabalho.



Conselhos e palavras de despedida



Agora você pode aproveitar todos os benefícios de um mecanismo simples de CI / CD em seu projeto Flutter, independentemente do tamanho, intensidade de desenvolvimento ou carteira.



Por fim, aqui estão algumas dicas e observações que fiz enquanto trabalhava neste fluxo de trabalho:



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

  • step’ shell-. workflow . . .

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



Ainda há toneladas de melhorias e melhorias potenciais pela frente. Escreva nos comentários suas idéias para melhorar o fluxo de trabalho. O que você pessoalmente falta nele? Talvez a implementação das ideias mais interessantes dos leitores constitua a base do próximo artigo sobre o assunto.



Todos os scripts e fluxos de trabalho podem ser encontrados no repositório com o aplicativo de teste .



Obrigado pela atenção.



PS Nossa equipe de Surf lança muitas bibliotecas úteis para Flutter. Nós os carregamos para o repositório SurfGear .



All Articles