Dev Life

Modelos privados do GitHub Actions para a sua organização

Após muitos anos usando o Jenkins, decidimos migrar para o GitHub Actions, onde enfrentamos alguns desafios. Por isso, desenvolvemos uma solução alternativa que permite que usuários não Enterprise do GitHub com repositórios privados atualizem seus modelos de fluxo de trabalho.
Imagem para Modelos privados do GitHub Actions para a sua organização

O GitHub Actions é a novidade no mundo de CI/CD, mas está superando rapidamente os servidores tradicionais de automação de código aberto. Já faz algum tempo que a equipe de desenvolvimento da Sinch Mailgun sabe que algumas das ferramentas e tecnologias que usamos nessa configuração têm uma comunidade cada vez menor e correm o risco de se tornarem muito específicas para que a manutenção valha a pena. É por esses motivos que estamos no meio de uma migração de nosso CI/CD baseado no Jenkins para o CI/CD do GitHub Actions. Aviso: às vezes, as pilhas não estão incluídas, a menos que você seja cliente do GitHub Enterprise…

Digo isso porque, até muito recentemente, os modelos de fluxo de trabalho em repositórios privados não eram compatíveis, então tivemos que usar uma solução alternativa. Felizmente, o GitHub lançou uma atualização recente que agora tem suporte a essa funcionalidade em seus planos Enterprise. Mas para quem está nos planos Free ou Team do GitHub, o problema persiste. Não sabemos se ou quando o GitHub vai liberar essa atualização para outros planos, então, enquanto isso não acontece, veja pelo que passamos e como resolvemos isso.

O desafio de reutilizar fluxos de trabalho

Minha experiência anterior com o GitLab CI encurtou a curva de aprendizado com o GitHub Actions, mas os detalhes mais minuciosos mostraram-se diferentes. Por exemplo, incluir um conjunto de etapas de um projeto em outro é bem diferente. Tanto o GitHub Actions quanto o GitLab CI têm suporte à reutilização de etapas de um repositório em outro, mas os planos Free e Team do GitHub Actions exigem que as etapas a serem incluídas sejam acessíveis publicamente. No Gitlab CI, é possível usar a autenticação para importar tarefas, estágios, variáveis etc. de um repositório privado usando a diretiva includes .

No momento da redação deste texto, o GitHub Actions tinha issues (na verdade, duas) rastreando essa funcionalidade desejada para repositórios privados. Essas issues ficaram abertas por quase 18 meses, e essa funcionalidade foi adicionada recentemente para quem tem planos Enterprise. Não sei ao certo quando (ou se) essa funcionalidade estará disponível para todo mundo, mas parece ser uma parte fundamental do funcionamento para dar suporte à reutilização privada de fluxos de trabalho.

Sincronização de modelos de fluxo de trabalho 

Pesquisei as publicações no Stack Overflow e vasculhei as conversas de comentários do GitHub Issues, mas não encontrei boas soluções alternativas para a falta de capacidade de incluir fluxos de trabalho de outros repositórios privados na mesma organização. 

Pensei que os submódulos do git poderiam ser uma possível solução. Afinal, eles permitem que um repositório inclua outro repositório. Poderíamos incluir nossos modelos de fluxo de trabalho como um submódulo em nosso repositório e usar o gerenciamento de submódulos do git para manter os modelos atualizados? Poderíamos, mas não se quiséssemos ter modelos irrelevantes para alguns repositórios no repositório de modelos. O git trata um repositório como a menor unidade no gerenciamento de submódulos, então nossa solução teria um repositório com apenas fluxos de trabalho relevantes, ou evitaria o uso de submódulos de uma vez por todas.

Optamos por não usar submódulos porque queríamos que um repositório de fluxo de trabalho armazenasse modelos de Go e Python (entre outros), mas extraí algumas informações valiosas dessa linha de raciocínio. Algumas pessoas usam links simbólicos (symlinks) para incluir partes de um repositório em outro em vez de submódulos.

Então, tentamos vincular nosso modelo ao fluxo de trabalho do nosso aplicativo. ln $DIR/workflows/go/build_and_push.yml $DIR/my-app/.github/workflows/build_and_push.yml funciona para manter o nosso modelo de fluxo de trabalho sincronizado com o aplicativo… até executarmos um comando git que modifica o modelo (o git altera os inodes dos arquivos que modifica). Como queremos conseguir usar o git pull para atualizações nos modelos, precisaríamos encontrar algo que aplicasse automaticamente as alterações do repositório de modelos ao arquivo de um aplicativo.

Modelos do GitHub Actions: uma solução alternativa aceitável para planos não Enterprise

Sem a capacidade nativa de incluir modelos privados em nossos fluxos de trabalho e como queríamos ter um único repositório para armazenar nossos modelos de fluxo de trabalho, optamos por essa solução simples que otimiza a facilidade de aplicar as atualizações de modelo. Até que os planos Free e Team do GitHub deem suporte a modelos privados, você pode migrar, mas esta é uma possível solução que tentamos e testamos.

Incluímos exemplos de git hooks em nosso repositório de modelos (por exemplo, hooks/post-checkout.sample) porque qualquer comando git que vá quebrar os links precisará restabelecê-los. Os hooks para post-checkout e post-merge ficam mais ou menos assim:

                                

                                    #!/bin/sh
