Criando uma GitHub Action com Node.js
Nesse post irei demonstrar como construir uma Action com Node.js, processo que no geral é simples e lhe dá autonomia para não se limitar ao uso de Actions preexistentes que não façam exatamente o que você precise. Automatizaremos como exemplo uma ação sempre realizada ao abrirmos um pull request em alguns repositórios da DM: atribuí-lo para o usuário que o criou.
Não irei entrar em detalhes sobre a feature Actions do GitHub pois a documentação deles é bem completa, vale recorrer para entender alguns conceitos/encontrar mais exemplos.
A Action é uma aplicação que realiza uma tarefas como lint, testes, deploy a partir de um evento como o push em uma branch ou criação de uma nova tag. É possível organizar e combinar as Actions, compondo um workflow: nele está definido quais Actions/comandos serão executados, quando e como. As regras do workflow ficam definidas em um arquivo de configuração YAML, encontrado no caminho .github/workflows/
de um repositório no GitHub.
Ela pode ser desenvolvida em qualquer linguagem, desde que executável via Docker. Iremos construir a nossa Action com Node.js, que segue um processo diferente e principalmente por ser executada em menor tempo que uma Action de container Docker.
Partindo de um repositório público clonado localmente e configurado via $ npm init
, o primeiro passo é criar um arquivo action.yml
de metadata da Action na raiz do projeto:
Os primeiros campos definem como a sua Action irá aparecer no Marketplace do GitHub ao ser publicada. Em inputs
estão os parâmetros, cujos valores são definidos no arquivo de um workflow que irá usar esta Action. O input token
será utilizado para se comunicar com a API do GitHub via Octokit. Neste caso, iremos usar o token padrão disponibilizado pela Actions, ${{ github.token }}
. Em runs
, definimos qual a versão do Node.js a utilizar e qual o arquivo de entrada da aplicação. Esse é o básico para seguirmos, mas recomendo conferir a documentação do arquivo de metadata para conhecer os outros campos disponíveis.
É obrigatório que sua Action possua um README.md
, então crie algo como:
O GitHub disponibiliza um toolkit para simplificar a construção das Actions. Nele se encontram diversos pacotes, dentre os quais usaremos dois:
- @actions/core, para ler o input e efetuar o log em caso de erro;
- @actions/github, para efetuar o assign via API do GitHub.
Instale-os com $ npm i --save @actions/core @actions/github
.
Crie uma pasta src/
e dentro dela, o arquivo index.js
:
Este arquivo será o entrypoint da aplicação, lendo inputs e executando ações de acordo. O código lê o input com getInput()
e executa uma função handle()
, falhando a Action via setFailed()
com uma mensagem em caso de erro.
Agora, crie o arquivo github.js
dentro de src/
. Nele, iremos definir a função handle()
, que irá criar um Octokit e executar assign()
. É interessante dividir as responsabilidades aqui para poder verificar o context.eventName
e executar diferentes funções a partir de seu conteúdo.
Dentro dessa função, iremos utilizar a API de Pull Request para buscar os dados do PR atual. O context
do Octokit possui informações sobre a execução do workflow e do evento que gerou o trigger; mais especificamente, possui o payload completo do webhook da API. Os eventos são diversos, mas há exemplos e listagem das propriedades de cada evento na documentação.
Neste cenário, só estamos interessados em PRs. Então faremos uma verificação na função handle()
, lançando um erro se for outro evento. Em context
conseguimos obter os valores de owner
, repo
e number
para utilizar como parâmetros de addAssignees()
, junto com actor
.
O pacote do Toolkit possui a função addAssignees, que possui o mesmo efeito prático que um POST
no caminho /repos/:owner/:repo/issues/:issue_number/assignees
na API REST v3 do GitHub. Utilizaremos-a em assign()
:
Veja que para realizarmos assign para um usuário específico, bastaria adicionar um novo campo em inputs
no arquivo action.yml
representando o nome do usuário e usar esse valor em assignees
. Seguindo essa ideia, poderíamos usar outras funções do Octokit como pull.createReviewComment()
para criar um comentário ou issues.addLabels()
para adicionar labels ao PR.
Para a Action poder ser executada, é preciso que o repositório possua todas suas dependências. Como uma alternativa a subir o node_modules/
, iremos utilizar a ferramenta @zeit/ncc
para compilar o código e os pacotes em um .js
único. Instale com $ npm i --save-dev @zeit/ncc
. Para facilitar a execução, adicione o NPM script em package.json
:
E crie um .gitignore
para não subir os pacotes:
Execute o script, suba suas alterações no GitHub e gere uma tag:
O GitHub recomenda alguns padrões de versionamento, mas um simples v1 já nos permite utilizar a Action em outro projeto. Para isso, basta adicionar um arquivo de workflow, ex. pull-request.yml
em .github/workflows/
do repo com o seguinte conteúdo (lembre-se de substituir os campos em <>):
Efetuando o merge deste código na branch principal, ao abrir um PR você receberá o assign automaticamente 👌.
Se acessar a página do repositório da Action, virá esse botão para gerar um release e publicar a Action no Marketplace. Se pretende compartilhar sua Action com outras pessoas, é interessante que o faça.
Esse exemplo foi inspirado na Action Auto assign PR and request review, disponível no GitHub da Delivery Much. Além de auto assign para abertura de pull requests, é possível solicitar review para uma lista de usuários e times. Fique a vontade para usá-la 😉
Espero que com esse exemplo prático você consiga ter ideias para automatizar algum processo nos projetos que você trabalhe. Há diversas possibilidades, como podemos ver na Awesome Actions e nesta Action que permite você jogue xadrez no seu repositório ♟.
Stay home!