Article image
Sebastião Almeida
Sebastião Almeida03/12/2022 13:55
Compartilhe

Criando uma pipeline de CI/CD para aplicações Go (Golang)

  • #GitHub
  • #Arquitetura de Sistemas
  • #GoLang

Criando uma pipeline de CI/CD para aplicações Go (Golang)

Nesse artigo eu vou mostrar como podemos criar pipelines de CI/CD utilizando somente o Github.

Para criarmos uma pipeline de CI/CD para aplicações Go vamos utilizar um recurso do Github chamado Github Actions, esse recurso vai permitir que, após cada Pull Request ou push para a branch master, seja realizado um lint do nosso código, sejam executados os testes unitários e seja gerado o build da nossa aplicação. Teremos também um recurso de geração de releases a cada nova tag criada no repositório.

A APLICAÇÃO DE EXEMPLO

Para essa explicação, construiremos um básico "Hello Word" em golang.

O repositório de exemplo está acessível aqui, sinta-se à vontade para cloná-lo ou apenas acompanhá-lo.

Não vou entrar em muitos detalhes sobre o código do aplicativo em si. É um aplicativo “Hello world” básico que imprime o texto “Hello GitHub” na saída padrão.

Aqui está o código para o arquivo main.go:

image

E aqui está a função “HelloGithub”:

image

Por fim, os testes unitários

image

COMO O GITHUB ACTIONS FUNCIONA

O GitHub Actions nos permitirá a construção de uma Pipeline de CI/CD completa, profundamente integrada com o ecossistema GitHub, sem a necessidade de usar um serviço de terceiros como Travis CI ou Circle CI.

Os recursos do GitHub Actions são baseadas no conceito de workflows (fluxos de trabalho). Um workflow nada mais é do que um conjunto de tarefas e etapas que são executadas quando alguma condição ou evento é atendido. (Ex: um push para o repositório, um pull request, uma implantação, etc). 

Você pode ter vários workflows por projeto, cada um respondendo a um conjunto diferente de eventos.

Em nosso exemplo, teremos dois workflows. O workflow “Main” que será acionado quando houver um push na branch master ou quando um PR for criado e o workflow "Release" que será executado quando uma nova tag for enviada ao GitHub, que criará uma nova release do nosso aplicativo.

Cada workflow é composto por um ou mais jobs. Nosso workflow "Build" terá 3 jobs (Lint, Build e Test) e nosso workflow "Release" terá um único job "release".

Cada job é feito de etapas (steps). Por exemplo, o job “Test” terá etapas para fazer o checkout do código-fonte e etapas para executar os testes.

A melhor parte é que você não precisa reinventar a roda e pode reutilizar ações existentes criadas pelo próprio GitHub ou pela comunidade e até mesmo imagens regulares do Docker em suas etapas.

Veremos exemplos de como fazer isso nesse artigo.

Os workflows são definidos em arquivos YAML localizados no caminho: github/workflows do seu repositório.

Cada arquivo no diretório /workflows representa um workflow diferente.

Veja o arquivo que contém o workflow de Build.

image

Começamos definindo um nome para o workflow e quando ele será executado. No nosso caso, queremos que ele seja executado quando houver um push na branch master ou um pull request. Há muitos eventos que você pode ouvir. Você pode ler mais sobre esse assunto aqui.

image

O nosso workflow  contém 3 jobs, “Lint”, “Test” e “Build”.

Vamos dar uma olhada rápida no job “Lint”:

image


Aqui, especificamos que queremos que este job seja executado em uma máquina Ubuntu. (palavra-chave “runs-on” ).

As ações têm suporte para Linux, Mac e Windows, bem como Docker. 

Em seguida, definimos as etapas que compõem nosso job.

A primeira coisa é instalar o Go. O GitHub já disponibiliza uma ação para ele, então vamos usá-lo:


image



Acho que a sintaxe é bem explicativa. A palavra-chave with”nos permite especificar os argumentos exigidos pela ação. Nesse caso, a ação “setup-go” nos permite especificar a versão go a ser usada.

O próximo passo é verificar o código-fonte. Novamente, usaremos uma ação pronta do GitHub:

image

E finalmente vamos instalar e executar a ferramenta golangci-lint:

image

Creio que para termos uma visão geral sobre a estrutura dos jobs, isso nos basta. O resto dos jobs são bastante semelhantes. Vamos dar uma olhada no job "Test".

image

E com isso, finalizamos nosso primeiro workflow ;)

CRIANDO REGRA PARA AS BRANCHES

Agora vamos fazer nossa pipeline proteger os merges de nossos pull requests.

No repositório do seu projeto no GitHub, acesse a opção "settings" e dentro dela, a opção "branches", na tela que aparecer, clique no botão "add rule", você será direcionado a uma pagina para adicionar regras de proteções a suas branches.

No campo "Branch name pattern" digite * para que a nova regra seja aplicada a todas as branches.

image

Nessa mesma página selecione a opção "Require status checks to pass before merging"e no campo de pesquisa que surgir, procure e selecione o nome dos jobs que criamos: Lint, Test e Build.

image

Para finalizar, clique no botão "create" no final da página para finalizar a criação da sua nova regra.

image

 Essa nova regra vai permitir o merge nas branches somente após todos os jobs da nossa pipeline serem aprovados com sucesso.

Vamos criar uma nova branch, fazer uma mudança no código e abrir um PR para ver o workflow "Main" em ação.

image

Adicionaremos mais uma linha de código em nosso arquivo main:

image

Vamos fazer um push da nossa nova branch para o repositório remoto e criar um pull request para a branch main. O fluxo de trabalho "Build" será iniciado imediatamente.

O merge para a branch main ficará bloqueado até que todas as etapas do workflow sejam executadas e aprovadas, você poderá ver o status de cada etapa diretamente no Pull Request:

image


Podemos também criar um teste com falha de propósito para vermos o merge bloqueado devido ao check "Test" não ter sido aprovado.

image

image

O WORKFLOW DE RELEASE

É hora de criar nosso workflow de “Release”. Cada workflow é um arquivo separado, então criaremos o arquivo: .github/workflows/release.yml com o seguinte conteúdo:

image

No código acima, especificamos que o workflow será executado sempre que uma nova tag for criada.

O job fará o check-out do código e usará a imagem oficial do docker GoReleaser para fazer todo o trabalho.

 

Ao usar o docker é possível definir o “args” e o “entrypoint” do container. Neste caso, usaremos o entrypoint padrão, mas definiremos um argumento diferente nas etapas “Validate” e “Create Release”.

Também especificamos a variável de ambiente GITHUB_TOKEN exigida pelo Go Releaser para criar nossa release no GitHub. Esta variável será passada para o container. Observe que a variável secrets.GITHUB_TOKEN é injetada automaticamente pela plataforma Actions, portanto, não é necessário criá-la.

Se você criar uma tag e enviar para o repositório, uma nova versão será criada no GitHub com os artefatos do aplicativo e Changelog gerados pela ferramenta GoReleaser.

image

image

E temos nossa primeira pipeline construído com GitHub Actions. ;)

É um exemplo muito básico, mas acho que o suficiente para dar uma boa ideia de como funciona.

DOCUMENTAÇÕES

Ferramenta de lint de código Golang (golangci-lint): https://github.com/golangci/golangci-lint

Ferramenta de release de projetos Golang (GoReleaser): https://goreleaser.com/ci/actions/?h=git

Compartilhe
Comentários (2)
Mario Junior
Mario Junior - 24/02/2023 11:31

Muito bom! Obrigado por compartilhar. 👍

João Anastácio
João Anastácio - 03/12/2022 15:37

Golang e muito top!