GitHub Actions poderia ser muito melhor
(blog.yossarian.net)- O GitHub Actions é útil em termos de produtividade e funcionalidades, mas na escrita de workflows reais, atrasos na depuração, erros de segurança, ausência de tipagem e falta de ações oficiais levam a perda recorrente de tempo
- Mesmo pequenas mudanças exigem repetir
git add; git commit; git pushe checar logs no navegador, e houve caso em que até um workflow simples de release exigiu 4 commits e releases que falharam - Expansões
${{... }},pull_request_target, permissões padrão amplas doGITHUB_TOKENe referências de SHA de forks são todos pontos de segurança fáceis de errar; em especial, o SHA de fork pode parecer um commit do repositório pretendido - Entradas de actions não têm validação com
type:, eworkflow_calleworkflow_dispatchtambém diferem no escopo de suporte, então é preciso lidar manualmente com strings em estilo CSV ou entradas achatadas em vez de arrays e objetos - Validação no momento do push, verificações de segurança em tempo de execução, redução das permissões padrão do token em repositórios pessoais, checagem de tipos mais rígida e mais ações oficiais ou semi-oficiais poderiam aumentar a confiabilidade dos workflows
Útil, mas atrapalha repetidamente no dia a dia com GitHub Actions
- O GitHub Actions é uma ferramenta que, desde 2019, ajuda muito em produtividade e sensação de estabilidade, a ponto de ser usada todos os dias em projetos profissionais e pessoais
- A expansão de funcionalidades também continuou de forma constante
- Ainda assim, no processo real de desenvolvimento, ele também pode ser causa de grande frustração e perda de tempo
Depurar é pesado demais até para erros pequenos
- Houve um caso em que, ao configurar um workflow de release, foram necessários 4 commits e 4 releases com falha só para corrigir erros pequenos
- não usar
${{ ... }}onde era necessário - esquecer uma relação em
needs:
- não usar
- O ciclo atual de depuração tem muitas etapas mesmo para verificar um erro simples
- é preciso sair do ambiente de desenvolvimento e ir para o navegador
- encontrar a aba correta
- clicar até entrar no resumo do Actions e na tela de status
- atualizar logs de console em buffer procurando o próximo erro
- Mesmo uma mudança pequena pode levar mais de 30 segundos em todo o processo
-
Direções de melhoria
- Deveria haver um shell de depuração interativo ou, no mínimo, a possibilidade de reexecutar o workflow com pequenas mudanças sem precisar de
git add; git commit; git push - Configurações do repositório deveriam permitir rejeitar workflows claramente inválidos no momento do push
- sintaxe que não pode funcionar
- referência a jobs ou steps inexistentes
- casos em que um workflow simplesmente não roda por causa de YAML inválido
- Deveria haver um shell de depuração interativo ou, no mínimo, a possibilidade de reexecutar o workflow com pequenas mudanças sem precisar de
Erros de segurança acontecem com facilidade demais
- No GitHub Actions, é fácil um workflow escrito pelo usuário acabar vulnerável sem querer
- Ao usar expansões
${{ ... }}no shell ou em outros contextos de execução, se o valor expandido vier de entrada controlada pelo usuário, isso pode levar à injeção de código malicioso- No exemplo, código pode ser injetado por meio de
inputs.frobe vazar$MY_IMPORTANT_SECRET
- No exemplo, código pode ser injetado por meio de
pull_request_targeté muito difícil de usar com segurança em workflows que não sejam triviais- O caso de uso pretendido parece ser algo restrito, como colocar labels ou comentar em PRs vindos de forks
- Mas, na prática, ele fica exposto como uma grande armadilha
- Permissões em nível de workflow e job também tendem a ser configuradas em excesso
- O acesso padrão do
GITHUB_TOKENcomum é muito amplo - Em repositórios de contas pessoais, é frequente esquecer de reduzir as permissões padrão do token
- Sem saber exatamente quais permissões são necessárias, acaba-se concedendo privilégios em excesso
- O acesso padrão do
- O modelo de permissões do GitHub aumenta a confusão ao forçar tudo em um único eixo
read/write/noneid-token: writepermite ler o token OpenID Connect do workflow- Algumas operações
GETde security advisory exigem permissãowrite, enquanto outras exigem apenasread
Actions fixadas por SHA podem se confundir com commits de fork
- Uma referência como
actions/checkout@c7d749a2d57b4b375d1ebcd17cfbfb60c676f18epode, na verdade, não ser um commit do repositórioactions/checkout - Esse SHA pode ser um commit em um fork dentro da rede de
actions/checkoute, por causa do uso de alternates pelo GitHub, parecer pertencer ao repositório pai - Texto relacionado da Chainguard divide o problema em três partes
- referências a SHA de forks e de repositórios de destino não são visualmente distinguíveis
- a API REST do GitHub em
/repos/{user}/{repo}/commits/{ref}retorna um JSON que parece referenciar apenas{user}/{repo}, mesmo quando{ref}existe só em um fork - se não for possível distinguir SHAs de fork e não-fork, é possível contornar as restrições de fontes confiáveis do GitHub Actions
- A resposta do GitHub até agora se limitou basicamente a adicionar um aviso na documentação
-
Direções de melhoria
- É necessário um modo de segurança paranoica para workflows que rejeite workflows claramente inseguros no momento do push
- Assim como instrumentações de runtime tipo AddressSanitizer, deveria ser possível falhar um workflow durante a execução ao detectar padrões perigosos para segurança
- Ex.: falhar se
actions/checkoutfor usado empull_request_targetcom uma ref diferente da do repositório de destino
- Ex.: falhar se
- Também se pode considerar descontinuar ou remover
pull_request_target - Mesmo em repositórios pessoais, deveria ser possível configurar escopos padrão do
GITHUB_TOKENde forma mais restritiva, como já ocorre em organizações e empresas - Actions fixadas por SHA que não existam no repositório referenciado e existam apenas em um fork deveriam ser rejeitadas por padrão
O sistema de tipos carece de consistência e expressividade
- Uma GitHub Action customizada pode definir entradas em
inputs:, mas entradas de actions não têm imposição de tipo - Declarações como
type: numbernão funcionam para entradas de actions - Até dentro do GitHub Actions o suporte a tipos é inconsistente
workflow_call: suporte aboolean,number,stringworkflow_dispatch: suporte aboolean,choice,number,string- action inputs: sem suporte a tipos
- Mesmo entradas com suporte a tipos não permitem estruturas compostas, como arrays e objetos
- não é possível uma entrada de array como
paths: [foo, bar, baz] - não é possível uma entrada de objeto com hierarquia em
headers:
- não é possível uma entrada de array como
- Em vez disso, autores de actions acabam exigindo formatos improvisados
- implementar parsing de CSV manualmente em algo como
paths: foo,bar,baz - achatar estruturas naturais em algo como
header-foo,header-baz
- implementar parsing de CSV manualmente em algo como
- Isso não é bom nem para manutenção nem para segurança
- é preciso gerenciar manualmente um único namespace plano de entradas
- para entradas complexas, acaba-se criando linguagens ad hoc e não especificadas
-
Direções de melhoria
- Autores de actions e workflows deveriam poder usar
type:em todos os lugares choicetambém não deveria ficar restrito aworkflow_dispatch, mas disponível em todos os contextos- Quando for possível inferir tipos estaticamente, mudanças de workflow com tipos inválidos deveriam ser rejeitadas no momento do push
- É necessário ter
type: objectetype: array - Mesmo que não seja perfeito por permitir tipos internos heterogêneos, ainda seria melhor do que o estado atual
- Se necessário, isso poderia ser passado como string serializada em JSON
- O GitHub Actions já tem a função
fromJSON(...)
- Autores de actions e workflows deveriam poder usar
Actions oficiais estão diretamente ligadas à confiança na plataforma
- O ecossistema de terceiros do GitHub Actions tem muitas actions de alta qualidade
- Abaixo dele, há as actions oficiais mantidas pelo GitHub
- operações essenciais de
git:actions/checkout - tarefas do GitHub e gerenciamento de repositório:
actions/{upload,download}-artifact,actions/cache,actions/stale - configuração essencial:
actions/setup-python,actions/setup-node
- operações essenciais de
- Essas actions têm alta importância e uso geral, então há muito valor em existirem implementações oficiais e mantidas
- Mas o número de actions oficiais é pequeno, e há casos em que até tarefas que se conectam diretamente a recursos do GitHub não são oferecidas como action oficial
- Ex.: uma action para adicionar programaticamente um pull request à merge queue
- O GitHub CLI tem
gh pr merge, mas isso não é exposto como action
-
Casos de actions oficiais descontinuadas
actions/create-release: sem manutenção desde março de 2021- Usuários são incentivados a migrar para workflows mantidos pela comunidade
- Um exemplo citado é
softprops/action-gh-release actions/upload-release-asset: marcada como sem manutenção no mesmo período deactions/create-releaseactions/setup-ruby: sem manutenção desde fevereiro de 2021- Usuários são incentivados a migrar para
ruby/setup-ruby - A transição em si foi relativamente pouco dolorosa, mas a impressão de que componentes centrais podem ser abandonados afeta negativamente a confiança na plataforma
- Quando faltam actions oficiais de alta qualidade, usuários acabam lidando diretamente com a superfície da API do GitHub e continuam cometendo erros de segurança
-
Direções para melhorar o ecossistema
- Tarefas que conectam diretamente diferentes partes da infraestrutura do GitHub, e que hoje exigem chamadas REST API ou execução manual de
gh, merecem existir como actions oficiais - O GitHub poderia colaborar com grandes actions de terceiros para criar uma organização semi-oficial como
community-actions- actions revisadas pelo GitHub
- conformidade com boas práticas de segurança em repositórios
- atualizações de versão semântica
- uma trilha de confiança clara para actions importantes do ecossistema
- Tarefas que conectam diretamente diferentes partes da infraestrutura do GitHub, e que hoje exigem chamadas REST API ou execução manual de
Ferramentas existentes e o roadmap do GitHub
- Muitas pessoas recomendam o emulador local de GitHub Actions
nektos/act- ele é útil para iterar e depurar workflows simples com rapidez
- mas é visto como uma ferramenta com perdas, já que imagens e eventos podem não corresponder totalmente ao ambiente real do GitHub
rhysd/actionlinté usado para lint local e lint de segurança- uma limitação é que ele só faz lint de workflows e não de actions
- A extensão GitHub Actions para VS Code oferece lint no editor e integração com workflows do repositório
- é baseada no schema JSON público
- mostra workflows em execução, autocompletação de variáveis etc.
- mas ainda há casos que ela não detecta, como typos em secrets ou em nomes de outputs gerados dinamicamente, então a depuração via add-commit-push ainda é necessária
- Julian Dunn, responsável pela gestão do projeto GitHub Actions, compartilhou algumas funcionalidades em andamento e prioridades
- a depuração interativa está no roadmap público do GitHub Actions
- também está no roadmap público o trabalho para sair do pinning por tags e SHA em workflows e avançar para algo mais imutável
- o GitHub considera prioridade a saúde e a qualidade do ecossistema de actions oficiais, e adota a estratégia de manter um número pequeno delas para conseguir dar manutenção e atenção suficientes a cada uma
1 comentários
Opiniões no Hacker News
Há duas maneiras de usar workflows do GitHub Actions. 1) “Programar” com GitHub Actions, plugando ferramentas do Marketplace até para coisas como enviar e-mails, fazendo o YAML crescer para 500–1000 linhas, cheio de condicionais e difícil de entender
2) Usar GitHub Actions apenas como “configuração” e ficar perguntando: “dá para empurrar essa complexidade do YAML para um script?”. Se até o envio de e-mail for colocado em um script, o workflow termina com umas 50–60 linhas, e dá para depurar o script localmente, praticamente eliminando aquele ciclo idiota de push-debug-commit. Sempre que entro em uma equipe nova, digo que o 1 é o caminho da loucura e que o 2 é o razoável, mas cerca de metade ainda escolhe o 1. A falta de ferramentas de depuração e o lock-in de fornecedor também viram problemas muito menores quando se escolhe o 2
Ainda assim, muitas vezes não basta empurrar tudo para uma linguagem de programação de verdade. Há casos em que é preciso usar recursos específicos de fornecedor do GHA, explicitar dependências entre jobs ou chamar uma REST API que já está bem abstraída como uma action. É possível reimplementar na linguagem que você quiser, mas isso não elimina a dependência do fornecedor e continua sendo frágil. No fim, a proposta de valor do lock-in de fornecedor do GHA é muito forte, então é preciso ter vantagens mais fortes para convencer as pessoas a seguirem a opção 2
Isso vale não só para o GHA, mas para qualquer servidor de build. O servidor de build deve ser um executor de scripts que adiciona histórico, gestão de artefatos, permissões/auditoria, enquanto o processo real de build deve ser delegado ao repositório que está sendo buildado
Como referência, existe o nektos/act, que tenta reproduzir localmente o comportamento do GHA: https://github.com/nektos/act
Build/testes/assinatura/deploy etc. devem ser escritos como scripts o mais portáveis possível, e esses scripts devem sempre funcionar localmente. Vejo o GitHub apenas como algo que prepara automaticamente o ambiente para executar esses scripts e de fato os executa
O loop de git commit, push e wait é uma experiência de usuário horrível. Os usuários merecem pipelines portáveis que rodem em qualquer lugar, inclusive na máquina local. O Act resolve esse problema até certo ponto, mas em geral não representa fielmente o ambiente real
Muitos pipelines não podem ser executados localmente como em produção, mas, em estágios de desenvolvimento menos críticos, não há motivo para não capturar esses workflows e executá-los localmente. O Garden oferece pipelines portáveis e acrescenta cache em todo o grafo de dependências. Alguns clientes reduziram o tempo de execução em mais de 80%, e os desenvolvedores veem imediatamente se os testes passaram ou falharam sem precisar dar push primeiro para o git. É open source: https://github.com/nektos/act, https://docs.garden.io
make releaseConcordo totalmente com a dor de depurar execuções do GH Actions. A única ferramenta disponível é ativar o debug e executar de novo. Acabo com um monte de commits lixo criados só para corrigir ou depurar pipelines, e a maioria é basicamente jogar coisas para ver se funciona
Mesmo algo bem básico, como lógica reutilizável, é desnecessariamente complexo ou mal documentado. Depois que você descobre, é até fácil, mas a documentação do GitHub era péssima. Parecia que, para ter reutilização, era preciso tornar uma action pública ou colocá-la em outro repositório, mas na prática dá para criar um novo arquivo YAML com a lógica reutilizável. Só que ele precisa ficar na raiz da pasta workflows para funcionar. Trabalhar com GH Actions é realmente doloroso, mas, depois que funciona, é muito conveniente. Seria bom ter algo como um runner local para testar actions antes de commitar e dar push
Isso não é um problema só do GitHub. Outras grandes plataformas de CI também não são muito melhores em termos de workflows e integrações. Hoje tento colocar tudo em scripts tanto quanto possível
GitHub Actions é um sistema de CI/CD péssimo. Ele não permite executar etapas em paralelo na mesma VM, e trabalhos baseados em contêiner são tratados como cidadãos de segunda classe
Por causa do primeiro problema, não dá para paralelizar a configuração de credenciais locais ou dependências de ambiente. Dá raiva ver
google-github-actions/setup-gcloudlevar mais de um minuto. Por causa do segundo problema, é muito difícil expressar a configuração do ambiente de CI com um Dockerfile dentro do repositório, fazer o CI reconstruir a imagem quando o conteúdo dela muda e deixar workflows que dependem dessa imagem aguardando, e, quando o conteúdo não mudou, não reconstruir e executar imediatamente com as dependências já instaladas. No GitHub Actions, você acaba tentando repopular o cache todas as vezes. O cache tem limite de 5 GB, não dá para aumentar nem pagando, e é descartado em FIFO, então, se passar de 5 GB, na prática é como se não existisse. Sinto muita falta do Concourse. Ele permitia jobs paralelos, triggers de pipeline personalizados, entrar por SSH no ambiente de CI para depurar, executar tarefas pontuais sem pipeline, passar o conteúdo de diretórios entre jobs sem criar artefatos, e coisas assim. GitHub Actions está mais para um CI/CD de brinquedo que ficou popular pela facilidade de começar — basta colocar um arquivo em.github/workflowse usar. É bom para atrair usuários, mas, depois dos recursos iniciais, não é poderoso o suficiente para ser chamado de “o melhor”Debug por SSH e tarefas pontuais eram um sonho
Não entendo como ainda não resolveram o fato de o GitHub não conseguir distinguir referências SHA de forks e de não forks, permitindo que um fork contorne as configurações de segurança do GitHub Actions
Se controles de segurança podem ser facilmente burlados, isso é uma questão de segurança grave. É verdade que você precisa convencer alguém a usar o seu SHA, mas engenharia social geralmente é a parte fácil
Sinceramente, chega a dar vergonha de tão ruim. Também é estranho que falhas de build não relacionadas notifiquem, por acaso, apenas o maintainer mais recente que fez merge por último. Eu poderia nem saber que minha atualização/build estava falhando havia uma semana até um maintainer recém-adicionado me avisar no Matrix
A cache action está visivelmente quebrada, e parece não ter mantenedor. Se a UI quebra ou a aba é descarregada, você nem consegue acompanhar em tail os logs de build de uma etapa. Praticamente não há abstrações para tornar workflows portáveis entre nós de trabalho. As imagens padrão são quase um desastre. No começo eu achava apenas irritante por serem inchadas demais, mas, quanto mais investigava, mais pensava: “isso ficou nas mãos de alguém da Microsoft que não entende Linux”. Não há esforço para oferecer imagens mais leves para quem usa Nix ou Docker. Estou saindo do GitHub, e o principal motivo é que o Actions me fez acordar para a realidade. O motivo de eu não programar em YAML é que vejo YAML como uma desonra criada por dinheiro de VC despejado sobre um ecossistema que não conhece tecnologias modernas e realmente úteis
Recomendo fortemente https://pre-commit.ci. Não tenho ligação com eles; sou apenas um usuário satisfeito
A ideia é escrever hooks, ou usar hooks existentes, que verificam o código antes do commit e, quando possível, o corrigem; depois do push, o CI verifica de novo e, se necessário, até faz commits automáticos com as correções. O cache automático é tão bom que chega a ser difícil acreditar na velocidade, e como a mesma configuração é usada na máquina de desenvolvimento local e no CI, muitos problemas de depuração são reduzidos. É gratuito para open source. Ele não faz coisas como releases automáticos, só verificações, mas faz isso muito bem
Para verificar o próprio código-fonte, também dá para rodar análises estáticas com
skip_install = True. Basta executar em um contêiner que tenha o tox instalado como ferramenta global e todas as versões necessárias do Python. Por exemplo, há uma imagem como https://github.com/georgek/docker-python-multiversion. Ela não é mantida, mas é fácil de atualizar. Um boilerplate com algo como[tox] envlist = py{310,311},[testenv]e, em[testenv:check],pre-commit run --all-files --show-diff-on-failurejá é suficienteDepois de passar vezes demais pelo loop
git commit; git push; repeat, encontrei https://github.com/mxschmitt/action-tmate. Ele abre um acesso shell entre as etapas, então não resolve todos os problemas, mas às vezes reduz bastante o sofrimentoPessoalmente, eu gostaria de poder anexar relatórios HTML a uma action run sem precisar usar algo como
actions/upload-artifactEspecialmente em builds de teste, muitas vezes quero ver rapidamente o relatório HTML gerado. As formas que conheço hoje são upload-artifact ou GH Pages, mas GH Pages não é muito bom quando há várias saídas de relatório no mesmo repositório ou quando você quer ver rapidamente um relatório antigo, não o mais recente. Seria bom ter uma action simples, tipo
attach-report, que adicionasse um link para o relatório HTML ao resumo do job e renderizasse o HTML ao clicar. Outros sistemas automatizados de CI/CD oferecem, por padrão, suporte muito mais rico para capturar e visualizar relatórios HTML$GITHUB_STEP_SUMMARY, sem passar por upload de artefatos ou coisas do tipoVejo o GH Actions, na prática, como algo bem próximo de um rebranding do “Azure Pipelines” da Microsoft. Como alguém que já usou todas as formas anteriores dos pipelines de build e release do TFS/VSTS/AzDO, essa equipe não é boa nisso
O Azure Pipelines só ficou minimamente utilizável porque todas as abordagens tentadas antes literalmente falharam. Também houve um projeto para executar pipelines localmente, permitindo um loop edit-run-debug em um ambiente pessoal sem commits, mas é claro que foi cancelado: https://github.com/microsoft/azure-pipelines-agent/pull/2687/files#diff-570b52bcb927a5365c22d17a21e8e19e1ba3427c7f3ec16ea64b7b3d14e4ee20. Ainda assim, há ferramentas que melhoram a qualidade de vida. Por exemplo, uma extensão do VS Code que reconhece a sintaxe: https://marketplace.visualstudio.com/items?itemName=ms-azure-devops.azure-pipelines. Para dizer algo um pouco polêmico: se tivessem usado XML em vez de YAML, apenas a declaração xmlns no topo já permitiria validação na maioria dos bons editores de código, sem intervenção adicional do usuário. XML é horrível, mas ao descartá-lo por completo também perdemos muitos recursos úteis junto