Cómo desplegar una web en Github Pages


En los artículos Cómo crear un blog con Astro y Cómo adaptar Astro a TypeScript, te contaba las dos formas de crear un blog con Astro (con y sin TypeScript). Pero un blog no sirve de mucho si no está desplegado en un servidor al que puedan acceder otras personas.

En este artículo, te cuento cómo puedes desplegar la web y, en concreto, cómo hacerlo en Github Pages.

Y si tienes interés en saber por qué he elegido Github Pages, puedes descubrirlo aquí.

Desplegar la web en un servidor

Como con cualquier web basada en JavaScript/TypeScript, necesitamos hacer los siguientes pasos:

  1. Comprobar la web: lint, test, check, … y combinaciones de estos y otros comandos que pueden variar según el framework y que sirven para descubrir un buen número de fallos, errores y “cosas a mejorar”.
  2. Construir la web: build, comando sencillo de recordar y que genera los archivos “usables” en una carpeta que, en general, se llama dist (aunque hay casos en los que tiene otro nombre, como puede ser output, .next, …)
  3. Desplegar la web*: básicamente, copiar los archivos generados por el build en el servidor (porfa, nada de FTP, usa sistemas seguros)

Puedes realizar estos pasos manualmente… pero, lo normal, es automatizarlo.

En mi caso, voy a hacerlo con Github Actions y voy a publicar en Github Pages y, además, voy a tener el código base en un repo privado y el código para Github Pages en un repo público. ¿Por qué? Puedes leerlo aquí.

Paso 1: Configuración del repositorio privado

¿Alguien no desarrolla contra un repo a día de hoy?

Asumo que nadie.

Así que lo único que puede faltarte es que sea un repo privado para que cualquier código que tengas que sea específico para ti esté protegido de ojos indiscretos.

Dicho esto, este repo contiene todo el código fuente de tu web generada con Astro y será el encargado de construir el sitio y de enviar los archivos estáticos resultantes a un segundo repositorio público.

flowchart LR
    A[Repo Privado] --->|"build (CI)"| A
    A --->|"copy (CI)"| B[Repo Público]
    B --->|deploy| C[Github Pages]

Paso 2: Configurar el flujo de trabajo de GitHub Actions

Vamos a definir un flujo de trabajo de Github Actions en el repo privado. Este flujo de trabajo se encargará de construir el proyecto y subir los archivos generados al repo público.

Para ello, puedes crear el archivo del flujo de trabajo con el siguiente comando desde la carpeta raíz del repo:

mkdir -p .github/workflows && touch .github/workflows/nombre-del-workflow.yml # en mi caso, lo he llamado `build-and-store-website.yml

Después, necesitas escribir el siguiente código en dicho archivo:

name: Deploy statics on public repo
on:
  push:
    branches: 
      - main
    paths:
      - 'src/content/**'
      - 'src/pages/**'

