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.
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 pushy 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:
git clone + push + activación de Pages).config.json con tus datos y tus colores.push → Pages funciona cambiando el color al menos dos veces.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.
Este documento es autosuficiente. No hay que abrir ningún curso externo para terminarlo.
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).
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.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).
GitHub Pages es un alojamiento estático incluido con cada repositorio público. Se publica HTML/CSS/JS por uno de estos métodos:
gh-pages (método antiguo)./docs de main (otro método).La URL pública es https://<utilisateur>.github.io/<nom-du-depot>/ — accesible desde cualquier navegador.
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:
permissions:
contents: read
pages: write
id-token: writeEs también la avería 1 de la misión 3 — reténla.
En cada ejecución, GitHub Actions expone variables de entorno útiles:
| Variable | Contenido |
|---|---|
GITHUB_SHA | El SHA del commit que disparó el run |
GITHUB_REF_NAME | El nombre de la rama (main, feature-x, etc.) |
GITHUB_RUN_NUMBER | Un 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.
Bucle final esperado — debes reproducirlo al menos 2 veces:
site/config.json en VS Code.couleur_fond (por ejemplo #0f172a → #7c3aed).git add site/config.json && git commit -m "changement de fond" && git push.https://<tu>.github.io/<repo>/ → el fondo es violeta.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.
outils/build.py — es el contrato entre config.json y el HTML/CSS final.dist/: figura en .gitignore y se reconstruye en cada run.git --version).python --version — para probar build.py en local).projet14-github-actions-pages-tp/ en tu disco.projet14-actions-pages..gitignore (ya se suministran).Desde la raíz del proyecto:
python .\outils\build.pyDebes 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.
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 mainPestañ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.
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.
Este es el corazón del proyecto. Debes demostrar que el ciclo CI/CD funciona, disparándolo al menos dos veces:
site/config.json (cambia couleur_fond por ejemplo a #dc2626 — rojo).git add site/config.json && git commit -m "fond en rouge" && git push.https://<tu>.github.io/<repo>/ → el fondo es rojo.#16a34a, azul #0891b2, violeta #7c3aed, a tu elección).Prueba a aportar en el informe:
git log --oneline).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.
La carpeta casses/ contiene tres workflows defectuosos, cada uno ilustrando un error clásico. Para cada uno:
.github/workflows/ (junto a los workflows oficiales)..github/workflows/ (no en casses/ — el original se queda defectuoso).| Archivo | Síntoma esperado |
|---|---|
casse-1-permissions-manquantes.yml | El job deployer falla con Resource not accessible by integration |
casse-2-declencheur-errone.yml | Ningún run se dispara: el workflow se ignora en silencio |
casse-3-chemin-artefact.yml | El 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.
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):
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).
Un RAPPORT.md en la raíz de tu repositorio, que contenga:
https://<tu>.github.io/<repo>/).on: push sin precisar branches: — ¿cuándo se dispara? ¿Es un problema?id-token: write es necesario para publicar en GitHub Pages? ¿Para qué sirve el token OIDC?git push en main al mismo tiempo? ¿Qué cambia el bloque concurrency en el comportamiento?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?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.build.py creara un archivo historique.log? ¿Dónde habría que guardarlo?| Elemento | Puntos |
|---|---|
Misión 1 — config.json personalizado y build local OK | 20 |
| Misión 2 — Prueba del ciclo push → Pages (2 colores + capturas + SHAs) | 30 |
| Misión 3 — 3 averías diagnosticadas y reparadas | 30 |
| Calidad del informe (estructura, capturas, explicaciones) | 20 |
| Bonus — Misión 4 | +10 |
| Total | 100 (+10) |
Penalizaciones:
.github/workflows/ (y por tanto ignorado por GitHub).dist/ commiteada (debe quedarse en .gitignore).outils/build.py.# 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.pyLos 5 gestos que salvan en el diagnóstico:
Re-run failed jobs: relanza solo los pasos en fallo, más rápido.workflow_dispatch: añádelo a tus workflows para poder relanzarlos a mano sin hacer un commit vacío.echo "::debug::mi mensaje": imprime un mensaje de debug en los logs.site/config.jsonEs el único archivo que debes modificar en condiciones normales.
{
"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:
couleur_fond, couleur_texte, couleur_accent) deben respetar el formato #RRGGBB — si no, build.py rechaza.site/src/index.html.templatePlantilla HTML cuyo cada {{MARQUEUR}} lo sustituye build.py. La página mostrará:
config.json).config.json).GITHUB_* del runner).El archivo completo se suministra en site/src/index.html.template.
site/src/css/style.css.templatePlantilla CSS que usa {{COULEUR_FOND}}, {{COULEUR_TEXTE}} y {{COULEUR_ACCENT}} en un bloque :root.
Extracto:
:root {
--fond: {{COULEUR_FOND}};
--texte: {{COULEUR_TEXTE}};
--accent: {{COULEUR_ACCENT}};
}
body {
background: var(--fond);
color: var(--texte);
}.github/workflows/deployer-pages.ymlname: 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@v4Lectura línea a línea: ver 02-CORRECTION.md → Misión 2.
.github/workflows/verifier-config.ymlname: 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"outils/build.py hace cuatro cosas:
site/config.json.#RRGGBB (regex).{{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.
casses/casse-1-permissions-manquantes.ymlEl workflow corre pero el job deployer falla con:
Error: Resource not accessible by integrationPregunta que debes hacerte: ¿qué sección entera falta, arriba del archivo, junto a on:?
casses/casse-2-declencheur-errone.ymlUn 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?
casses/casse-3-chemin-artefact.ymlbuild.py acierta y muestra OK dist/index.html genere. Pero upload-pages-artifact falla con:
Error: Path does not exist: ./publicPregunta 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