#
# An example hook script to keep your application workflow up-to-date with this
# template. For example, this updates my-app's workflow with the go template.
#
# To activate, this must have execute permission and be copied like so:
#
# $ cp hooks/post-checkout.sample .git/hooks/post-checkout
# $ chmod +x .git/hooks/post-checkout

# setup our project directory, assumes my-app is sibling
export PROJ_DIR=..
# remove existing workflow for clean install
rm $PROJ_DIR/my-app/.github/workflows/build_and_push.yml
# link app workflow to template
ln -v $PROJ_DIR/workflows/go/build_and_push.yml $PROJ_DIR/my-app/.github/workflows/build_and_push.yml
                                
                            

git merge e git checkout são operações comuns que quebram links, então executamos este script após essas ações.

Portanto, para aplicar os modelos de fluxo de trabalho aos seus aplicativos, é preciso:

  1. Clonar o nosso repositório workflows.
  2. Editar os arquivos hooks/post-checkout.sample e hooks/post-merge.sample para corresponder aos seus aplicativos.
  3. Copiá-los para seus respectivos locais em .git/hooks e dar permissão de execução a eles.
  4. Executar um git checkout ou git pull (git checkout master a partir da primária funciona).

Infelizmente, isso não mantém o repositório de modelos atualizado por conta própria e requer alguma interação das pessoas proprietárias do projeto para adotar as atualizações – afinal, as alterações precisam ser aplicadas ao repositório remoto. É possível fazer isso por meio dos seguintes comandos:

                                

                                    # from the workflow templates repo
$ git pull
# from the project repo
$ git add .github/workflows/
$ git commit -m "upgrade workflow template"
$ git push
                                
                            

E depois disso, o fluxo de trabalho será executado com as alterações mais recentes do repositório de modelos.

Simplificar a configuração usando um gerenciador de pacotes baseado em Git

Como otimizamos a facilidade de aplicar os modelos, fizemos outras concessões com nossa solução alternativa. A mais óbvia delas é a configuração necessária. Estamos pedindo para os usuários organizarem os repositórios de uma forma específica e copiarem e colarem arquivos manualmente. Esses requisitos contribuem para uma certa fragilidade, em que o sistema pode quebrar se os arquivos forem movidos ou os inodes mudarem sem o nosso conhecimento.

No entanto, há um benefício interessante que obtemos com essa solução. Descobrimos um conjunto de ferramentas e scripts que pode funcionar em um contexto genérico. Criamos um gerenciador de pacotes genérico baseado em git que utiliza links e git hooks para atualizar arquivos entre repositórios git:

                                

                                    #!/bin/sh

# set paths/files (relative to workflows repo)
export TEMPLATE_DIR=.
export APP_DIR=../my-app
export DOCKER_TEMPL=go/Dockerfile
export WKFLW_TEMPL=go/build.yml

#remove files for clean install
rm $APP_DIR/Dockerfil $APP_DIR/.github/workflows/build.yml

# link app workflow to template
ln -v $TEMPLATE_DIR/$DOCKER_TEMPL $APP_DIR/Dockerfile
ln -v $TEMPLATE_DIR/$WKFLW_TEMPL $APP_DIR/.github/workflows/build.yml

                                
                            

Prós e contras do GitHub Actions

Mas, por que o GitHub Actions? Bom, nosso sistema atual exige modificações, pull requests, implantações e muito mais para integrar um novo serviço. E, como usuários do GitHub Enterprise, temos uma grande alocação de minutos de execução no GitHub Actions e, por isso, decidimos avaliar isso como uma alternativa. 

Agora que passamos algum tempo usando o GitHub Actions, estas são as minhas conclusões:

Prós do GitHub Actions Contras do GitHub Actions
Os runners hospedados significam que não precisamos nos preocupar com a manutenção deles. A cobrança é por minutos de execução, o que não nos incentiva a ter testes de longa duração nos runners hospedados pelo GitHub.
Oferece alguns benefícios de segurança (embora tenham acabado de sofrer uma violação). Ainda faltam alguns recursos oferecidos pelo Jenkins.
Tem uma sintaxe de CI/CD mais comum definida por YAML. O GitHub usa metáforas e linguagem no Actions semelhantes às da concorrência, mas as metáforas nem sempre são as mesmas. Isso torna a migração e o aprendizado um pouco difíceis.
É mais fácil integrar tarefas públicas/de software de código aberto (OSS). Trabalhar com secrets pode ser perigoso e a equipe de desenvolvimento pode vazar dados sem saber.
A integração com a interface de usuário do GitHub facilita a visualização dos resultados de CI.

Como você pode ver, a grama do vizinho nem sempre é mais verde. Mas prevemos que o GitHub, com seus infinitos recursos da Microsoft, vai superar o Jenkins no longo prazo. 

Próximos passos

Embora nossa solução alternativa não seja bonita, é uma solução que funciona até que o GitHub passe a oferecer suporte a modelos privados para planos não Enterprise. Se isso acontecer, você pode migrar os modelos para um único repositório. 

Dito isso, temos uma conta do GitHub Enterprise, mas ainda não podemos ativar a funcionalidade de repositório privado que o GitHub adicionou. Ainda estamos determinando a origem desse problema. Até lá, estamos usando a solução alternativa e voltaremos a verificar no futuro para ver se conseguimos obter acesso a essa nova funcionalidade. Aguarde novidades!