Missão GitHub Actions: publicar um site no GitHub Pages, em um push

14 min

Projeto 14 — CI/CD com GitHub Actions · Nível principiante → intermédio · Duração estimada: 1 h 30 a 2 h

Recebe um mini-sítio estático já ligado a dois workflows GitHub Actions funcionais. Clona, faz push, o sítio fica online. Altera uma cor em config.json, volta a fazer push, a página pública atualiza-se sozinha. Termina por reparar três workflows defeituosos que ilustram os erros clássicos do CI/CD.


Índice


O contexto

Acaba de ser contratado como júnior numa agência web. O primeiro trabalho: um mini-sítio «portfólio» a alojar gratuitamente. A consignação do chefe:

«Quero poder mudar a cor do sítio ao alterar um único ficheiro, fazer um git push e que se atualize sozinho. Sem FTP, sem servidor para administrar.»

O sénior da equipa já escreveu os dois workflows GitHub Actions e o script de build. O seu trabalho:

  1. Pegar no projeto entregue (git clone + push + ativação do Pages).
  2. Personalizar config.json com as suas informações e as suas cores.
  3. Provar que o ciclo push → Pages funciona ao mudar a cor pelo menos duas vezes.
  4. Reparar três workflows defeituosos que não partem do zero mas ilustram os 3 erros mais frequentes do CI/CD.

Este projeto não é um TP em que escreve YAML a partir do zero. É um TP em que toma conta de uma cadeia CI/CD já no sítio, como numa equipa real: não reinventa os workflows, compreende-os, personaliza-os, diagnostica-os.


Conceitos essenciais antes de começar

Este documento é autónomo. Nenhum curso externo a abrir para o terminar.

1. O que é o GitHub Actions?

O GitHub Actions é um motor de execução integrado no GitHub. Em cada evento (push, pull_request, temporizador, botão manual), lança workflows descritos em YAML, em runners (VMs Ubuntu / Windows / macOS gratuitas para repositórios públicos).

2. Anatomia de um workflow YAML

yaml
name: Mon premier workflow            # affiche dans l'UI

on:                                   # QUAND s'executer
  push:
    branches: [main]

jobs:                                 # QUOI faire (>= 1 job)
  construire:                         # nom libre du job
    runs-on: ubuntu-latest            # OU s'executer (image de la VM)
    steps:                            # les etapes du job
      - uses: actions/checkout@v4     # etape "action reutilisable"
      - run: echo "Bonjour"           # etape "commande shell"

Dois tipos de etapas:

  • uses: chama uma action publicada (actions/checkout@v4, actions/setup-python@v5, etc.).
  • run: executa um comando shell no runner.

3. Os 4 acionadores que precisa de conhecer

on:Acionado quando…
push: { branches: [main] }Faz-se push de um commit para main
pull_request: { branches: [main] }Alguém abre / atualiza uma PR para main
schedule: [{ cron: "0 6 * * *" }]Todos os dias às 06:00 UTC
workflow_dispatch:Botão manual no separador Actions

O mesmo workflow pode ter vários acionadores ao mesmo tempo — é o caso de deployer-pages.yml (push + manual).

4. O que é o GitHub Pages?

O GitHub Pages é um alojamento estático oferecido com cada repositório público. Publica-se HTML/CSS/JS por um dos métodos seguintes:

  • A partir de um ramo gh-pages (método antigo).
  • A partir de uma pasta /docs de main (outro método).
  • A partir de um workflow GitHub Actions (método moderno, o deste projeto).

O URL público é https://<utilisateur>.github.io/<nom-du-depot>/ — acessível a partir de qualquer navegador.

5. O trio de actions oficiais para o Pages

Para publicar a partir de Actions, deployer-pages.yml encadeia três actions oficiais:

Restrição capital: o job que publica precisa de permissões particulares, senão obtém-se Resource not accessible by integration. O workflow fornecido já as declara:

yaml
permissions:
  contents: read
  pages: write
  id-token: write

É também a avaria 1 da missão 3 — retenha-a.

6. Variáveis automáticas fornecidas pelo runner

Em cada execução, o GitHub Actions expõe variáveis de ambiente úteis:

VariávelConteúdo
GITHUB_SHAO SHA do commit que acionou o run
GITHUB_REF_NAMEO nome do ramo (main, feature-x, etc.)
GITHUB_RUN_NUMBERUm contador que incrementa em cada run

Neste projeto, outils/build.py lê estas três variáveis para as mostrar na página publicada. Prova visual de que a implantação vem mesmo de Actions e não de um python build.py em local.


A arquitetura alvo

Ciclo final esperado — deve reproduzi-lo pelo menos 2 vezes:

  1. Abrir site/config.json no VS Code.
  2. Mudar couleur_fond (por exemplo #0f172a#7c3aed).
  3. git add site/config.json && git commit -m "changement de fond" && git push.
  4. Ir ao separador Actions do repositório → ver o workflow a correr em direto (~40 s).
  5. Quando estiver tudo verde, abrir https://<vous>.github.io/<depot>/o fundo está violeta.

Organização dos ficheiros

projet14-github-actions-pages-tp/
├── 00-ENONCE.md                             <- este documento
├── 02-CORRECTION.md                         <- solucoes detalhadas (a ler DEPOIS de tentar)
├── README.md

├── site/                                    <- O SITIO (fonte)
│   ├── config.json                          <- VOCE ALTERA : cores, titulo, autor
│   └── src/
│       ├── index.html.template              <- modelo HTML (marcadores {{...}})
│       └── css/
│           └── style.css.template           <- modelo CSS (marcadores {{...}})

├── outils/
│   └── build.py                             <- FORNECIDO — a nao modificar

├── .github/                                 <- WORKFLOWS JA FUNCIONAIS
│   └── workflows/
│       ├── deployer-pages.yml               <- build + publica no Pages
│       └── verifier-config.yml              <- valida config.json em PR

├── casses/                                  <- 3 workflows defeituosos (Missao 3)
│   ├── casse-1-permissions-manquantes.yml
│   ├── casse-2-declencheur-errone.yml
│   └── casse-3-chemin-artefact.yml

└── .gitignore                               <- ignore dist/

Ponto importante: os dois workflows já estão em .github/workflows/. Não tem nada a escrever para a parte de implantação — apenas a compreender o que se passa e a personalizar config.json.


As regras do jogo

  1. Não modifica outils/build.py — é o contrato entre config.json e o HTML/CSS final.
  2. Não faz push da pasta dist/: figura no .gitignore e é reconstruída em cada run.
  3. Qualquer dado sensível (palavras-passe, chaves API) vai para Settings → Secrets and variables → Actions, nunca para um ficheiro YAML.
  4. Cada mudança de cor deve passar por um commit — sem alteração manual no sítio publicado.
  5. O repositório deve ser público (GitHub Pages em repositórios privados exige um plano pago).

Preparação

Pré-requisitos

  1. Git instalado (git --version).
  2. Python 3.10+ instalado (python --version — para testar build.py em local).
  3. Uma conta GitHub.
  4. Esta pasta projet14-github-actions-pages-tp/ no seu disco.

Etapa 0 — Criar o seu repositório GitHub

  1. No GitHub, clique em New repository.
  2. Nome sugerido: projet14-actions-pages.
  3. Visibilidade: Public (obrigatório para GitHub Pages gratuito).
  4. Sem README nem .gitignore (já fornecidos).

Etapa 1 — Testar o build em local

A partir da raiz do projeto:

powershell
python .\outils\build.py

Deve ver:

OK  dist/index.html      genere
OK  dist/css/style.css   genere
--- Substitutions appliquees ---
  {{TITRE}} -> Mon premier site pilote par GitHub Actions
  {{COULEUR_FOND}} -> #0f172a
  ...

Abra dist/index.html num navegador: vê o sítio com o fundo escuro por omissão. Se funcionar em local, funcionará no Actions.

Etapa 2 — Inicializar o repositório local e fazer push

powershell
git init
git branch -M main
git add .
git commit -m "point de depart projet14"
git remote add origin https://github.com/<votre-utilisateur>/projet14-actions-pages.git
git push -u origin main

Etapa 3 — Ativar o GitHub Pages em modo Actions

  1. No GitHub, abra o repositório → SettingsPages.
  2. Em Source, escolha GitHub Actions (e não Deploy from a branch).
  3. Guarde.

Etapa 4 — Observar a primeira implantação

Separador Actions do seu repositório → aparece um run intitulado Deployer sur GitHub Pages. Espere que fique verde (~40 segundos após o push).

No fim do job deployer, uma mensagem anuncia:

Your site is live at https://<vous>.github.io/projet14-actions-pages/

Clique → vê o seu sítio publicado.


As missões

Missão 1 — Personalizar config.json (20 pontos)

Abra site/config.json e altere pelo menos:

  • titre — coloque o seu nome ou o de um projeto fictício.
  • auteur — o seu nome.
  • couleur_fond — no formato #RRGGBB (por exemplo #7c3aed, #dc2626, #0891b2).

Verifique com python .\outils\build.py que o build continua a passar. Um formato errado (rouge, #ff, RGB(255,0,0)) é detetado por build.py e bloqueará o workflow.

Restrição: não parta o JSON. Uma vírgula a mais, uma chaveta em falta → workflow vermelho no GitHub.


Missão 2 — Provar o ciclo push → Pages (30 pontos)

Isto é o coração do projeto. Deve demonstrar que o ciclo CI/CD funciona, ao acioná-lo pelo menos duas vezes:

  1. Altere site/config.json (mude couleur_fond por exemplo para #dc2626 — vermelho).
  2. git add site/config.json && git commit -m "fond en rouge" && git push.
  3. Espere que o workflow Deployer sur GitHub Pages fique verde no separador Actions (~40 s).
  4. Abra https://<vous>.github.io/<votre-depot>/o fundo está vermelho.
  5. Repita a operação com outra cor (verde #16a34a, azul #0891b2, violeta #7c3aed, à sua escolha).

Prova a fornecer no relatório:

  • Duas capturas de ecrã da página pública com duas cores diferentes.
  • Os dois SHA de commit correspondentes (saída de git log --oneline).
  • Uma captura do separador Actions a mostrar os dois runs verdes.

Ponto-chave: na página pública, um cartão mostra Commit SHA : <7 caractères>. Este SHA corresponde ao último commit em main. É a prova visual de que a página vem mesmo de Actions.


Missão 3 — Inquérito: reparar 3 workflows defeituosos (30 pontos)

A pasta casses/ contém três workflows defeituosos, cada um a ilustrar um erro clássico. Para cada um:

  1. Copie o ficheiro para .github/workflows/ (ao lado dos workflows oficiais).
  2. Faça push — observe que o workflow não se comporta como previsto (falha ou ausência de acionamento).
  3. Diagnostique ao ler os logs (separador Actions → run → etapa).
  4. Repare na cópia em .github/workflows/ (não em casses/ — o original permanece defeituoso).
  5. Faça push de novo — o workflow deve agora ficar verde.
FicheiroSintoma esperado
casse-1-permissions-manquantes.ymlO job deployer falha com Resource not accessible by integration
casse-2-declencheur-errone.ymlNenhum run é acionado: o workflow é ignorado em silêncio
casse-3-chemin-artefact.ymlO job construire falha com Error: Path does not exist: ./public

Truque importante: pode lançar cada workflow manualmente via Run workflow no separador Actions, sem ter de fazer um commit verdadeiro em cada teste. Os workflows defeituosos têm todos um workflow_dispatch: ou pode acrescentá-lo temporariamente.

Prova a fornecer para cada avaria: captura do run vermelho antes, diff da correção, captura do run verde depois.


Missão 4 — Bónus (+10 pontos)

À escolha, um só basta:

  • a) Acrescente um badge de estado num README.md na raiz do seu repositório GitHub (não o do TP):

    markdown
    ![Deploy](https://github.com/<vous>/<depot>/actions/workflows/deployer-pages.yml/badge.svg)

    Mostra passing (verde) ou failing (vermelho) em direto.

  • b) Crie uma PR de teste com um config.json voluntariamente inválido (por exemplo "couleur_fond": "rouge"). O workflow Verifier la configuration deve falhar e bloquear o merge. Forneça a captura do bloqueio.

  • c) Crie um terceiro workflow manual (workflow_dispatch:) que envia uma notificação curl a um webhook (URL fornecido via Settings → Secrets, nunca em claro no YAML).


Entregáveis

Um RAPPORT.md na raiz do seu repositório, contendo:

  1. O URL público do seu sítio (https://<vous>.github.io/<depot>/).
  2. Para a Missão 2: duas capturas de ecrã da página com duas cores diferentes + os dois SHA + captura do separador Actions com os runs verdes.
  3. Para a Missão 3: para cada avaria, captura do run vermelho + diagnóstico escrito + diff da correção + captura do run verde.
  4. As suas respostas às questões de reflexão.
  5. (Bónus) A prova da missão 4 se a fez.

Questões de reflexão

  1. Um workflow que contém on: push sem precisar branches: — quando é acionado? Isto é um problema?
  2. Por que id-token: write é necessário para publicar no GitHub Pages? Para que serve o token OIDC?
  3. O que acontece se dois colegas fizerem git push em main ao mesmo tempo? O bloco concurrency muda o quê no comportamento?
  4. Acrescenta um secret API_KEY em Settings → Secrets. O código do workflow pode lê-lo via ${{ secrets.API_KEY }}. Pode ser impresso nos logs? Por que o GitHub mascara certos valores?
  5. Tem dois workflows: deployer-pages.yml e verifier-config.yml. O segundo é inútil já que o primeiro faz o mesmo build? Justifique por que são complementares.
  6. Um runner GitHub Actions é uma VM efémera: nada persiste entre dois runs. Que consequência isto tem se o seu build.py criasse um ficheiro historique.log? Onde seria preciso guardá-lo?

Cotação

ElementoPontos
Missão 1 — config.json personalizado e build local OK20
Missão 2 — Prova do ciclo push → Pages (2 cores + capturas + SHAs)30
Missão 3 — 3 avarias diagnosticadas e reparadas30
Qualidade do relatório (estrutura, capturas, explicações)20
Bónus — Missão 4+10
Total100 (+10)

Penalizações:

  • −10 por workflow situado fora de .github/workflows/ (logo ignorado pelo GitHub).
  • −15 por qualquer secret em claro num ficheiro YAML commitado.
  • −5 por pasta dist/ commitada (deve permanecer no .gitignore).
  • −10 por qualquer alteração de outils/build.py.

Caixa de ferramentas GitHub Actions

yaml
# Squelette de reference d'un workflow
name: Description humaine
on:                                       # QUAND
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:                      # bouton manuel

permissions:                              # SEULEMENT ce qui est necessaire
  contents: read
  pages: write

concurrency:                              # ne pas empiler les runs
  group: pages
  cancel-in-progress: false

jobs:
  mon-job:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - name: Une commande
        run: python outils/build.py

Os 5 gestos que salvam no diagnóstico:

  1. Separador Actions → run vermelho → step falhado: a mensagem de erro está no topo, a vermelho. Lê-la à letra evita 90 % dos erros.
  2. Re-run failed jobs: relança apenas as etapas em falha, mais rápido.
  3. workflow_dispatch: acrescente-o aos seus workflows para os poder relançar manualmente sem um commit vazio.
  4. echo "::debug::mon message": imprime uma mensagem de debug nos logs.
  5. Ler os logs até ao fim: por vezes o erro significativo está no meio, não no fim.


ANEXO A — O sítio estático

Ficheiro: site/config.json

É o único ficheiro que deve modificar em tempo normal.

json
{
  "titre": "Mon premier site pilote par GitHub Actions",
  "sous_titre": "Change une couleur, fais un push, et regarde GitHub Pages se mettre a jour tout seul.",
  "couleur_fond": "#0f172a",
  "couleur_texte": "#f1f5f9",
  "couleur_accent": "#38bdf8",
  "auteur": "Etudiant du cours AOA-DEVOPS-101",
  "version": "1.0.0"
}

Restrições do ficheiro:

  • As 7 chaves são obrigatórias.
  • As 3 cores (couleur_fond, couleur_texte, couleur_accent) devem respeitar o formato #RRGGBB — senão build.py rejeita.
  • JSON válido: aspas duplas, vírgula depois de cada campo exceto o último.

Ficheiro: site/src/index.html.template

Modelo HTML em que cada {{MARQUEUR}} é substituído por build.py. A página mostrará:

  • O título e o subtítulo (a partir de config.json).
  • O autor e a versão (a partir de config.json).
  • O SHA do commit, o número do run e o ramo (a partir das variáveis GITHUB_* do runner).
  • A data de build.

O ficheiro completo é fornecido em site/src/index.html.template.

Ficheiro: site/src/css/style.css.template

Modelo CSS que usa {{COULEUR_FOND}}, {{COULEUR_TEXTE}} e {{COULEUR_ACCENT}} num bloco :root.

Excerto:

css
:root {
  --fond: {{COULEUR_FOND}};
  --texte: {{COULEUR_TEXTE}};
  --accent: {{COULEUR_ACCENT}};
}

body {
  background: var(--fond);
  color: var(--texte);
}


ANEXO B — Os workflows fornecidos

Ficheiro: .github/workflows/deployer-pages.yml

yaml
name: Deployer sur GitHub Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: false

jobs:
  construire:
    name: Construire le site
    runs-on: ubuntu-latest
    steps:
      - name: Recuperer le code
        uses: actions/checkout@v4

      - name: Installer Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Generer dist/ a partir de config.json
        run: python outils/build.py

      - name: Preparer le dossier a publier
        uses: actions/upload-pages-artifact@v3
        with:
          path: dist

  deployer:
    name: Publier sur GitHub Pages
    needs: construire
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.publication.outputs.page_url }}
    steps:
      - name: Publier l'artefact
        id: publication
        uses: actions/deploy-pages@v4

Leitura linha a linha: ver 02-CORRECTION.md → Missão 2.

Ficheiro: .github/workflows/verifier-config.yml

yaml
name: Verifier la configuration

on:
  pull_request:
    branches: [main]
  push:
    branches-ignore: [main]

jobs:
  linter:
    name: Valider config.json
    runs-on: ubuntu-latest
    steps:
      - name: Recuperer le code
        uses: actions/checkout@v4

      - name: Installer Python 3.12
        uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Construire en dry-run
        run: python outils/build.py

      - name: Verifier que dist/ est bien genere
        run: |
          test -f dist/index.html
          test -f dist/css/style.css
          echo "OK - site construit sans erreur"


ANEXO C — O script de build

outils/build.py faz quatro coisas:

  1. site/config.json.
  2. Valida que as 7 chaves esperadas estão presentes.
  3. Valida que as 3 cores estão no formato #RRGGBB (regex).
  4. Substitui todos os {{MARQUEUR}} nos dois modelos e escreve o resultado em dist/.

No GitHub Actions, as variáveis GITHUB_SHA, GITHUB_REF_NAME, GITHUB_RUN_NUMBER estão automaticamente disponíveis. Em local, não existem → o script mostra local-* no lugar. É assim que se distingue uma página mesmo implantada por Actions de uma página construída em local.



ANEXO D — As três avarias a reparar

Ficheiro: casses/casse-1-permissions-manquantes.yml

O workflow corre mas o job deployer falha com:

Error: Resource not accessible by integration

Pergunta a colocar a si mesmo: que secção inteira falta, no topo do ficheiro, ao lado de on:?

Ficheiro: casses/casse-2-declencheur-errone.yml

Um push em main nunca aciona este workflow. Nenhum run aparece. Nenhum erro.

Pergunta a colocar a si mesmo: leia a chave sob on:. O GitHub espera push, mas o que está escrito?

Ficheiro: casses/casse-3-chemin-artefact.yml

build.py tem sucesso e mostra OK dist/index.html genere. Mas upload-pages-artifact falha com:

Error: Path does not exist: ./public

Pergunta a colocar a si mesmo: onde build.py escreve o sítio (olhe para o código Python)? Que path: é preciso então pôr no workflow?


Curso criado pelo Dr. Haythem REHOUMA — Desenvolvimento e implantação de soluções de dados