Correção — projeto 14: GitHub Actions + GitHub Pages

11 min

Este documento contém as soluções completas das 4 missões, as explicações linha a linha dos workflows fornecidos, as reparações das 3 avarias e as respostas às questões de reflexão.

Abra-o só depois de ter tentado de verdade. Ler a solução antes de ter tateado é perder 80 % da aprendizagem.


Índice


Missão 1 — Personalizar config.json

Exemplo de ficheiro válido:

json
{
  "titre": "Portfolio de <votre nom>",
  "sous_titre": "Deploiement automatique via GitHub Actions",
  "couleur_fond": "#7c3aed",
  "couleur_texte": "#f8fafc",
  "couleur_accent": "#22d3ee",
  "auteur": "<Votre Nom>",
  "version": "1.0.0"
}

Pontos a respeitar:

  1. As 7 chaves (titre, sous_titre, couleur_fond, couleur_texte, couleur_accent, auteur, version) são obrigatórias.
  2. As 3 cores devem respeitar o formato #RRGGBB (6 caracteres hexadecimais depois do #).
  3. O ficheiro deve permanecer um JSON válido: vírgula depois de cada campo exceto o último, aspas duplas em todo o lado.

Validação local:

powershell
python .\outils\build.py

Deve mostrar OK dist/index.html genere e OK dist/css/style.css genere. Qualquer erro ERREUR : interrompe o script e bloqueará depois o workflow GitHub.

Paleta sugerida para testar o ciclo na missão 2:

CorFundoTextoDestaque
Noite escura#0f172a#f1f5f9#38bdf8
Violeta vibrante#7c3aed#f8fafc#22d3ee
Vermelho vivo#dc2626#fef2f2#facc15
Verde floresta#16a34a#f0fdf4#fbbf24
Azul oceano#0891b2#ecfeff#f472b6

Missão 2 — Compreender os workflows fornecidos + provar o ciclo push → Pages

Leitura linha a linha de deployer-pages.yml

yaml
name: Deployer sur GitHub Pages          # nom qui apparait dans l'UI Actions

Este nome é puramente cosmético — ajuda a reconhecer o workflow no separador Actions do repositório.

yaml
on:
  push:
    branches: [main]
  workflow_dispatch:

Dois acionadores:

  • Automático assim que se faz push para main (o caso normal).
  • Manual através de um botão Run workflow no separador Actions (útil para reimplantar sem um novo commit).
yaml
permissions:
  contents: read
  pages: write
  id-token: write

O trio mágico do Pages. Sem estas três linhas, obtém-se Resource not accessible by integration no job deployer (é exatamente a avaria 1 da missão 3):

  • contents: read — permite a actions/checkout@v4 ler o código.
  • pages: write — autoriza actions/deploy-pages@v4 a publicar.
  • id-token: write — necessário para a autenticação OIDC entre o runner e o serviço Pages.
yaml
concurrency:
  group: pages
  cancel-in-progress: false

Se dois git push chegarem quase ao mesmo tempo, o GitHub Actions não lança duas implantações em paralelo no mesmo grupo pages — os runs executam-se um a seguir ao outro. cancel-in-progress: false = deixa-se o run em curso terminar antes de arrancar o seguinte. É crucial para o Pages: duas publicações simultâneas produzem estados incoerentes.

yaml
jobs:
  construire:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4                   # 1. recuperer le code
      - uses: actions/setup-python@v5               # 2. installer Python
        with:
          python-version: "3.12"
      - run: python outils/build.py                  # 3. generer dist/
      - uses: actions/upload-pages-artifact@v3       # 4. preparer l'artefact
        with:
          path: dist                                 #    <-- IMPORTANT : dossier a publier

Quatro etapas sequenciais. Se uma falhar, as seguintes não se executam.

yaml
  deployer:
    needs: construire                                # attend que "construire" reussisse
    environment:
      name: github-pages                             # environnement officiel Pages
      url: ${{ steps.publication.outputs.page_url }} # affiche l'URL dans l'UI
    steps:
      - uses: actions/deploy-pages@v4
        id: publication

O job deployer espera explicitamente por construire graças a needs:. No fim, a UI GitHub mostra um ligação clicável para https://<vous>.github.io/<depot>/.

Leitura linha a linha de verifier-config.yml

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

Aciona-se em PR para main e em push para qualquer ramo exceto main. O objetivo: validar antes de o código ser fundido. Note que se exclui main do push: — senão duplicaria o trabalho com deployer-pages.yml.

O resto das etapas é idêntico a deployer-pages.yml exceto que não se publica: limita-se a verificar que dist/ se gera sem erro.

Prova do ciclo push → Pages

Sequência a reproduzir duas vezes:

powershell
# --- Iteration 1 : fond violet ---
# Edite site/config.json : "couleur_fond": "#7c3aed"
git add site/config.json
git commit -m "fond violet"
git push
# Attendre ~40 s -> ouvrir https://<vous>.github.io/<depot>/ -> fond violet

