Misión GitHub Actions: publicar un sitio en GitHub Pages, en un push

14 min

Proyecto 14 — CI/CD con GitHub Actions · Nivel principiante → intermedio · Duración estimada: 1 h 30 a 2 h

Recibes un mini-sitio estático ya enlazado a dos workflows GitHub Actions funcionales. Clonas, empujas, tu sitio está en línea. Cambias un color en config.json, vuelves a empujar, la página pública se actualiza sola. Terminas reparando tres workflows defectuosos que ilustran los errores clásicos del CI/CD.


Tabla de contenidos


El contexto

Acabas de ser contratado como junior en una agencia web. Tu primer trabajo: un mini-sitio «portfolio» a alojar gratis. La consigna del jefe:

«Quiero poder cambiar el color del sitio modificando un solo archivo, hacer un git push y que se actualice solo. Ningún FTP, ningún servidor que administrar.»

El senior del equipo ya ha escrito los dos workflows GitHub Actions y el script de build. Tu trabajo:

  1. Tomar en mano el proyecto entregado (git clone + push + activación de Pages).
  2. Personalizar config.json con tus datos y tus colores.
  3. Probar que el ciclo push → Pages funciona cambiando el color al menos dos veces.
  4. Reparar tres workflows defectuosos que no parten de cero pero ilustran los 3 errores más habituales del CI/CD.

Este proyecto no es un TP donde escribes YAML desde cero. Es un TP donde tomas en mano una cadena CI/CD ya en marcha, como en un equipo real: no reinventas los workflows, los entiendes, personalizas, diagnosticas.


Conceptos esenciales antes de empezar

Este documento es autosuficiente. No hay que abrir ningún curso externo para terminarlo.

1. ¿Qué es GitHub Actions?

GitHub Actions es un motor de ejecución integrado en GitHub. En cada evento (push, pull_request, temporizador, botón manual), lanza workflows descritos en YAML, en runners (VM Ubuntu / Windows / macOS gratis para los repositorios públicos).

2. Anatomía de un workflow YAML

yaml
name: Mi primer workflow            # se muestra en la UI

on:                                   # CUÁNDO ejecutarse
  push:
    branches: [main]

jobs:                                 # QUÉ hacer (>= 1 job)
  construire:                         # nombre libre del job
    runs-on: ubuntu-latest            # DÓNDE ejecutarse (imagen de la VM)
    steps:                            # los pasos del job
      - uses: actions/checkout@v4     # paso "acción reutilizable"
      - run: echo "Hola"           # paso "comando shell"

Dos tipos de pasos:

  • uses: llama a una action publicada (actions/checkout@v4, actions/setup-python@v5, etc.).
  • run: ejecuta un comando shell en el runner.

3. Los 4 disparadores que hay que conocer

on:Se dispara cuando…
push: { branches: [main] }Se empuja un commit a main
pull_request: { branches: [main] }Alguien abre / actualiza una PR hacia main
schedule: [{ cron: "0 6 * * *" }]Todos los días a las 06:00 UTC
workflow_dispatch:Botón manual en la pestaña Actions

Un mismo workflow puede tener varios disparadores a la vez — es el caso de deployer-pages.yml (push + manual).

4. ¿Qué es GitHub Pages?

GitHub Pages es un alojamiento estático incluido con cada repositorio público. Se publica HTML/CSS/JS por uno de estos métodos:

  • Desde una rama gh-pages (método antiguo).
  • Desde una carpeta /docs de main (otro método).
  • Desde un workflow GitHub Actions (método moderno, el de este proyecto).

La URL pública es https://<utilisateur>.github.io/<nom-du-depot>/ — accesible desde cualquier navegador.

5. El trío de actions oficiales para Pages

Para publicar desde Actions, deployer-pages.yml encadena tres actions oficiales:

Restricción capital: el job que publica necesita permissions particulares, si no se obtiene Resource not accessible by integration. El workflow suministrado ya las declara:

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

Es también la avería 1 de la misión 3 — reténla.

6. Variables automáticas que da el runner

En cada ejecución, GitHub Actions expone variables de entorno útiles:

VariableContenido
GITHUB_SHAEl SHA del commit que disparó el run
GITHUB_REF_NAMEEl nombre de la rama (main, feature-x, etc.)
GITHUB_RUN_NUMBERUn contador que se incrementa en cada run

En este proyecto, outils/build.py lee esas tres variables para mostrarlas en la página publicada. Prueba visual de que el despliegue viene de Actions y no de un python build.py en local.