jobs:
  build:
    name: create-package
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [21]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        name: Use Node.js 21
        with:
          node-version: ${{ matrix.node-version }}
      
      - name: Install Yarn
        run: npm install --global yarn

      - name: Install dependencies with Yarn
        run: yarn install
      
      - name: Build the project with Yarn
        run: yarn build
        env:
          CI: false

      - run: ls -R dist

      - name: Push built files to public repo
        run: |
          git config --global user.email "${{ secrets.GIT_USER_EMAIL }}"
          git config --global user.name "${{ secrets.GIT_USER_NAME }}"
          git clone https://${{ secrets.API_TOKEN_GITHUB }}@github.com/${{ secrets.GIT_USER }}/${{ secrets.GIT_REPO }}.git deploy_repo
          rm -rf deploy_repo/*
          cp -R dist/* deploy_repo/
          cd deploy_repo
          git add .
          COMMIT_MESSAGE="Deploy new version - $(date -u +"%Y-%m-%d %H:%M:%S UTC")"
          git commit -m "$COMMIT_MESSAGE"
          git push origin main
        env:
          GITHUB_TOKEN: ${{ secrets.API_TOKEN_GITHUB }}

Nota:

Aunque procuro mantener actualizados los artículos (sobre todo, el código que se puede copiar), por favor, comprueba el código porque puede que haya versiones desactualizadas.

¿Cuándo se activa?

En el archivo tenemos una sección (justo después del nombre) con la clave on (que viene a traducirse como ejecútate cuando suceda lo siguiente).

Así que, ¿cuándo queremos que se ejecute?

Cuando hagamos cambios de contenido que estén listos para producción.

No voy a entrar a explicar Git, así que, en corto, esto se traduce en:

Cuando hagamos un “push a la rama main” que tenga cambios en las carpetas src/content y src/pages.

Traducido a YAML (el formato que se usa para definir estos flujos) queda como sigue:

on:
  push:
    branches: 
      - main
    paths:
      - 'src/content/**'
      - 'src/pages/**'

¿Qué debe hacer?

Además de la sección de activación, tenemos una sección de trabajos (o jobs) aunque en este flujo solo hay uno. Esto se debe a que cada trabajo tiene su propio entorno y es más sencillo generar los estáticos de la web y copiarlos en el repo público que definir un trabajo para cada paso y luego compartir los artefactos entre los entornos de cada trabajo.

Los puntos importantes para entender este trabajo son los siguientes:

  • runs-on: qué sistema operativo va a utilizar el trabajo para su ejecución (en este caso, la última versión de Ubuntu).
  • steps: pasos para completar el trabajo

Dicho esto, ¿qué pasos necesitamos para completar el trabajo?

  1. Leer del repositorio: para eso usamos una acción prefedefinida (actions/checkout)
  2. Preparar Node: usamos otra acción predefinida (actions/setup-node) con la versión que hayamos configurado (en strategy->matrix->node-version)
  3. Instalar Yarn: puedes saltarte este paso si decides usar npm en vez de yarn. Igualmente, necesitarás adaptarlo si usas pnpm.
  4. Generación del código: son los pasos de “Instalar dependencias” y “Construir el proyecto
  5. He añadido un paso para comprobar qué se genera (ls -R dist) pero solo es necesario para depuración
  6. Finalmente, paso “a pelo” para copiar los archivos estáticos generados en el repo público
jobs:
  build:
    name: create-package
    runs-on: ubuntu-latest

    strategy:
      matrix:
        node-version: [21]

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        name: Use Node.js 21
        with:
          node-version: ${{ matrix.node-version }}
      
      - name: Install Yarn
        run: npm install --global yarn

      - name: Install dependencies with Yarn
        run: yarn install
      
      - name: Build the project with Yarn
        run: yarn build
        env:
          CI: false

      - run: ls -R dist

      - name: Push built files to public repo
        run: |
          git config --global user.email "${{ secrets.GIT_USER_EMAIL }}"
          git config --global user.name "${{ secrets.GIT_USER_NAME }}"
          git clone https://${{ secrets.API_TOKEN_GITHUB }}@github.com/${{ secrets.GIT_USER }}/${{ secrets.GIT_REPO }}.git deploy_repo
          rm -rf deploy_repo/*
          cp -R dist/* deploy_repo/
          cd deploy_repo
          git add .
          COMMIT_MESSAGE="Deploy new version - $(date -u +"%Y-%m-%d %H:%M:%S UTC")"
          git commit -m "$COMMIT_MESSAGE"
          git push origin main
        env:
          GITHUB_TOKEN: ${{ secrets.API_TOKEN_GITHUB }}

Si te fijas, todas las partes que pueden variar según quién seas, o el repo que uses, están como secrets del repo. De esta manera, puedes copiar el contenido y solo necesitarás configurar esos secrets para que el flujo de trabajo funcione correctamente.

Para facilitar este paso, a continuación te listo qué variables debes configurar en los secrets:

  • GIT_USER_EMAIL
    • puedes averiguarlo con git config --global user.email
  • GIT_USER_NAME
    • puedes averiguarlo con git config --global user.name
  • API_TOKEN_GITHUB
    • Necesitarás un token con permisos para repo y workflow
  • GIT_USER
    • tu nombre de usuario en Github (puedes sacarlo de la URL: https://github.com/<username>/<repo>/)
  • GIT_REPO
    • identificador del repo público (puedes sacarlo de la URL: https://github.com/<username>/<repo>/)

El Diablo está en los detalles

Astro, igual que muchos otros frameworks (cada vez más frameworks, de hecho), tiene telemetría activada por defecto (es decir, recibe datos de lo que haces con Astro para, al menos en teoría, mejorar el propio framework).

Si no estás de acuerdo con esta práctica, necesitas modificar el package.json del proyecto para desactivarla.

Ahora mismo, tu package.json se verá así:

{
    "name": "astro-blog-template",
    "type": "module",
    "version": "0.0.1",
    "scripts": {
        "dev": "astro dev",
        "start": "astro dev",
        "build": "astro check && astro build",
        "preview": "astro preview",
        "astro": "astro"
    },
    "dependencies": {
        "@astrojs/mdx": "^3.1.4",
        "@astrojs/rss": "^4.0.7",
        "@astrojs/sitemap": "^3.1.6",
        "astro": "^4.14.5",
        "@astrojs/check": "^0.9.3",
        "typescript": "^5.5.4"
    }
}

Y, tras el cambio, debería verse como sigue:

{
    "name": "astro-blog-template",
    "type": "module",
    "version": "0.0.1",
    "scripts": {
        "dev": "astro dev",
        "start": "astro dev",
        "build": "astro telemetry disable && astro check && astro build",
        "preview": "astro preview",
        "astro": "astro"
    },
    "dependencies": {
        "@astrojs/mdx": "^3.1.4",
        "@astrojs/rss": "^4.0.7",
        "@astrojs/sitemap": "^3.1.6",
        "astro": "^4.14.5",
        "@astrojs/check": "^0.9.3",
        "typescript": "^5.5.4"
    }
}

Paso 3: Configuración del repositorio público

El segundo repositorio es público y contiene únicamente los archivos estáticos que serán publicados en GitHub Pages. En este caso, no nesitamos un flujo de trabajo específico para publicar, basta con activar Github Pages desde la configuración y seleccionar la rama main como fuente de los archivos a publicar.

Sí que necesitarás configurar tu dominio personalizado (si es que quieres usarlo) y marcar la casilla para forzar el uso de HTTPS.

Una pequeña trampa

Gracias por llegar hasta aquí. En próximos artículos, seguiré contando cómo crear una plantilla personalizada.

Aunque si no quieres crearla, puedes usar la mía (la que tendrás, más o menos, si sigues todos los pasos de todos los artículos de esta serie) con el siguiente comando:

yarn create astro -- --template borjalofe/astro-blog-template