# --- Iteration 2 : fond rouge ---
# Edite site/config.json : "couleur_fond": "#dc2626"
git add site/config.json
git commit -m "fond rouge"
git push
# Attendre ~40 s -> ouvrir la meme URL -> fond rouge

Verificações a incluir no relatório:

  • git log --oneline: mostra os 2 commits com os respetivos SHAs curtos.
  • Separador Actions: os 2 runs de Deployer sur GitHub Pages estão verdes.
  • Duas capturas de ecrã da página pública com as 2 cores diferentes.
  • Em cada captura, o cartão «Commit SHA» deve corresponder ao commit que acionou esse run.

Missão 3 — Reparação das 3 avarias

Avaria 1 — Permissões em falta

Diff da correção em .github/workflows/casse-1-permissions-manquantes.yml:

diff
 name: Casse 1 - Permissions manquantes

 on:
   push:
     branches: [main]
   workflow_dispatch:

+permissions:
+  contents: read
+  pages: write
+  id-token: write
+
 concurrency:
   group: pages
   cancel-in-progress: false

 jobs:
   ...

Diagnóstico escrito a colocar no relatório:

Sem o bloco permissions:, o GitHub concede ao workflow apenas contents: read (leitura do código) — não a permissão de publicar no Pages. A action actions/deploy-pages@v4 exige explicitamente pages: write e id-token: write (token OIDC). Daí o erro Resource not accessible by integration (403 Forbidden) no job deployer.

Regra de segurança: dá-se o mínimo de permissões possível, mas não menos. O princípio do menor privilégio aplicado ao CI/CD.

Avaria 2 — Acionador errado

Diff da correção:

diff
 name: Casse 2 - Declencheur errone

 on:
-  pushh:
+  push:
     branches: [main]

Diagnóstico escrito a colocar no relatório:

O GitHub Actions não assinala os acionadores desconhecidos em on: — ignora-os em silêncio. Um simples pushh (com dois h) faz com que nenhum run seja acionado: não se vê erro, não se vê nada de nada no separador Actions.

Como se descobre a avaria? Faz-se push de um commit, vai-se a Actions, constata-se que nenhum run aparece para este workflow. O único indício é a ausência.

Truque antiarmadilha: um Run workflow rápido (botão manual, disponível se workflow_dispatch: estiver presente) verifica que o próprio workflow é sintaticamente válido. O workflow_dispatch: funciona independentemente de push:.

Avaria 3 — Caminho de artefacto errado

Diff da correção:

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

Diagnóstico escrito a colocar no relatório:

outils/build.py escreve sistematicamente em dist/ (constante DIST = RACINE / "dist" no código Python). O workflow pedia path: public — uma pasta que não existe. Daí:

Error: Path does not exist: ./public

actions/upload-pages-artifact@v3 verifica a existência da pasta antes de criar o artefacto e falha imediatamente se o caminho estiver errado. A boa prática: fazer sempre apontar o workflow para a pasta exata que o script de build produz.


Missão 4 — Bónus

4.a — Badge de estado em README.md

No README.md do seu repositório GitHub (na raiz, não na pasta projet14-github-actions-pages-tp/ do curso), acrescente:

markdown
# Mon site portfolio

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

Site publie : https://<vous>.github.io/<depot>/

O badge fica verde se o último run passou, vermelho caso contrário. Atualiza-se sozinho.

4.b — PR de teste com config.json inválido

powershell
git checkout -b test-config-cassee
# Editer site/config.json et remplacer "couleur_fond": "#0f172a" par "couleur_fond": "rouge"
git add site/config.json
git commit -m "test : couleur invalide"
git push -u origin test-config-cassee
# Puis sur GitHub : ouvrir une PR de test-config-cassee vers main

O workflow Verifier la configuration vai acionar-se, build.py vai protestar:

ERREUR : couleur_fond doit etre au format #RRGGBB (recu : 'rouge')

A PR mostra um quadrado vermelho e o botão Merge pull request fica bloqueado se configurou a proteção de ramo (Settings → Branches → Add rule → Require status checks to pass).

4.c — Workflow com secret

.github/workflows/notifier.yml:

yaml
name: Notifier une URL

on:
  workflow_dispatch:

jobs:
  notifier:
    runs-on: ubuntu-latest
    steps:
      - name: Ping du webhook
        env:
          URL: ${{ secrets.URL_WEBHOOK }}
        run: |
          curl -X POST "$URL" \
            -H "Content-Type: application/json" \
            -d '{"texte":"Un utilisateur a declenche notifier.yml"}'

O secret URL_WEBHOOK define-se em Settings → Secrets and variables → Actions → New repository secret. Nunca aparecerá nos logs: o GitHub mascara automaticamente qualquer valor que corresponda a um secret.


Respostas às questões de reflexão

