Módulo 1: Why Cicd And Gitops

7. Manos a la obra: tu primer workflow local, de punta a punta

Descripción

Este es el momento donde todo lo que leíste en las lecciones 1 a 5, e instalaste en la lección 6, se convierte en un workflow corriendo de verdad. Vas a crear un repositorio desechable —fuera de andes-cargo-infra/, que arrancas recién en la lección 8—, escribir un workflow de "hola mundo", y correrlo con act: listar sus jobs, dispararlo con un evento simulado, y leer su salida completa. También vas a reproducir, a propósito, dos errores reales de act — para que los reconozcas de inmediato cuando aparezcan más adelante en esta guía, no la primera vez que te bloqueen sin previo aviso.

Conexión con el módulo

Esta es la lección bisagra de todo el módulo. La lección 6 te dio la herramienta lista; esta lección la usa, por primera vez, sobre un workflow real. Todo lo que escribas aquí es intencionalmente desechable —un "hola mundo" sin ningún vínculo con Andes Cargo— porque el objetivo no es empezar el proyecto real todavía (eso es la lección 8), es que el ciclo completo —escribir YAML, listar jobs, dispararlos, leer la salida— te quede grabado antes de tocar infraestructura que le importa a alguien.


El ciclo completo, de un vistazo

sequenceDiagram
    participant Tú
    participant act
    participant Docker

    Tú->>act: act -l
    act-->>Tú: lista de jobs detectados en .github/workflows/

    Tú->>act: act push
    act->>Docker: docker pull catthehacker/ubuntu:act-latest
    Docker-->>act: imagen lista
    act->>Docker: crea un contenedor efímero para el job
    Docker->>Docker: corre cada step, en orden
    Docker-->>act: salida de cada step
    act-->>Tú: "Job succeeded" (o el error, si algo falló)

Dos comandos, cada uno con un trabajo específico: act -l lee el YAML y te dice qué jobs existen y qué eventos los disparan, sin ejecutar nada. act push (o act a secas, si el workflow escucha push) simula ese evento y corre el job completo, de verdad, dentro de un contenedor.


El proyecto: un repositorio, un workflow

Crea una carpeta nueva para este ejercicio, fuera de cualquier proyecto real:

mkdir act-lab && cd act-lab
git init
mkdir -p .github/workflows

.actrc — la misma línea que ya escribiste en la lección 6, ahora en este proyecto específico (recuerda: act la busca en el directorio de trabajo actual, no de forma global):

-P ubuntu-latest=catthehacker/ubuntu:act-latest

.github/workflows/hello.yml — el único workflow de esta lección:

name: hello-world

on:
  push:
    branches: [main]

jobs:
  say-hello:
    runs-on: ubuntu-latest
    steps:
      - name: Print a greeting
        run: echo "Hello from act, running Andes Cargo's first workflow"
      - name: Show reproducibility fields
        run: |
          echo "run_id=${{ github.run_id }}"
          echo "run_number=${{ github.run_number }}"
          echo "sha=${{ github.sha }}"

Fíjate en la estructura, aunque el Módulo 2 la diseccione a fondo: on dice cuándo corre (en cada push a main), jobs agrupa el trabajo (un solo job, say-hello), runs-on dice en qué imagen (ubuntu-latest, la que .actrc mapea a catthehacker/ubuntu:act-latest), y steps es la secuencia de comandos, en orden. El segundo step imprime tres valores que vas a usar para confirmar, con tus propios ojos, la tabla de reproducibilidad del diseño de esta guía: github.run_id y github.run_number (fijos bajo act), y github.sha (el commit real de tu repositorio, que va a variar).

Confirma que act detecta el workflow antes de correr nada:

git add -A
git commit -m "first workflow"
act -l

Qué esperar (salida literal, ejecutada para escribir esta lección):

Stage  Job ID     Job name   Workflow name  Workflow file  Events
0      say-hello  say-hello  hello-world    hello.yml      push

Esta tabla es puramente informativa: te dice qué jobs existen, en qué archivo, y qué evento los dispara — sin ejecutar un solo comando todavía.


Paso 1 — Disparar el workflow con act push

act push

Qué esperar (salida literal, ejecutada para escribir esta lección — nota antes de leerla: en una Mac con chip Apple Silicon vas a ver, además, la advertencia de arquitectura de la lección 6; se omite aquí por brevedad, ya la conoces):

