Entrega contínua de blogs Hugo com GitHub Actions
Aprenda a agilizar o worflow de publicação do seu blog feito com Hugo usando entrega contínua (continuous delivery) com GitHub Actions para atualizar automaticamente a página no GitHub Pages.
O Hugo é hoje um dos melhores e mais populares geradores de sites estáticos. Já o GitHub Pages oferece um excelente serviço para distribuir gratuitamente conteúdos estáticos, o que é perfeito para blogs. Logo, é natural que as duas ferramentas sejam comumente usadas em conjunto.
A partir do momento que o blog estiver todo configurado, com todos os templates customizados da forma como queremos, o dia-a-dia do blog passa a revolver em torno da criação de conteúdo.
Existem várias formas de trabalhar, mas qualquer workflow escolhido terá um mínimo de passos comuns:
- Criar um novo conteúdo (como posts, artigos, imagens, etc)
- Rodar o comando
hugo
com alguma parametrização para gerar o conteúdo estático atualizado - Publicar o novo conteúdo estático no repositório
<nomeusuario>.github.io
O restante deste artigo vai tratar de configurar um workflow usando GitHub Actions para automatizar o processo de gerar conteúdo estático e publicá-lo. Ao final, teremos um processo de entrega contínua para nosso blog.
Nosso esquema será esse: Commit na master -> Workflow GitHub Actions -> Blog github.io atualizado
A única ação necessária será um commit no branch master
contendo as alterações desejadas. Estou usando este fluxo de trabalho e o considero bem fácil e prático.
O tutorial abaixo assume que você está usando dois repositórios:
- Repositório com o template do Hugo e o conteúdo
- Repositório
<nomeusuario>.github.io
E tudo o que você precisa para implementá-lo é seguir os passos descritos abaixo.
#Configuração do workflow de CI/CD com GitHub Actions
Até uns dias atrás, eu nunca tinha trabalho com as ferramentas de CI/CD do GitHub. Mas o GitHub Actions se mostrou bastante fácil de aprender, especialmente para quem já tem alguma experiência com ferramentas de CI/CD como o Circle CI ou Travis.
O GitHub Actions chama os pipelines de workflows. Para criar um novo workflow, primeiro você deve criar, na raiz do repositório do seu blog (onde fica seu template), a seguinte estrutura de diretórios:
.github/workflows
Dentro você terá de adicionar um arquivo de workflow. Ele pode ter qualquer nome, e deve terminar com a extensão .yml
ou .yaml
.
Abaixo, podemos ver o workflow que estou usando no meu blog atualmente, já comentado. Você também pode baixar ele aqui.
name: Deploy para <nomeusuario>.github.io
on:
push:
branches:
- master # somente commits no branch master geram deploys
# Também pode rodar um deploy automaticamente todos os dias
# para publicar posts agendados (com datas futuras)
schedule:
- cron: '0 8 * * *' # 08:00 am UTC
jobs:
# O job "build" é responsável por baixar o template, o hugo e
# compilar o site estático
build:
name: Gerar conteúdo estático
runs-on: ubuntu-latest
steps:
# Primeiro, fazemos checkout do repositório
- uses: actions/checkout@master
# Depois, usamos uma action para clonar o repositório do tema
# Substitua o endereço e o diretório por um de sua preferência
- name: Atualizar template
uses: "srt32/git-actions@v0.0.3"
with:
args: "git clone https://github.com/htr3n/hyde-hyde.git themes/hyde-hyde"
# Permite instalar qualquer versão do hugo que você precisar
- name: Setup Hugo
uses: peaceiris/actions-hugo@v2
with:
hugo-version: '0.69.0' # se quiser a última, pode usar 'latest'
extended: true # permite compilar SCSS e SASS
- name: Build
run: hugo # gera os arquivos estáticos
# Esta action faz upload do diretório /public como artefato
# do job "build"
- name: Upload dos artefatos
uses: actions/upload-artifact@v1
with:
name: public
path: ./public
publish:
name: Publish to <nomeusuario>.github.io
runs-on: ubuntu-latest
needs: build # o job "publish" só roda se deu tudo certo durante o "build"
steps:
# Primeiro fazemos checkout do nosso repositório github.io
# no diretório static-site
- uses: actions/checkout@v2
with:
repository: '<nomeusuario>/<nomeusuario>.github.io'
# token com as permissões necessárias
token: ${{ secrets.CD_BLOG_TOKEN }}
path: static-site
# Fazemos download dos artefatos do job "build" para o
# diretório /public
- name: Download dos artefatos
uses: actions/download-artifact@v1
with:
name: public
# Copiamos os novos arquivos estáticos em /public para /static-site
- name: Aplicar atualização
shell: bash
run: |
cp -r ./public/* ./static-site
# Adicionamos as alterações e geramos um commit
- name: Commit files
run: |
cd static-site
git config --local user.email "<email-valido>"
git config --local user.name "GitHub Action - Deploy"
git add .
git commit -m "Add changes" -a
# Por último, precisamos fazer um push para o repositório github.io
# de destino
- name: Push changes
uses: ad-m/github-push-action@master
with:
repository: '<nomeusuario>/<nomeusuario>.github.io'
github_token: ${{ secrets.CD_BLOG_TOKEN }}
directory: static-site
Sera necessário subsituir algumas variáveis no template, como o <nomeusuario>
, o <email-valido>
, e o template utilizado.
Além disso, você deve ter notado que em dois momentos precisamos de um token do GitHub, como nesta linha: github_token: ${{ secrets.CD_BLOG_TOKEN }}
. É bem fácil gerar e cadastrar este token.
Provavelmente, habitalando somente a opção "public_repo" já deve funcionar, mas não testei esta opção. Se você quiser testar, pode deixar um comentário contando se funcionou.
Feito isso, clique em Generate token
e uma tela vai abrir. Salve este token, ele é importante. Agora, precisaremos criar um Secret no nosso repositório.
Navegue para as configurações do seu repositório, e na seção Secrets, cadastre um secret com o nome CD_BLOG_TOKEN
, com o token como valor.
Feito isso, basta fazer um commit na master e o workflow será disparado. Você pode acompanhar o resultado em Actions:
Se o processo ocorrer sem problemas, você vai ver todos os jobs com sucesso e o seu blog vai estar atualizado.
#Agendamento de posts
Esta abordagem também permite a publicação automática de posts agendados. A resposta está no começo do workflow. Podemos configurar um agendamento através de uma expressão cron simples, como esta:
schedule:
- cron: '0 0 5 * *'
Esta expressão descreve a frequência de deploy nos seguintes termos:
┌───────────── minuto (0 - 59) │ ┌───────────── hora (0 - 23) │ │ ┌───────────── dia do mês (1 - 31) │ │ │ ┌───────────── mês (1 - 12 ou JAN-DEZ) │ │ │ │ ┌───────────── dia da semana (0 - 6 ou DOM-SAB) │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
Assim, nossa expressão 0 0 5 * *
significa que o workflow deve ser disparado todos os dias, de todos os meses, às 5 horas da manhã.
No cabeçalho dos nossos posts, precisamos definir a data de postagem:
+++
author = "John Connor"
title = "Post agendado para o futuro longínquo"
date = "2032-07-03"
+++
Se publicarmos este post com data futura para o branch master
, por padrão o Hugo não vai incluí-lo na geração do site. O workflow será executado todos os dias às 5 horas da manhã, até o dia 3 de julho de 2032, a data agendada para esta postagem, e ela será incluída na publicação do site deste dia em diante.
#Trabalhando com branches
Por fim, esta abordagem também nos permite escrever os posts em branches individuais, sem marcar os posts com draft = true
. Eu crio branches no formato post/nome-do-branch
. Quando está pronto, é só fazer merge para o branch master
que o processo vai ser disparado.
E é isso. Espero que essas dicas sejam úteis. E se você usa alguma outra estratégia ou tem alguma dica útil, compartilhe nos comentários.