DEV Community

Cover image for Cómo desplegar Angular 19 en GitHub Pages desde GitHub Actions
Altaskur
Altaskur

Posted on

Cómo desplegar Angular 19 en GitHub Pages desde GitHub Actions

Indice

En los últimos directos estoy desarrollando una Pokédex funcional con Angular 19, y quería aprovechar la ocasión para mostrar el proceso completo, incluyendo cómo hacer el despliegue automático con GitHub Pages mediante GitHub Actions

La idea era sencilla: que cada vez que terminara un directo y subiera cambios a la rama main, el proyecto se construyera automáticamente y se publicara en GitHub Pages. Algo más que suficiente para ir mostrando los avances sin tener que hacer deploys manuales.

La idea era perfecta... hasta que empezaron los problemas.

Como hacía tiempo que no montaba un flujo de despliegue con Angular y Pages, fui directo a buscar tutoriales (en pleno directo, claro 😅). Pero todos los que encontraba pedían configurar un token personal de GitHub para poder hacer el despliegue, y yo sabía —por experiencia— que eso ya no es necesario.

💬 Este post refleja un proceso personal de aprendizaje mientras desarrollo un proyecto propio. No representa ninguna práctica oficial.


Plantilla del workflow

Aquí te dejo la plantilla .yml que estoy utilizando y que puedes adaptar fácilmente a tu repositorio:

name: 🚀 Deploy Angular to GitHub Pages

on:
  push:
    branches: [main]
  workflow_dispatch:

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

concurrency:
  group: 'pages'
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: 🧾 Checkout repository
        uses: actions/checkout@v4

      - name: ⚙️ Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: 📦 Install dependencies
        run: npm ci

      - name: 🛠️ Build Angular App
        run: npx ng build --configuration production --base-href "/[NOMBRE_DEL_REPOSITORIO]/"

      - name: 🧩 Add .nojekyll file
        run: echo "" > dist/[NOMBRE_DEL_PROYECTO]/browser/.nojekyll

      - name: 📤 Upload Pages artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: dist/[NOMBRE_DEL_PROYECTO]/browser

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: ⚙️ Setup GitHub Pages
        uses: actions/configure-pages@v4

      - name: 🚀 Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

Enter fullscreen mode Exit fullscreen mode

Y no te olvides de ajustar el angular.json:

"options": {
  "outputPath": "dist/[NOMBRE_DEL_PROYECTO]",
  "baseHref": "/[NOMBRE_DEL_REPOSITORIO]/"
}

Enter fullscreen mode Exit fullscreen mode

😉 Ahora que ya tienes la solución funcional, voy a contarte todo lo que pasó para llegar hasta aquí.

Primer problema: Missing download info

Al montar el workflow siguiendo algunos ejemplos que encontré por internet, me topé con el siguiente error:

Missing download info for actions/upload-pages-artifact@v3
Enter fullscreen mode Exit fullscreen mode

Y claro, lo primero que pensé fue: ¿qué demonios significa esto? 🤯

upload-pages-artifact es una acción de GitHub que permite subir el contenido generado (por ejemplo, la carpeta dist de Angular) para que otra acción, normalmente deploy-pages, lo recoja y lo publique en GitHub Pages.

Hasta aquí todo bien, ¿no? Pues no.

A fecha de creación de esta entrada, la última versión de deploy-pages es la v4, así que normalmente la invocas con:

uses: actions/deploy-pages@v4
Enter fullscreen mode Exit fullscreen mode

Pero aquí viene el plot twist: esta versión espera un formato de artefacto diferente al que genera upload-pages-artifact@v2, que es el que aún aparece en muchos tutoriales y ejemplos.

Primer golpe de realidad: me costó un buen rato descubrirlo, así que tenlo en cuenta cuando estés consultando documentación desactualizada o siguiendo ejemplos de hace unos meses.

Segundo problema: ¿Y si es mi custom domain?

Una vez solucionado el error anterior, la pipeline se ejecutaba correctamente y me lanzaba el siguiente mensaje:

Evaluate and set environment url
Evaluated environment url: http://altaskur.live/pokedex-angular/
Cleaning up orphan processes
Enter fullscreen mode Exit fullscreen mode

¡Perfecto! 🎉 Ya lo tenía. Podía visitar la URL y acceder al proyecto... o eso pensaba.