[hello-world/say-hello] ⭐ Run Set up job
[hello-world/say-hello] 🚀  Start image=catthehacker/ubuntu:act-latest
[hello-world/say-hello]   🐳  docker pull image=catthehacker/ubuntu:act-latest platform= username= forcePull=true
[hello-world/say-hello] using DockerAuthConfig authentication for docker pull
[hello-world/say-hello]   🐳  docker create image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[hello-world/say-hello]   🐳  docker run image=catthehacker/ubuntu:act-latest platform= entrypoint=["tail" "-f" "/dev/null"] cmd=[] network="host"
[hello-world/say-hello]   🐳  docker exec cmd=[node --no-warnings -e console.log(process.execPath)] user= workdir=
[hello-world/say-hello]   ✅  Success - Set up job
[hello-world/say-hello] ⭐ Run Main Print a greeting
[hello-world/say-hello]   🐳  docker exec cmd=[bash -e /var/run/act/workflow/0] user= workdir=
[hello-world/say-hello]   | Hello from act, running Andes Cargo's first workflow
[hello-world/say-hello]   ✅  Success - Main Print a greeting [57.549834ms]
[hello-world/say-hello] ⭐ Run Main Show reproducibility fields
[hello-world/say-hello]   🐳  docker exec cmd=[bash -e /var/run/act/workflow/1] user= workdir=
[hello-world/say-hello]   | run_id=1
[hello-world/say-hello]   | run_number=1
[hello-world/say-hello]   | sha=d379bd25a7179b3b0e1f598cc891a8ee62abc04a
[hello-world/say-hello]   ✅  Success - Main Show reproducibility fields [60.556458ms]
[hello-world/say-hello] ⭐ Run Complete job
[hello-world/say-hello] Cleaning up container for job say-hello
[hello-world/say-hello]   ✅  Success - Complete job
[hello-world/say-hello] 🏁  Job succeeded

Lee esta salida con la misma atención que le dedicarías a un terraform plan. Cada línea con 🐳 es una operación real de Docker —act no la simula, la ejecuta—; el docker create/docker run con entrypoint=["tail" "-f" "/dev/null"] es el truco técnico que usa act para mantener el contenedor vivo mientras corre cada step dentro de él, uno por uno, con docker exec. Cada ✅ marca un paso exitoso; si cualquiera fallara, act se detiene ahí y reporta el fallo, exactamente como un runner real de GitHub.

La tabla de reproducibilidad, confirmada con tus propios ojos

Mira las tres líneas que imprimió el segundo step:

CampoValor de esta corrida¿Fijo o variable?
run_id1Fijoact no incrementa este valor entre corridas locales, a diferencia de GitHub real
run_number1Fijo, misma razón
shad379bd25a7179b3b0e1f598cc891a8ee62abc04aVariable — es el commit real de git rev-parse HEAD en tu repositorio; el tuyo va a ser un hash completamente distinto

Esta es, literalmente, la distinción de honestidad más fina de esta guía, confirmada con un comando real en vez de solo leída en el diseño: cada vez que corras act push sobre este mismo repositorio, run_id y run_number van a seguir siendo 1 — pero si corres el workflow después de un nuevo commit, sha va a cambiar. Ningún bloque "Qué esperar" de esta guía presenta un SHA de commit como si fuera un valor fijo — siempre está marcado como variable, exactamente como aquí.


Paso 2 — Reproducir dos errores reales, a propósito

Error 1 — YAML mal indentado

Rompe la indentación de tu workflow a propósito. Crea .github/workflows/broken.yml:

name: broken-workflow
on:
  push:
jobs:
  say-hello:
runs-on: ubuntu-latest
    steps:
      - run: echo "this yaml is broken"

Fíjate en el error: runs-on perdió su indentación respecto a say-hello. Corre:

act push

Qué esperar (salida literal, reproducida para esta lección):

Error: workflow is not valid. 'broken.yml': yaml: line 7: mapping values are not allowed in this context

act valida la sintaxis YAML antes de intentar ejecutar nada — ni siquiera llega a crear un contenedor. El mensaje te da el archivo y la línea exactos, aunque el número de línea puede no coincidir exactamente con dónde "se ve" el error a simple vista (YAML es sensible a indentación de una forma que a veces hace que el error se reporte una línea antes o después de donde el ojo humano lo detecta primero). Borra broken.yml antes de seguir — no lo necesitas para el resto de esta lección.

Error 2 — Un evento que no dispara ningún job

Tu hello.yml solo escucha push. ¿Qué pasa si le pides a act que simule un evento distinto, que ningún job de este repositorio espera?

act pull_request

Qué esperar (salida literal, reproducida para esta lección):

Error: Could not find any stages to run. View the valid jobs with `act --list`. Use `act --help` to find how to filter by Job ID/Workflow/Event Name

Este no es un error de sintaxis —tu YAML sigue siendo válido—, es act diciéndote, con precisión, que ningún job de este repositorio está configurado para reaccionar al evento pull_request. Es el mismo tipo de situación que vas a encontrar en el Módulo 3, cuando ci.yml escuche pull_request y apply.yml escuche push — si le pides a act el evento equivocado para el workflow equivocado, este es exactamente el mensaje que vas a ver.


Errores comunes

Confundir el error de YAML con un problema de Docker (de diagnóstico). Qué pasa: alguien ve Error: workflow is not valid y empieza a revisar si Docker está corriendo, si la imagen se descargó bien, etc. Por qué pasa: cualquier error de act se siente, a primera vista, como "algo con los contenedores salió mal". Cómo detectarlo: si el mensaje incluye la frase workflow is not valid seguida de un nombre de archivo y un número de línea. Cómo corregirlo: este error ocurre antes de que act toque Docker en absoluto — es un error de parseo de YAML, resuelto editando el archivo indicado, nunca reiniciando contenedores.