La arquitectura objetivo

Bucle final esperado — debes reproducirlo al menos 2 veces:

  1. Abrir site/config.json en VS Code.
  2. Cambiar couleur_fond (por ejemplo #0f172a#7c3aed).
  3. git add site/config.json && git commit -m "changement de fond" && git push.
  4. Ir a la pestaña Actions del repositorio → ver el workflow correr en directo (~40 s).
  5. Una vez todo verde, abrir https://<tu>.github.io/<repo>/el fondo es violeta.

Disposición de los archivos

projet14-github-actions-pages-tp/
├── 00-ENONCE.md                             <- este documento
├── 02-CORRECTION.md                         <- soluciones detalladas (a leer DESPUÉS del intento)
├── README.md

├── site/                                    <- EL SITIO (fuente)
│   ├── config.json                          <- TÚ MODIFICAS: colores, título, autor
│   └── src/
│       ├── index.html.template              <- plantilla HTML (marcadores {{...}})
│       └── css/
│           └── style.css.template           <- plantilla CSS (marcadores {{...}})

├── outils/
│   └── build.py                             <- SUMINISTRADO — no modificar

├── .github/                                 <- WORKFLOWS YA FUNCIONALES
│   └── workflows/
│       ├── deployer-pages.yml               <- build + publica en Pages
│       └── verifier-config.yml              <- valida config.json en PR

├── casses/                                  <- 3 workflows defectuosos (Misión 3)
│   ├── casse-1-permissions-manquantes.yml
│   ├── casse-2-declencheur-errone.yml
│   └── casse-3-chemin-artefact.yml

└── .gitignore                               <- ignora dist/

Punto importante: los dos workflows ya están en .github/workflows/. No tienes que escribir nada para la parte de despliegue — solo entender lo que ocurre y personalizar config.json.


Las reglas del juego

  1. No modificas outils/build.py — es el contrato entre config.json y el HTML/CSS final.
  2. No empujas la carpeta dist/: figura en .gitignore y se reconstruye en cada run.
  3. Todo dato sensible (contraseñas, claves API) va a Settings → Secrets and variables → Actions, nunca a un archivo YAML.
  4. Cada cambio de color debe pasar por un commit — ninguna modificación manual en el sitio publicado.
  5. El repositorio debe ser público (GitHub Pages en repositorios privados pide un plan de pago).

Preparación

Requisitos previos

  1. Git instalado (git --version).
  2. Python 3.10+ instalado (python --version — para probar build.py en local).
  3. Una cuenta GitHub.
  4. Esta carpeta projet14-github-actions-pages-tp/ en tu disco.

Paso 0 — Crear tu repositorio GitHub

  1. En GitHub, pulsa New repository.
  2. Nombre sugerido: projet14-actions-pages.
  3. Visibilidad: Public (obligatorio para GitHub Pages gratis).
  4. Sin README ni .gitignore (ya se suministran).

Paso 1 — Probar el build en local

Desde la raíz del proyecto:

powershell
python .\outils\build.py

Debes 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
  ...

Abre dist/index.html en un navegador: ves el sitio con el fondo oscuro por defecto. Si funciona en local, funcionará en Actions.

Paso 2 — Inicializar el repositorio local y empujar

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

Paso 3 — Activar GitHub Pages en modo Actions

  1. En GitHub, abre el repositorio → SettingsPages.
  2. En Source, elige GitHub Actions (y no Deploy from a branch).
  3. Guarda.

Paso 4 — Observar el primer despliegue

Pestaña Actions de tu repositorio → aparece un run titulado Deployer sur GitHub Pages. Espera a que pase a verde (~40 segundos después del push).

Al final del job deployer, un mensaje anuncia:

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

Pulsa → ves tu sitio publicado.


Las misiones

Misión 1 — Personalizar config.json (20 puntos)

Abre site/config.json y modifica al menos:

  • titre — pon tu nombre o el de un proyecto ficticio.
  • auteur — tu nombre.
  • couleur_fond — en formato #RRGGBB (por ejemplo #7c3aed, #dc2626, #0891b2).

Comprueba con python .\outils\build.py que el build sigue pasando. Un formato malo (rouge, #ff, RGB(255,0,0)) lo detecta build.py y bloqueará el workflow.

Restricción: no rompas el JSON. Una coma de más, una llave de menos → workflow rojo en GitHub.


Misión 2 — Probar el bucle push → Pages (30 puntos)

Este es el corazón del proyecto. Debes demostrar que el ciclo CI/CD funciona, disparándolo al menos dos veces:

  1. Modifica site/config.json (cambia couleur_fond por ejemplo a #dc2626 — rojo).
  2. git add site/config.json && git commit -m "fond en rouge" && git push.
  3. Espera a que el workflow Deployer sur GitHub Pages pase a verde en la pestaña Actions (~40 s).
  4. Abre https://<tu>.github.io/<repo>/el fondo es rojo.
  5. Repite la operación con otro color (verde #16a34a, azul #0891b2, violeta #7c3aed, a tu elección).

Prueba a aportar en el informe:

  • Dos capturas de pantalla de la página pública con dos colores distintos.
  • Los dos SHA de commit correspondientes (salida de git log --oneline).
  • Una captura de la pestaña Actions que muestre los dos runs verdes.

Punto clave: en la página pública, una baldosa muestra Commit SHA : <7 caractères>. Ese SHA corresponde al último commit en main. Es la prueba visual de que la página viene de Actions.


Misión 3 — Investigación: reparar 3 workflows defectuosos (30 puntos)

La carpeta casses/ contiene tres workflows defectuosos, cada uno ilustrando un error clásico. Para cada uno:

  1. Copia el archivo a .github/workflows/ (junto a los workflows oficiales).
  2. Empuja — observa que el workflow no se comporta como se espera (fallo o ausencia de disparo).
  3. Diagnostica leyendo los logs (pestaña Actions → run → paso).
  4. Repara en la copia de .github/workflows/ (no en casses/ — el original se queda defectuoso).
  5. Empuja de nuevo — el workflow debe pasar ahora a verde.
ArchivoSíntoma esperado
casse-1-permissions-manquantes.ymlEl job deployer falla con Resource not accessible by integration
casse-2-declencheur-errone.ymlNingún run se dispara: el workflow se ignora en silencio
casse-3-chemin-artefact.ymlEl job construire falla con Error: Path does not exist: ./public

Truco importante: puedes lanzar cada workflow a mano con Run workflow en la pestaña Actions, sin hacer un commit de verdad en cada prueba. Los workflows defectuosos tienen todos un workflow_dispatch: o puedes añadirlo temporalmente.

Prueba a aportar para cada avería: captura del run rojo antes, diff del correctivo, captura del run verde después.


Misión 4 — Bonus (+10 puntos)

A elegir, uno solo basta:

  • a) Añade un badge de estado en un README.md en la raíz de tu repositorio GitHub (no el del TP):

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

    Muestra passing (verde) o failing (rojo) en directo.

  • b) Crea una PR de prueba con un config.json inválido a propósito (por ejemplo "couleur_fond": "rouge"). El workflow Verifier la configuration debe fallar y bloquear el merge. Aporta la captura del bloqueo.

  • c) Crea un tercer workflow manual (workflow_dispatch:) que envíe una notificación curl a un webhook (URL dada vía Settings → Secrets, nunca en claro en el YAML).


Entregables

Un RAPPORT.md en la raíz de tu repositorio, que contenga:

  1. La URL pública de tu sitio (https://<tu>.github.io/<repo>/).
  2. Para la Misión 2: dos capturas de pantalla de la página con dos colores distintos + los dos SHA + captura de la pestaña Actions con los runs verdes.
  3. Para la Misión 3: para cada avería, captura del run rojo + diagnóstico escrito + diff del correctivo + captura del run verde.
  4. Tus respuestas a las preguntas de reflexión.
  5. (Bonus) La prueba de la misión 4 si la has hecho.

Preguntas de reflexión

  1. Un workflow que contiene on: push sin precisar branches: — ¿cuándo se dispara? ¿Es un problema?
  2. ¿Por qué id-token: write es necesario para publicar en GitHub Pages? ¿Para qué sirve el token OIDC?
  3. ¿Qué ocurre si dos compañeros hacen git push en main al mismo tiempo? ¿Qué cambia el bloque concurrency en el comportamiento?
  4. Añades un secreto API_KEY en Settings → Secrets. El código del workflow puede leerlo vía ${{ secrets.API_KEY }}. ¿Puede imprimirse en los logs? ¿Por qué GitHub enmascara ciertos valores?
  5. Tienes dos workflows: deployer-pages.yml y verifier-config.yml. ¿El segundo es inútil puesto que el primero hace el mismo build? Justifica por qué son complementarios.
  6. Un runner GitHub Actions es una VM efímera: nada persiste entre dos runs. ¿Qué consecuencia tiene eso si tu build.py creara un archivo historique.log? ¿Dónde habría que guardarlo?

Baremo

ElementoPuntos
Misión 1 — config.json personalizado y build local OK20
Misión 2 — Prueba del ciclo push → Pages (2 colores + capturas + SHAs)30
Misión 3 — 3 averías diagnosticadas y reparadas30
Calidad del informe (estructura, capturas, explicaciones)20
Bonus — Misión 4+10
Total100 (+10)

Penalizaciones:

  • −10 por workflow situado fuera de .github/workflows/ (y por tanto ignorado por GitHub).
  • −15 por cualquier secreto en claro en un archivo YAML commiteado.
  • −5 por carpeta dist/ commiteada (debe quedarse en .gitignore).
  • −10 por cualquier modificación de outils/build.py.

Caja de herramientas GitHub Actions

yaml
# Esqueleto de referencia de un workflow
name: Descripción humana
on:                                       # CUÁNDO
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:                      # botón manual

permissions:                              # SOLO lo necesario
  contents: read
  pages: write

concurrency:                              # no apilar los 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: Un comando
        run: python outils/build.py

Los 5 gestos que salvan en el diagnóstico:

  1. Pestaña Actions → run rojo → step fallido: el mensaje de error está arriba, en rojo. Leerlo literalmente evita el 90 % de los errores.
  2. Re-run failed jobs: relanza solo los pasos en fallo, más rápido.
  3. workflow_dispatch: añádelo a tus workflows para poder relanzarlos a mano sin hacer un commit vacío.
  4. echo "::debug::mi mensaje": imprime un mensaje de debug en los logs.
  5. Lee los logs hasta el final: a veces el error significativo está en medio, no al final.


ANEXO A — El sitio estático

Archivo: site/config.json

Es el único archivo que debes modificar en condiciones normales.

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"
}

Restricciones del archivo:

  • Las 7 claves son obligatorias.
  • Los 3 colores (couleur_fond, couleur_texte, couleur_accent) deben respetar el formato #RRGGBB — si no, build.py rechaza.
  • JSON válido: comillas dobles, coma después de cada campo salvo el último.

Archivo: site/src/index.html.template

Plantilla HTML cuyo cada {{MARQUEUR}} lo sustituye build.py. La página mostrará:

  • El título y el subtítulo (desde config.json).
  • El autor y la versión (desde config.json).
  • El SHA del commit, el número del run y la rama (desde las variables GITHUB_* del runner).
  • La fecha de build.

El archivo completo se suministra en site/src/index.html.template.

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

Plantilla CSS que usa {{COULEUR_FOND}}, {{COULEUR_TEXTE}} y {{COULEUR_ACCENT}} en un bloque :root.

Extracto:

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

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


ANEXO B — Los workflows suministrados

Archivo: .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

Lectura línea a línea: ver 02-CORRECTION.md → Misión 2.

Archivo: .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 — El script de build

outils/build.py hace cuatro cosas:

  1. Lee site/config.json.
  2. Valida que las 7 claves esperadas están presentes.
  3. Valida que los 3 colores están en formato #RRGGBB (regex).
  4. Sustituye todos los {{MARQUEUR}} en las dos plantillas y escribe el resultado en dist/.

En GitHub Actions, las variables GITHUB_SHA, GITHUB_REF_NAME, GITHUB_RUN_NUMBER están disponibles automáticamente. En local no existen → el script muestra local-* en su lugar. Así se distingue una página de verdad desplegada por Actions de una página construida en local.



ANEXO D — Las tres averías a reparar

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

El workflow corre pero el job deployer falla con:

Error: Resource not accessible by integration

Pregunta que debes hacerte: ¿qué sección entera falta, arriba del archivo, junto a on:?

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

Un push a main nunca dispara este workflow. Ningún run aparece. Ningún error.

Pregunta que debes hacerte: lee la clave bajo on:. GitHub espera push, ¿qué se ve escrito?

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

build.py acierta y muestra OK dist/index.html genere. Pero upload-pages-artifact falla con:

Error: Path does not exist: ./public

Pregunta que debes hacerte: ¿dónde escribe build.py el sitio (mira el código Python)? ¿Qué path: hay que poner entonces en el workflow?


Curso creado por el Dr. Haythem REHOUMA — Desarrollo y despliegue de soluciones de datos