Pero sorpresa: al acceder a la URL obtenía un 404.

¿Perdona? Si la pipeline decía que el despliegue se había hecho correctamente... ¿por qué no puedo acceder?

Mi primera sospecha fue que quizás el problema era el custom domain. GitHub Pages, por defecto, muestra las páginas usando la ruta [tu_usuario].github.io, pero puedes añadirle un dominio personalizado, como es mi caso (altaskur.live).

¿Entonces no estaba bien configurado? 🤔

Revisé otros proyectos que tengo activos usando GitHub Pages y todos estaban funcionando perfectamente. Así que descarté que el problema viniera del dominio.

Tercer problema: ¿Y si la ruta del proyecto?

En este caso, el nombre del proyecto de Angular y el del repositorio en GitHub no coinciden. Y pensé: ¿y si, al hacer el build, Angular está usando internamente el nombre del proyecto en lugar del nombre del repositorio?

Entonces, al acceder a la URL del deploy con el nombre del repositorio, evidentemente no podría encontrar los archivos... lo que causaría un buen 404.

En mi cabeza tenía todo el sentido del mundo, así que me puse a investigar.

Descubrí que en el archivo angular.json puedes establecer manualmente estos parámetros añadiendo lo siguiente:

"options": {
  "outputPath": "dist/[NOMBRE_DEL_PROYECTO]",
  "baseHref": "/[NOMBRE_DEL_REPOSITORIO]/"
}
Enter fullscreen mode Exit fullscreen mode

Hice los cambios, subí el proyecto y...

Sorpresa: otro 404. 😩

Ya estaba desesperado. Probé unas cuantas combinaciones, rutas distintas, incluso nombres en minúsculas… casi doy el proyecto por perdido.

Hasta que…

Cuarto paso y solución: ¿Espera... qué estamos mandando al deploy?

Ya un poco harto, me puse a repasar conceptos.
La pipeline se ejecutaba sin errores, el build se completaba correctamente... Entonces, ¿el problema está en el deploy?

Lo siguiente que hice fue generar un build en local para ver qué estaba saliendo. En principio, todo parecía bien (aunque aquí estaba la clave, y yo aún no lo sabía).
Dentro de la carpeta dist, vi un directorio llamado browser, y ahí estaba el proyecto transpilado, listo para desplegar.
Lo abrí en el navegador: ¡todo funcionando! Bueno, salvo algún CORS... nada nuevo.

¿Entonces qué puede estar pasando?

Se me ocurrió entonces añadir unos logs al workflow para ver exactamente qué estaba subiendo:

  - name: Check output directory
        run: ls -lah dist/pokedex

      - name: Check browser directory
        run: ls -lah dist/pokedex/browser

      - name: Show index.html
        run: cat dist/pokedex/browser/index.html || echo "index.html NOT FOUND"
Enter fullscreen mode Exit fullscreen mode

Y ahí lo vi, como un mazazo:
index.html NOT FOUND

Fijándome bien, caí en la cuenta de algo que había pasado por alto: la carpeta browser.

👀 Busqué información al respecto y, efectivamente, desde Angular 17, la estructura del build cambió y ahora el contenido a desplegar se encuentra dentro de esa carpeta browser.

Solo tuve que modificar el yml, apuntar bien al nuevo path, quitar los logs…
¡y por fin funcionando! 🎉

Conclusión

Después de unos cuantos tropiezos, errores 404, pruebas en directo y mucho ensayo-error, por fin logré tener mi proyecto de Angular 19 desplegado automáticamente en GitHub Pages usando GitHub Actions.
Y lo mejor de todo: sin tokens manuales ni pasos innecesarios.

Lo que parecía una simple tarea de CI/CD terminó siendo una masterclass práctica de versiones, rutas, estructuras de build y pequeños detalles que cambian según la versión de Angular.

Pero de eso se trata, ¿no? De ir aprendiendo a golpes... y luego dejarlo por escrito para no olvidarlo 😅.

¿Te ha pasado algo parecido? ¿Has tenido que pelearte con GitHub Pages o las nuevas versiones de Angular?
Cuéntamelo en los comentarios o compártelo si crees que a alguien más le puede ahorrar un buen rato.

Nos vemos en el siguiente directo, ¡o en el siguiente post! 👋

Top comments (0)