Olvidar que act -l no ejecuta nada (de expectativa). Qué pasa: alguien corre act -l, ve la tabla de jobs, y espera que eso signifique que el workflow ya corrió. Por qué pasa: la salida se parece, superficialmente, a un resumen de ejecución. Cómo detectarlo: si esperabas ver el echo de tu workflow después de correr act -l, y no aparece en ningún lado. Cómo corregirlo: -l es de "list" (listar) — es puramente informativo, lee el YAML sin correr un solo step. Para ejecutar de verdad, necesitas act push (o act a secas, o act -j <job-id> para un job específico).

Esperar que act pull_request funcione sobre cualquier repositorio (conceptual, ver Error 2 arriba). Qué pasa: alguien asume que act <evento> es un comando genérico que siempre corre "el workflow", sin importar qué evento escuche ese workflow específico. Por qué pasa: es fácil pensar en act como "el botón de correr todo" en vez de "el simulador de un evento específico". Cómo detectarlo: si te sorprende el error Could not find any stages to run al pedir un evento que tu YAML no declara en su bloque on. Cómo corregirlo: revisa siempre act -l primero — la columna Events te dice exactamente qué evento tienes que pedirle a act para que ese job corra.


Ejercicios

Ejercicio 1 — Corre el ciclo de memoria. Sin mirar esta lección, escribe los dos comandos que usaste hoy, en orden, y qué hace cada uno.

Ver solución

1. act -l — lee el YAML de .github/workflows/ y muestra qué jobs existen, en qué archivo, y qué evento los dispara, sin ejecutar nada. 2. act push (o act <evento> en general) — simula ese evento específico y corre, de verdad, dentro de un contenedor Docker, cada job que lo escuche, step por step, en orden.

Ejercicio 2 — Predice qué campo cambia. Si corres act push sobre el mismo repositorio de esta lección tres veces seguidas, sin hacer ningún commit nuevo entre medio, ¿qué esperas que pase con run_id, run_number y sha en cada corrida?

Ver solución

run_id y run_number van a seguir siendo 1 las tres vecesact no los incrementa entre corridas locales, a diferencia de GitHub real, donde cada corrida de un workflow tiene un run_id distinto y creciente. sha también va a ser idéntico en las tres corridas, porque no hiciste ningún commit nuevo entre medio — github.sha bajo act refleja el HEAD real de tu repositorio Git local en el momento de la corrida, y si el HEAD no cambió, el valor tampoco cambia. El sha solo cambiaría si hicieras un nuevo commit antes de volver a correr act push.

Ejercicio 3 — Diagnostica el evento equivocado. Un compañero te muestra este mensaje: "corrí act workflow_dispatch sobre mi repositorio y me dio Could not find any stages to run, pero estoy seguro de que mi YAML no tiene ningún error de sintaxis". ¿Qué comando le pedirías que corra primero para diagnosticar el problema, y qué esperarías ver ahí?

Ver solución

Le pedirías que corra act -l primero. Ese comando muestra, en la columna Events, exactamente qué evento(s) disparan cada job de su repositorio. Si la columna dice push (como en el hello.yml de esta lección) y él pidió workflow_dispatch, el diagnóstico es inmediato: no hay ningún error de sintaxis —tenía razón en eso—, simplemente le pidió a act un evento que ningún job de ese repositorio escucha. La solución es o bien correr act push (el evento correcto para ese YAML), o bien agregar workflow_dispatch al bloque on del workflow si de verdad quiere poder dispararlo manualmente — patrón que el Módulo 2 de esta guía cubre en detalle.


Resumen y siguiente paso

En esta lección corriste, por primera vez, un workflow completo con act: listaste sus jobs con act -l (literal), lo disparaste con act push y leíste su salida completa, línea por línea, incluidas las operaciones reales de Docker por debajo. Confirmaste con tus propios ojos la tabla de reproducibilidad del diseño de esta guía: run_id y run_number fijos en 1, sha variable según tu commit real. También reprodujiste, de forma literal, los dos errores más comunes de esta capa: YAML mal indentado, y pedir un evento que ningún job escucha.

Antes de avanzar deberías poder: escribir un workflow mínimo de "hola mundo" de memoria; explicar la diferencia entre act -l y act push; y leer el mensaje Could not find any stages to run sin confundirlo con un problema de Docker.

Tienes el ciclo completo grabado. Lo único que falta es empezar el proyecto real. La lección 8 —el proyecto de este módulo— prepara andes-cargo-infra/, el repositorio que vas a usar, sin reescribirlo, hasta el capstone del Módulo 8.

Recursos

  1. nektosact.com — User Guide — documentación oficial de act -l, act push, y cómo filtrar por evento o job.
  2. GitHub Docs — Understanding GitHub Actions — la anatomía de on/jobs/steps/runs-on que empezaste a usar en esta lección, diseccionada a fondo en el Módulo 2.
  3. YAML Spec — Indentation — la especificación oficial de YAML, la fuente de por qué la indentación no es una preferencia de estilo, sino sintaxis obligatoria.