# Automatiza tus construcciones con GitHub Actions


## Requisitos previos

Completa todas las secciones anteriores de esta guía, comenzando con [Contenerizar una aplicación Node.js](/guides/nodejs/configure-github-actions/containerize/).

También debes tener:

- Una cuenta de [GitHub](https://github.com/signup).
- Una cuenta verificada de [Docker Hub](https://hub.docker.com/signup).

---

## Descripción general

En esta sección, configurarás un **pipeline de CI/CD** utilizando [GitHub Actions](https://docs.github.com/en/actions) para hacer lo siguiente de forma automática:

- Construir tu aplicación Node.js dentro de un contenedor Docker.
- Ejecutar pruebas unitarias y de integración, garantizando que tu aplicación cumpla con estándares sólidos de calidad de código.
- Realizar escaneos de seguridad y evaluación de vulnerabilidades.
- Enviar imágenes listas para producción a [Docker Hub](https://hub.docker.com).

---

## Conecta tu repositorio de GitHub a Docker Hub

Para permitir que GitHub Actions construya y envíe imágenes de Docker, almacenarás de forma segura tus credenciales de Docker Hub en tu nuevo repositorio de GitHub.

### Paso 1: Conecta tu repositorio de GitHub a Docker Hub

1. Crea un Token de acceso personal (PAT) desde [Docker Hub](https://hub.docker.com).
   1. Desde tu cuenta de Docker Hub, ve a **Account Settings → Security** (Configuración de la cuenta → Seguridad).
   2. Genera un nuevo token de acceso con permisos de **Read/Write** (Lectura/Escritura).
   3. Nómbralo con algo descriptivo, como `docker-nodejs-sample`.
   4. Copia y guarda el token; lo necesitarás en el paso 4.

2. Crea un repositorio en [Docker Hub](https://hub.docker.com/repositories/).
   1. Desde tu cuenta de Docker Hub, selecciona **Create a repository** (Crear un repositorio).
   2. Para el nombre del repositorio (Repository Name), utiliza algo descriptivo; por ejemplo: `nodejs-sample`.
   3. Una vez creado, copia y guarda el nombre del repositorio; lo necesitarás en el paso 4.

3. Crea un nuevo [repositorio en GitHub](https://github.com/new) para tu proyecto de Node.js.

4. Agrega las credenciales de Docker Hub como secretos en tu repositorio de GitHub.

   En tu repositorio de GitHub recién creado:
   1. Desde **Settings** (Configuración), ve a **Secrets and variables → Actions → New repository secret** (Secretos y variables → Actions → Nuevo secreto de repositorio).

   2. Agrega los siguientes secretos:

   | Nombre                   | Valor                                                    |
   | ------------------------ | -------------------------------------------------------- |
   | `DOCKER_USERNAME`        | Tu nombre de usuario de Docker Hub                       |
   | `DOCKERHUB_TOKEN`        | Tu token de acceso de Docker Hub (creado en el Paso 1)   |
   | `DOCKERHUB_PROJECT_NAME` | Tu nombre de proyecto de Docker (creado en el Paso 2)    |

   Con estos secretos, GitHub Actions puede autenticarse de forma segura con Docker Hub durante los flujos de trabajo automatizados.

5. Conecta tu proyecto local a GitHub.

   Vincula tu proyecto local `docker-nodejs-sample` al repositorio de GitHub que acabas de crear ejecutando el siguiente comando desde la raíz de tu proyecto:

   ```console
   $ git remote set-url origin https://github.com/{tu-nombre-de-usuario}/{nombre-de-tu-repositorio}.git
   ```

   > [!IMPORTANT]
   > Reemplaza `{tu-nombre-de-usuario}` y `{nombre-de-tu-repositorio}` con tu usuario y nombre de repositorio reales de GitHub.

   Para confirmar que tu proyecto local está correctamente conectado al repositorio remoto de GitHub, ejecuta:

   ```console
   $ git remote -v
   ```

   Deberías ver una salida similar a:

   ```console
   origin  https://github.com/{tu-nombre-de-usuario}/{nombre-de-tu-repositorio}.git (fetch)
   origin  https://github.com/{tu-nombre-de-usuario}/{nombre-de-tu-repositorio}.git (push)
   ```

   Esto confirma que tu repositorio local está vinculado adecuadamente y listo para enviar tu código fuente a GitHub.

6. Envía tu código fuente a GitHub.

   Sigue estos pasos para confirmar y enviar tu proyecto local a tu repositorio de GitHub:
   1. Prepara todos los archivos para el commit.

      ```console
      $ git add -A
      ```

      Este comando prepara todos los cambios (incluyendo archivos nuevos, modificados y eliminados), alistándolos para el commit.

   2. Confirma tus cambios (commit).

      ```console
      $ git commit -m "Initial commit with CI/CD pipeline"
      ```

      Este comando crea un commit que toma una captura de los cambios preparados con un mensaje descriptivo.

   3. Envía el código a la rama `main`.

      ```console
      $ git push -u origin main
      ```

      Este comando envía tus commits locales a la rama `main` del repositorio remoto de GitHub y establece la rama de seguimiento (upstream).

Una vez completado, tu código estará disponible en GitHub y cualquier flujo de trabajo de GitHub Actions que hayas configurado se ejecutará automáticamente.

> [!NOTE]  
> Conoce más sobre los comandos de Git utilizados en este paso:
>
> - [Git add](https://git-scm.com/docs/git-add) – Prepara los cambios (nuevos, modificados, eliminados) para el commit
> - [Git commit](https://git-scm.com/docs/git-commit) – Guarda una captura de tus cambios preparados
> - [Git push](https://git-scm.com/docs/git-push) – Sube los commits locales a tu repositorio de GitHub
> - [Git remote](https://git-scm.com/docs/git-remote) – Visualiza y gestiona las URLs de los repositorios remotos

---

### Paso 2: Configura el flujo de trabajo

Ahora crearás un flujo de trabajo de GitHub Actions que construya tu imagen de Docker, ejecute las pruebas y envíe la imagen a Docker Hub.

1. Desde tu repositorio en GitHub, selecciona la pestaña **Actions** en el menú superior.

2. Cuando se te solicite, selecciona **Set up a workflow yourself** (Configurar un flujo de trabajo por ti mismo).

   Esto abre un editor integrado para crear un nuevo archivo de flujo de trabajo. Por defecto, se guardará en:
   `.github/workflows/main.yml`

3. Agrega la siguiente configuración de flujo de trabajo al nuevo archivo:

```yaml
name: CI/CD – Node.js Application with Docker

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

jobs:
  test:
    name: Ejecutar pruebas de Node.js
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:18-alpine
        env:
          POSTGRES_DB: todoapp_test
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
        ports:
          - 5432:5432

    steps:
      - name: Descargar código
        uses: actions/checkout@v6

      - name: Configurar Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Almacenar en caché dependencias de npm
        uses: actions/cache@v5
        with:
          path: ~/.npm
          key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: ${{ runner.os }}-npm-

      - name: Construir imagen de prueba
        uses: docker/build-push-action@v7
        with:
          context: .
          target: test
          tags: nodejs-app-test:latest
          platforms: linux/amd64
          load: true
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache,mode=max

      - name: Ejecutar pruebas dentro del contenedor
        run: |
          docker run --rm \
            --network host \
            -e NODE_ENV=test \
            -e POSTGRES_HOST=localhost \
            -e POSTGRES_PORT=5432 \
            -e POSTGRES_DB=todoapp_test \
            -e POSTGRES_USER=postgres \
            -e POSTGRES_PASSWORD=postgres \
            nodejs-app-test:latest
        env:
          CI: true
        timeout-minutes: 10

  build-and-push:
    name: Construir y enviar imagen Docker
    runs-on: ubuntu-latest
    needs: test

    steps:
      - name: Descargar código
        uses: actions/checkout@v6

      - name: Configurar Docker Buildx
        uses: docker/setup-buildx-action@v4

      - name: Almacenar en caché capas de Docker
        uses: actions/cache@v5
        with:
          path: /tmp/.buildx-cache
          key: ${{ runner.os }}-buildx-${{ github.sha }}
          restore-keys: ${{ runner.os }}-buildx-

      - name: Extraer metadatos
        id: meta
        run: |
          echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> "$GITHUB_OUTPUT"
          echo "SHORT_SHA=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"

      - name: Iniciar sesión en Docker Hub
        uses: docker/login-action@v4
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Construir y enviar imagen de producción multi-arquitectura
        uses: docker/build-push-action@v7
        with:
          context: .
          target: production
          push: true
          platforms: linux/amd64,linux/arm64
          tags: |
            ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKERHUB_PROJECT_NAME }}:latest
            ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKERHUB_PROJECT_NAME }}:${{ steps.meta.outputs.SHORT_SHA }}
          cache-from: type=local,src=/tmp/.buildx-cache
          cache-to: type=local,dest=/tmp/.buildx-cache,mode=max
```

Este flujo de trabajo realiza las siguientes tareas para tu aplicación Node.js:

- Se activa con cada `push` (empuje) o `pull request` (solicitud de extracción) en la rama `main`.
- Construye una imagen Docker de prueba usando la etapa `test`.
- Ejecuta las pruebas en un entorno contenerizado.
- Detiene el flujo de trabajo si alguna prueba falla.
- Almacena en caché las capas de construcción de Docker y las dependencias de npm para ejecuciones más rápidas.
- Se autentica en Docker Hub usando secretos de GitHub.
- Construye una imagen usando la etapa `production`.
- Etiqueta y envía la imagen a Docker Hub con las etiquetas `latest` y el SHA corto del commit.

> [!NOTE]
> Para obtener más información sobre `docker/build-push-action`, consulta el [README de la GitHub Action](https://github.com/docker/build-push-action/blob/master/README.md).

---

### Paso 3: Ejecuta el flujo de trabajo

Después de agregar tu archivo de flujo de trabajo, activa el proceso de CI/CD.

1. Confirma y envía tu archivo de flujo de trabajo

   Desde el editor de GitHub, selecciona **Commit changes…** (Confirmar cambios...).
   - Este push activa automáticamente el pipeline de GitHub Actions.

2. Monitorea la ejecución del flujo de trabajo
   1. Desde tu repositorio de GitHub, ve a la pestaña **Actions**.
   2. Selecciona la ejecución del flujo de trabajo para seguir cada paso: **test**, **build**, **security** y (si es exitoso) **push** y **deploy**.

3. Verifica la imagen de Docker en Docker Hub
   - Después de una ejecución exitosa del flujo de trabajo, visita tus [repositorios de Docker Hub](https://hub.docker.com/repositories).
   - Deberías ver una nueva imagen en tu repositorio con:
     - Nombre del repositorio: `${nombre-de-tu-repositorio}`
     - Las etiquetas incluyen:
       - `latest` – representa la construcción exitosa más reciente; ideal para pruebas rápidas o despliegue.
       - `<short-sha>` – un identificador único basado en el hash del commit, útil para el seguimiento de versiones, reversiones y trazabilidad.

> [!TIP] Protege tu rama principal
> Para mantener la calidad del código y evitar pushes directos accidentales, habilita reglas de protección de ramas:
>
> - Desde tu repositorio de GitHub, ve a **Settings → Branches** (Configuración → Ramas).
> - En Branch protection rules (Reglas de protección de ramas), selecciona **Add rule** (Agregar regla).
> - Especifica `main` como el nombre de la rama.
> - Habilita opciones como:
>   - _Require a pull request before merging_ (Requerir una solicitud de extracción antes de fusionar).
>   - _Require status checks to pass before merging_ (Requerir que las comprobaciones de estado pasen antes de fusionar).
>
> Esto garantiza que solo el código probado y revisado se fusione en la rama `main`.

---

## Resumen

En esta sección, configuraste un pipeline de CI/CD completo para tu aplicación Node.js contenerizada usando GitHub Actions.

Lo que lograste:

- Creaste un nuevo repositorio de GitHub específicamente para tu proyecto.
- Generaste un token de acceso de Docker Hub y lo agregaste como secreto de GitHub.
- Creaste un flujo de trabajo de GitHub Actions que:
  - Construye tu aplicación en un contenedor Docker.
  - Ejecuta pruebas en un entorno contenerizado.
  - Envía una imagen a Docker Hub si las pruebas pasan.
- Verificaste que el flujo de trabajo se ejecuta con éxito.

Tu aplicación Node.js ahora cuenta con pruebas y despliegue automatizados.

---

## Recursos relacionados

Profundiza en la automatización y las mejores prácticas para aplicaciones contenerizadas:

- [Introducción a GitHub Actions](/guides/gha/) – Aprende cómo GitHub Actions automatizan tus flujos de trabajo
- [GitHub Actions para Docker Build](/build/ci/github-actions/) – Configura construcciones de contenedores con GitHub Actions
- [Sintaxis de flujo de trabajo para GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) – Referencia completa para escribir flujos de trabajo de GitHub
- [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) – Conoce las características y el uso de GHCR
- [Mejores prácticas para escribir Dockerfiles](/develop/develop-images/dockerfile_best-practices/) – Optimiza tu imagen para rendimiento y seguridad

---

## Próximos pasos

A continuación, aprende cómo puedes desplegar tu aplicación Node.js contenerizada en Kubernetes con una configuración lista para producción. Esto te ayudará a asegurar que tu aplicación se comporte como se espera en un entorno similar al de producción, reduciendo sorpresas durante el despliegue.

