Construindo um bom pipeline de CI/CD com o GitHub Actions Não se trata mais de um recurso extra "para quando houver tempo": em equipes modernas, é praticamente um requisito para uma implementação rápida e confiável. Mesmo assim, encontrar um exemplo completo, genérico e bem elaborado que você possa adaptar à sua empresa costuma ser muito mais complicado do que parece.
Nas linhas seguintes, combinaremos a teoria clássica de CI/CD. com exemplos de implementação no mundo real usando GitHub Actions, pipelines reutilizáveis, Tasks, scripts bash, Módulos PnP do PowerShellImplantações em Kubernetes, Google Cloud e Kinsta, juntamente com as melhores práticas de segurança, monitoramento e escalabilidade. A ideia é que você possa pegar esses elementos, adaptá-los ao seu contexto e evitar muitas das armadilhas comuns.
Por que você precisa de um pipeline de CI/CD bem estruturado?
No desenvolvimento profissional atual, CI/CD é o sistema circulatório do código.Ele integra alterações, executa testes, cria artefatos e implementa novas versões com intervenção mínima. Sem esse fluxo de trabalho, cada implementação se torna um processo lento, propenso a erros e manual.
A integração contínua (IC) concentra-se na validação de mudanças. Assim que são enviados para o repositório, testes unitários, verificações de código e análises estáticas são executados para detectar erros o mais rápido possível. Quanto mais rápido você receber feedback, mais cedo poderá corrigi-los e menos dolorosa será qualquer regressão.
Implantação Contínua (CD no sentido de Implantação Contínua) Ou, dependendo do nível de automação, a Entrega Contínua adiciona a automação da parte de lançamento: criação de imagens, publicação de pacotes, implantação em ambientes de teste, homologação ou produção e até mesmo alteração do tráfego usando estratégias azul-verde ou canário.
Em empresas com muito código legadoUm bom pipeline é uma das melhores alavancas para modernizar o ecossistema: ele permite introduzir testes em serviços legados, automatizar tarefas que antes eram feitas manualmente e reduzir o custo de manutenção de infraestruturas como Jenkins ou Nexus, que se tornaram obsoletas.
O que é o GitHub Actions e por que ele se encaixa tão bem com CI/CD?
O GitHub Actions é a plataforma de automação integrada ao GitHub. Ele permite definir fluxos de trabalho em arquivos YAML dentro do próprio repositório. Com ele, você pode compilar, testar, analisar e implantar seu software sem configurar servidores de CI externos.
Um fluxo de trabalho é um conjunto de tarefas e etapas. que é desencadeado por eventos como push, pull_request, schedule (CRON), workflow_dispatch (manual) ou até mesmo ações sobre problemas. Cada tarefa é executada em um executor (por exemplo, ubuntu-latest) e consiste em etapas que utilizam ações ou comandos reutilizáveis run.
O GitHub oferece um enorme mercado para ações. onde você encontra integrações prontas para praticamente tudo: Docker, Kubernetes, AWS, Azure, Google Cloud, SonarCloud, Slack, Jira, análise de segurança, linters para milhares de linguagens, etc. Isso reduz significativamente o tempo necessário para configurar pipelines avançados.
Em comparação com soluções como Jenkins ou Concourse.O GitHub Actions possui diversas vantagens claras: é um serviço gerenciado (você não precisa gerenciar servidores), está intimamente ligado ao código, utiliza um modelo de pagamento conforme o uso e conta com o suporte de uma comunidade enorme. Além disso, muitos desenvolvedores já estão familiarizados com ele por meio de projetos pessoais, o que reduz significativamente a curva de aprendizado.
Componentes básicos de um fluxo de trabalho do GitHub Actions
Tudo começa com um arquivo YAML em .github/workflows/, por exemplo ci.yml o build-test-deploy.ymlEmbora a sintaxe possa crescer consideravelmente, a estrutura básica é relativamente simples.
As seções principais do YAML são: name (nome do fluxo de trabalho), on (eventos que o desencadeiam), jobs (conjunto de tarefas lógicas), e dentro de cada tarefa, runs-on (corredor), steps (passos), env (variáveis globais) e if (condições para a execução de etapas ou tarefas).
Os empregos representam blocos de trabalho. que podem ser executados em paralelo ou em cadeia usando needsDentro de cada tarefa, as etapas utilizam ações (uses:) ou comandos (run:Um exemplo típico inclui: verificação de código, instalação de dependências, execução do linter, testes e compilação.
Segredos e variáveis de ambiente Eles são gerenciados no nível do repositório, da organização ou do ambiente. Nos fluxos de trabalho, eles são referenciados com ${{ secrets.MI_SECRET }} e permitem trabalhar com chaves de API, tokens de implantação ou credenciais de nuvem sem expô-los no repositório.
O YAML também permite a criação de arrays de execução. com strategy.matrixMuito útil para testar seu código em várias versões do Node, Python ou Java, ou até mesmo em diferentes sistemas operacionais, sem precisar escrever o mesmo bloco várias vezes.
Projete um pipeline CI/CD moderno usando as melhores práticas.
Um oleoduto saudável geralmente é dividido em fases bem definidas.: verificações rápidas (lint, testes unitários), construção do artefato, lançamento (versão, rotulagem, changelog, publicação no repositório de artefatos) e implantação em um ou mais ambientes.
A fase de integração contínua deve ser o mais rápida possível. Isso garante que qualquer solicitação de push ou pull receba feedback quase imediato. Uma prática comum é executar as várias verificações em paralelo usando arrays ou jobs separados, assumindo um custo ligeiramente maior em troca da redução do tempo total de espera.
Desacoplar o pipeline da linguagem concreta.Você pode usar uma ferramenta de tarefas como o Task (similar ao Make, mas com uma sintaxe mais amigável). Dessa forma, o fluxo de trabalho do GitHub Actions invoca apenas tarefas genéricas (task test, task lintetc.) e cada repositório define como eles são implementados internamente, dependendo se é Node, Java, Python, etc.
O versionamento e os artefatos entram em jogo durante a fase de lançamento.Aqui você cria uma imagem Docker, um arquivo jar/war, um pacote npm ou qualquer outro artefato, o envia para o registro correspondente (registro Docker, Maven, npm no Registro de Artefatos, etc.), marca os commits e gera releases ou changelogs no GitHub com ferramentas como git-cliff ou ações de liberação.
Finalmente, a fase de implantação. Mova esse artefato para o ambiente de execução: Kubernetes (GKE), Google App Engine, Cloud Functions, serviços na Kinsta, seus próprios servidores via SSH, etc. Aqui você pode encadear etapas subsequentes, como testes funcionais após a implantação ou notificações no Slack com detalhes da versão.
Exemplo: Pipeline completo com ESLint, testes e implantação na Kinsta
Um exemplo bastante ilustrativo é o uso do GitHub Actions. Validar uma aplicação React com ESLint e testes unitários e, em seguida, implantá-la na Kinsta usando sua API. Tudo é orquestrado em um único fluxo de trabalho de CI/CD.
A primeira parte do YAML define o gatilho. e o nome do pipeline. Por exemplo, que ele seja executado em cada push y pull_request para o ramo maine até mesmo agendado com tarefas CRON (por exemplo, todos os dias à meia-noite ou todas as segundas-feiras às 8:00 UTC) usando o evento schedule.
O primeiro trabalho em andamento pode ser chamado de eslint e é responsável por verificar a sintaxe do código. Ele é executado em ubuntu-latest e utiliza uma variedade de versões do Node (por exemplo, 18.x, 20.x), com etapas para obter e configurar o Node com actions/setup-node, armazenar em cache as dependências do npm, instalar com npm ci e jogar npm run lint.
O segundo emprego, testsDepende de eslint através needs: eslintPortanto, ele só é executado se a verificação de sintaxe for bem-sucedida. Internamente, o padrão se repete: checkout, instalação de dependências e execução de npm run test em uma versão específica do Node.
O terceiro emprego, deployEstá acorrentado após ambos os trabalhos uso needs: e utiliza uma etapa com curl Para chamar a API da Kinsta. Para isso, a chave da API e o ID do aplicativo são configurados como segredos no GitHub (KINSTA_API_KEY y APP_ID) e são expostos no trabalho através de env para construir a solicitação POST que aciona a implantação.
É importante entender que este trabalho de implantação A Kinsta considera a mera aceitação da API um sucesso; no entanto, se a implementação falhar posteriormente internamente na Kinsta, o fluxo de trabalho do GitHub ainda poderá mostrar um status verde. Isso deve ser levado em consideração para evitar complacência e complementar o processo com monitoramento pós-implementação.
Gerenciamento avançado de cron e agendamento de fluxos de trabalho
A sintaxe CRON no GitHub Actions É baseado no formato UNIX de cinco campos: minuto, hora, dia do mês, mês e dia da semana. Cada campo pode usar asteriscos, intervalos, listas e etapas (*, 1-5, 1,15,30, */5), que permite agendar tarefas de manutenção, backups, limpezas ou verificações periódicas.
Por exemplo, o 0 0 * * * executar o fluxo de trabalho toda meia-noite (UTC), enquanto 0 8 * * 1 Isso acontece todas as segundas-feiras às 8h. Essa ação se integra perfeitamente aos gatilhos habituais de push y pull_requestpara que o mesmo arquivo YAML possa reagir tanto a alterações de código quanto a execuções agendadas.
Essa funcionalidade é ideal para tarefas que não fazem sentido serem lançadas em cada commit.: verificações de segurança intensivas (por exemplo, com o OWASP Dependency Check em Java), auditorias de dependências, verificações de cobertura de testes ou limpeza de artefatos antigos no registro.
Reutilização de fluxos de trabalho: escalando CI/CD para centenas de repositórios.
Quando sua organização possui dezenas ou centenas de repositóriosCopiar e colar o mesmo arquivo YAML em todos os lugares é uma receita para o caos. Qualquer alteração exige a modificação de metade do GitHub Enterprise, tornando quase impossível manter a consistência e as melhores práticas.
A solução reside na criação de fluxos de trabalho reutilizáveis. centralizados em um repositório de "modelos" de CI/CD. Esses fluxos de trabalho expõem entradas e saídas, e cada serviço define apenas um pequeno arquivo YAML que os invoca, passando parâmetros como tipo de artefato (Docker, biblioteca Java, pacote npm), ambiente de execução de implantação (GKE, GAE, Cloud Function etc.) ou itens de tarefa que precisam ser executados.
Um padrão comum é separar três grandes fluxos de trabalho reutilizáveis.: um de build-check-task (integração contínua), outro dos build-release-dockerfile ou outros artefatos e uma terceira implantação (deploy-gke, deploy-gaeetc.), de forma que cada repositório construa seu pipeline combinando-os.
Para encapsular a lógica compartilhada, também é possível definir ações personalizadas. en .github/actionsPor exemplo, para configurar Gradle, Java, Node ou Task, obter metadados de compilação, publicar imagens Docker, marcar versões no Git com um script bash ou enviar notificações para o Slack. A regra de ouro é que os repositórios de serviços devem usar apenas fluxos de trabalho reutilizáveis, e não essas ações diretamente, para que a compatibilidade com versões anteriores seja mais fácil de gerenciar.
Integração contínua rápida com tarefas, matrizes e análise estática.
Durante a fase de construção ou verificação, é aconselhável acionar várias ações em paralelo.Testes unitários, análise estática (PMD, Checkstyle, SpotBugs em Java; ESLint em JS/TS), varredura com SonarCloud, etc. Isso mantém o tempo total do pipeline em um nível razoável, mesmo em bases de código grandes.
A tarefa (Taskfile.yml) atua como uma camada de abstração. em comandos específicos, permitindo que o fluxo de trabalho de CI simplesmente os chame. task check, task test o task lintPara um projeto Java, essas tarefas podem ser delegadas ao Gradle com JUnit, PMD, Checkstyle e SpotBugs; para um projeto Node, ao Jest, ESLint e ferramentas de segurança como... npm audit ou similar.
O GitHub Actions adiciona a parte do array. Para executar as mesmas tarefas em diferentes versões do ambiente de execução, como por exemplo, testar uma biblioteca Node nas versões 16, 18 e 20, ou um projeto Python nas versões 3.10 e 3.12, basta declarar um array de versões e usá-lo na configuração da tarefa.
Essa abordagem é especialmente útil em organizações que desejam oferecer suporte a múltiplas plataformas. (Java, Node, TypeScript, Python, etc.) sem precisar reescrever a lógica do pipeline para cada repositório: a tarefa se adapta a cada linguagem e os fluxos de trabalho reutilizáveis permanecem praticamente os mesmos.
Fase de lançamento: versionamento, etiquetagem e publicação de artefatos.
Após a aprovação nas verificações, é hora de construir o artefato que será efetivamente implantado.Imagem Docker, arquivo JAR, pacote npm, o que for apropriado. Isso envolve tanto as ferramentas da linguagem quanto os registros e a política de versionamento da organização.
Alguns projetos Java utilizam plugins como o Gradle Axion. Para gerenciar versões com base em tags do Git. Em contextos mistos (Java, Node, etc.), pode ser mais simples usar um script bash personalizado que calcule a próxima versão (por exemplo, usando SemVer), crie a tag, envie-a para o repositório remoto e gere a versão correspondente.
Ferramentas como git-cliff Eles ajudam a gerar registros de alterações. Com base nas mensagens de commit, as alterações são classificadas por tipo (funcionalidade, correção, alteração que quebra de compatibilidade, etc.). A integração dessas alterações ao pipeline garante que cada versão tenha um changelog claro, sem que ninguém precise escrevê-lo manualmente.
Para publicar artefatos, as ações e credenciais apropriadas são combinadas.Registros Docker (Docker Hub, GitHub Container Registry, Artifact Registry), repositórios Maven, registros npm, etc. Novamente, as credenciais são armazenadas como segredos e injetadas nos jobs somente quando necessário.
Implantação contínua em Kubernetes, GCP, Kinsta e outros ambientes.
A implantação é onde a CI/CD interage com a infraestrutura.Aqui, o GitHub Actions se integra perfeitamente a praticamente qualquer plataforma: Kubernetes, App Engine, Cloud Functions, servidores tradicionais, plataformas como Kinsta, etc.
Para o Kubernetes (por exemplo, no GKE), o padrão usual é o seguinte: É o seguinte: autenticar com o Google Cloud (usando as ações oficiais), configurar kubectl No contexto do cluster, aplique os manifestos ou gráficos do Helm e, se necessário, execute uma implantação controlada (por exemplo, com canary ou blue-green) e verifique o status com comandos de kubectl rollout status.
No caso do App Engine ou do Cloud FunctionsO pipeline cria a imagem ou o artefato, publica-o no Registro de Artefatos e, em seguida, invoca os comandos de implantação. gcloud apropriado, novamente usando credenciais gerenciadas, como segredos e executores efêmeros.
Quando a implantação é realizada em APIs externas, como as da Kinsta,geralmente um passo é suficiente curl ou uma ação especializada que envia a solicitação com o token de autenticação e os parâmetros necessários (ID do aplicativo, branch, etc.). A tarefa é considerada bem-sucedida se a API responder corretamente à nova solicitação de lançamento.
A implantação é quase sempre acompanhada de uma notificação. para o Slack, Teams ou outras ferramentas de comunicação, indicando qual serviço foi implantado, em qual ambiente, com qual versão, quem o acionou e links para os registros do fluxo de trabalho. Em produção, isso também serve para auditoria e rastreabilidade.
Controle de qualidade: segurança, monitoramento e registros.
Automatizar a compilação e a implantação é ótimo, mas sem visibilidade Em relação ao que está acontecendo, o pipeline pode se tornar uma caixa preta. O GitHub Actions oferece registros detalhados por execução, por tarefa e por etapa, permitindo diagnosticar falhas de compilação, teste ou implantação.
Para necessidades mais avançadas, são integrados serviços de observabilidade externos. ferramentas como Datadog, New Relic ou Splunk coletam métricas sobre fluxos de trabalho, tempos de execução, taxas de falha, etc., ajudando a detectar gargalos e priorizar otimizações de pipeline.
Em paralelo, a segurança desempenha um papel fundamental.Gerenciamento de segredos criptografados, políticas de acesso mínimo necessário, revisão de permissões de ação, incorporação de scanners de vulnerabilidade de código e dependências (varredura de código, varredura de segredos, OWASP, etc.) nos próprios fluxos de trabalho.
Muitas equipes também adicionam testes pós-implantação. No ambiente recém-atualizado: testes funcionais de ponta a ponta, verificações de desempenho, testes básicos de fumaça e, caso algo dê errado, mecanismos automatizados de reversão que restauram a versão estável anterior.
Governança de fluxo de trabalho: branches e pull requests protegidos
A forma de trabalhar com branches e pull requests deve estar alinhada com CI/CD. para que tudo faça sentido. O mais comum é proteger o ramo principal (main o master) e exigem que qualquer alteração passe por um PR e seja aprovada nas verificações do pipeline.
O GitHub permite definir regras de proteção de branches. Essas políticas forçam o uso de pull requests, bloqueiam commits diretos e exigem que certas verificações de status (fluxos de trabalho de ação específicos) estejam verdes antes de permitir a mesclagem. Elas também podem exigir revisões mínimas, regras de aprovação, etc.
Este modelo garante que o código que chega à produção O sistema passou por revisão humana e por todas as verificações automatizadas do pipeline, reduzindo drasticamente o risco de erros ou vulnerabilidades graves passarem despercebidos.
Em empresas com múltiplos ambientes A implantação em ambiente de produção (desenvolvimento, teste e produção) geralmente é reservada para fusões na ramificação principal, enquanto outras ramificações podem acionar implantações em ambientes anteriores para testes internos ou demonstrações.
Considerando o panorama geral, um pipeline de CI/CD bem projetado com GitHub Actions Torna-se a espinha dorsal do desenvolvimento: integrando alterações, executando conjuntos de testes abrangentes, criando e publicando artefatos, implantando em múltiplas plataformas de nuvem, monitorando com ferramentas de observabilidade e governando por meio de regras claras de ramificação e pull requests. Com fluxos de trabalho reutilizáveis, ações personalizadas, ferramentas auxiliares como Task, Release Action e Git Cliff, e um gerenciamento robusto de segredos e permissões, é possível dar suporte a tudo, desde aplicativos Python simples até arquiteturas Kubernetes complexas, mantendo a velocidade de entrega, a qualidade do código e a segurança sem sobrecarregar a equipe com tarefas manuais.