1. on: push sem branches: — quando é acionado?

É acionado em cada push em todos os ramos. Raramente é desejável: um git push num ramo de feature em desenvolvimento lança uma implantação Pages em produção. Limite sempre com branches: [main] ou uma lista explícita.

2. Por que id-token: write é necessário para o Pages? Para que serve o token OIDC?

actions/deploy-pages@v4 usa OIDC (OpenID Connect) para provar ao serviço Pages que o pedido vem mesmo de um run GitHub Actions autorizado, sem usar palavra-passe nem PAT (Personal Access Token). O token OIDC é efémero (duração de vida limitada ao run) e assinado pelo GitHub — mais seguro do que um secret estático. id-token: write autoriza o runner a gerar este token para este workflow.

3. Dois pushes simultâneos em main — o que faz concurrency?

Sem concurrency, o GitHub lança dois workflows em paralelo. O primeiro termina o upload do artefacto, o segundo arranca o seu próprio upload, e o Pages pode receber as duas publicações fora de ordem → a ordem final deixa de estar garantida. Com concurrency: { group: pages, cancel-in-progress: false }, os dois runs são serializados: o segundo espera que o primeiro termine. cancel-in-progress: true teria outro efeito — cancelar o run em curso assim que um novo arranca, útil para um rebuild ultrarrápido mas perigoso para o Pages.

4. Um secret pode aparecer nos logs?

O GitHub mascara automaticamente qualquer valor registado como secret: aparece sob a forma de *** nos logs. Mas se transformar o secret (por exemplo echo "$SECRET" | base64), o mascaramento já não se aplica ao valor transformado. Regra: nunca transformar um secret num run:, usá-lo tal qual via env: e passá-lo diretamente à ferramenta (aqui curl).

5. Por que manter os dois workflows deployer-pages.yml e verifier-config.yml?

  • deployer-pages.yml publica em main: é o workflow de entrega. Só corre depois de um merge.
  • verifier-config.yml valida nos ramos de feature e nas PR: é o workflow de prevenção. Impede que um config.json partido chegue a main.

Os dois formam uma rede de duas camadas: a verificação bloqueia a montante, a implantação entrega a jusante. É o padrão CI (Continuous Integration) + CD (Continuous Deployment).

6. O que aconteceria se build.py criasse um ficheiro historique.log?

Nada de duradouro. O runner é uma VM efémera destruída no fim do run. O ficheiro desapareceria com a VM. Para conservar um histórico entre runs, seria preciso commitá-lo no repositório (mau, polui o repo), guardá-lo num artefacto com actions/upload-artifact (duração de vida 90 dias no máximo), ou enviá-lo para um serviço externo (S3, Postgres, etc.).


Erros clássicos a evitar

SintomaCausa provávelSolução
Resource not accessible by integrationBloco permissions: ausente ou incompletoAcrescentar pages: write + id-token: write
Nenhum run é acionadoErro de digitação em on: (pushh:, pull_requests:)Verificar a ortografia das chaves YAML
Path does not exist: ./xxxCaminho em upload-pages-artifact.path: não corresponde à pasta de saída de build.pyVerificar path: = dist
A página publicada mostra o conteúdo antigoCache do navegadorCtrl+Shift+R para um recarregamento forçado
A primeira implantação demora > 5 minPropagação DNS do GitHub PagesNormal na primeira vez, ~40 s depois
Workflow vermelho na etapa python outils/build.pyconfig.json inválidoLançar python .\outils\build.py em local para ver o erro exato
O workflow corre mas o URL devolve 404Source Pages ainda em modo «Deploy from a branch»Passar para GitHub Actions em Settings → Pages
Dois runs Pages ao mesmo tempo produzem conteúdo incoerenteconcurrency: ausenteAcrescentar o bloco concurrency: { group: pages }

Como o professor avalia o seu trabalho

O professor abre três separadores:

  1. O seu repositório GitHub: estrutura dos ficheiros, conteúdo de config.json, presença de .github/workflows/ com pelo menos os 3 workflows corrigidos (os originais deployer-pages.yml e verifier-config.yml + as 3 avarias reparadas).
  2. O seu separador Actions: pelo menos 2 runs verdes de Deployer sur GitHub Pages (missão 2), mais os runs da missão 3 (antes/depois da reparação).
  3. O seu sítio publicado: https://<vous>.github.io/<votre-depot>/ — deve mostrar-se com as suas cores personalizadas e um SHA recente.

Depois lê o seu RAPPORT.md:

  • Conta as capturas de ecrã (2 para a missão 2, 6 para a missão 3: antes/depois × 3 avarias).
  • Verifica que os diagnósticos das avarias estão escritos com as suas palavras, não copiados deste documento.
  • Nota a qualidade das respostas às 6 questões de reflexão.

Um relatório limpo + um repositório bem estruturado = nota máxima fácil. A dificuldade não é técnica, está na rigor da demonstração.


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