# Construye la imagen Docker de la web y la sube al registry de Forgejo. # # POR QUÉ: antes el build corría en groucho, el host de despliegue (5,9 GB de RAM # compartidos con Mongo 7 + web + notifications + redis + node-red). Un `meteor build` # lo asfixiaba hasta dejarlo sin ssh. A partir de aquí groucho **no compila**: hace # `docker compose pull && up -d web` y ya. # # runs-on: docker → el runner de aaron (forge.yml, label `docker`, capacity 1). # # ⚠️ POR QUÉ KANIKO Y NO `docker build`: # aaron es un host COMPARTIDO — aloja git.comunes.org, Jenkins y GlitchTip. Construir con # `docker build` exige montar /var/run/docker.sock en el job, que es poder # root-equivalente sobre aaron; y además el límite de memoria del contenedor del job NO # afectaría al build, porque quien construye es el demonio, fuera del job. Kaniko # construye DENTRO del contenedor del job: no hace falta socket y `--memory` sí acota al # proceso que se come la RAM. Si el `meteor build` muere por memoria, sube el `--memory` # de `container.options` (y mira qué más está corriendo en aaron), no lo quites. # # ⚠️ POR QUÉ EL TEST VIVE EN ESTE FICHERO Y NO EN UNO APARTE: # `needs:` solo enlaza jobs DEL MISMO workflow — no hay forma de que un fichero # distinto bloquee a este. Y el requisito de la fase 11 es justo ese: que una # imagen no se publique nunca con los tests en rojo. Así que los dos jobs van # juntos, `build` depende de `test`, y se acabó. Si algún día act_runner soporta # `workflow_call` de forma fiable, se puede extraer. name: build-image on: push: branches: [meteor3-wip, tcef-master] paths-ignore: ['**.md'] pull_request: workflow_dispatch: jobs: # Suite de servidor (meteortesting:mocha). Bloquea el build: ver la nota de # arriba. Mide y publica duración y pico de RAM en el resumen del job para que # el margen del runner de aaron sea un dato y no una intuición. test: runs-on: docker container: image: node:22-bookworm # Medido en local: pico ~2,0 GB (meteor test + su mongod). 3g deja margen # sin comerse los 4g que necesita el build de Kaniko justo después # (capacity: 1 ⇒ los jobs no coinciden en el tiempo). Si el job muere por # OOM, sube esto y mira antes qué más está corriendo en aaron. options: --memory=3g --memory-swap=3g --cpus=3 env: # meteor se niega a arrancar como root si no. METEOR_ALLOW_SUPERUSER: '1' # Node 22 + Happy Eyeballs elige IPv6 sin ruta contra warehouse.meteor.com # (mismo workaround que en el Dockerfile y en dev). NODE_OPTIONS: --dns-result-order=ipv4first --no-network-family-autoselection METEOR_RELEASE: '3.1' steps: # `time` para medir el pico de RSS; el resto es lo mismo que instala el # Dockerfile (node-gyp 3.x de algunas deps de Atmosphere llama a `python`). - name: Herramientas run: | set -e apt-get update -qq apt-get install -y --no-install-recommends \ curl ca-certificates git time python3 python-is-python3 make g++ >/dev/null rm -rf /var/lib/apt/lists/* # Sin actions JS: el código se baja por el API de Forgejo, igual que en el # job de build (ver la nota de más abajo). - name: Descargar el código env: TOKEN: ${{ github.token }} run: | set -e mkdir -p src wget -q -O src.tar.gz \ "http://x-access-token:${TOKEN}@forgejo:3000/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz" tar xzf src.tar.gz -C src --strip-components=1 rm -f src.tar.gz test -f src/package.json || { echo "No hay package.json en el tarball"; ls src | head; exit 1; } # ~700 MB de meteor-tool en cada job. No se cachea porque `actions/cache` es # una action JS (fase 9 §3) y montar un volumen del host exige tocar # `valid_volumes` en la config de act_runner de aaron. Pendiente: si el job # se vuelve molesto, añadir /data/ci-cache/meteor -> /root/.meteor. - name: Instalar meteor-tool run: | set -e curl -fsSL "https://install.meteor.com/?release=${METEOR_RELEASE}" | sh meteor --version - name: Dependencias npm run: | set -e cd src meteor npm ci --no-audit --no-fund # `meteor test` levanta su propio mongod (single-node replica set), que es # lo que hace que el oplog esté disponible y los observadores reaccionen al # instante: contra un mongo externo sin oplog, Meteor cae a polling cada 10s # y los tests de subsUnion tardarían eso en ver cada cambio. - name: Tests de servidor run: | set -e cd src START=$(date +%s) /usr/bin/time -v -o ../time.log meteor npm run test:ci echo "DURACION=$(( $(date +%s) - START ))s" grep -E 'Maximum resident set size' ../time.log || true - name: Resumen if: always() run: | { echo "### Tests de servidor" echo "" echo "- pico de RSS: $(grep -oE '[0-9]+$' time.log 2>/dev/null | head -1 || echo '?') KB" } >> "$GITHUB_STEP_SUMMARY" || true build: # Ninguna imagen sale de aquí con los tests en rojo. Ese es el punto entero # de la fase 11. needs: test # En un PR solo interesa saber si pasa; publicar imagen es cosa de la rama. if: github.event_name != 'pull_request' runs-on: docker container: # `:debug` trae busybox: el runner necesita una shell para ejecutar los `run:`. image: gcr.io/kaniko-project/executor:v1.23.2-debug # Tope duro para no asfixiar aaron. memory-swap == memory ⇒ swap 0: preferimos que # muera el build a que el host empiece a paginar y se lleve por delante la forja. options: --memory=4g --memory-swap=4g --cpus=3 env: IMAGE: git.comunes.org/comunes/tcef-web steps: # Primero de todo: sin credenciales el build tarda ~30 min en llegar al push para # morir ahí. Mejor fallar en 2 segundos y decir exactamente qué falta. - name: Comprobar secretos shell: sh env: REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | export PATH=/busybox:/kaniko:$PATH if [ -z "$REGISTRY_USER" ] || [ -z "$REGISTRY_TOKEN" ]; then echo "Faltan los secretos REGISTRY_USER / REGISTRY_TOKEN." echo "Crealos en Settings -> Actions -> Secrets de este repositorio." echo "REGISTRY_TOKEN: token de git.comunes.org con scope write:package" echo "(Settings -> Applications -> Generate New Token). No reutilices uno con scope 'all'." exit 1 fi # Nada de `actions/checkout` (necesita Node) ni de `git` (la imagen de kaniko solo trae # busybox y los binarios de kaniko: no hay git, ni apk para instalarlo). Se baja el # tarball del commit por el API de Forgejo, que busybox sí sabe hacer con wget + tar. - name: Descargar el código shell: sh env: TOKEN: ${{ github.token }} run: | set -e export PATH=/busybox:/kaniko:$PATH mkdir -p src wget -q -O src.tar.gz \ "http://x-access-token:${TOKEN}@forgejo:3000/api/v1/repos/${GITHUB_REPOSITORY}/archive/${GITHUB_SHA}.tar.gz" tar xzf src.tar.gz -C src --strip-components=1 rm -f src.tar.gz test -f src/Dockerfile || { echo "No hay Dockerfile en el tarball"; ls src | head; exit 1; } - name: Credenciales del registry shell: sh env: REGISTRY_USER: ${{ secrets.REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} run: | set -e export PATH=/busybox:/kaniko:$PATH mkdir -p /kaniko/.docker AUTH=$(printf '%s:%s' "$REGISTRY_USER" "$REGISTRY_TOKEN" | base64 | tr -d '\n') printf '{"auths":{"git.comunes.org":{"auth":"%s"}}}' "$AUTH" > /kaniko/.docker/config.json - name: Build y push shell: sh run: | set -e export PATH=/busybox:/kaniko:$PATH SHORT_SHA=$(echo "$GITHUB_SHA" | cut -c1-12) BRANCH_TAG=$(echo "$GITHUB_REF_NAME" | tr '/' '-') # --cache: reutiliza capas entre builds (apt, meteor-tool, npm install) contra # el propio registry; sin esto cada push recompila el mundo. # --single-snapshot y --compressed-caching=false bajan mucho el pico de RAM, # que es justo lo que nos importa en un host compartido. /kaniko/executor \ --context "dir://$(pwd)/src" \ --dockerfile Dockerfile \ --destination "${IMAGE}:${BRANCH_TAG}" \ --destination "${IMAGE}:${SHORT_SHA}" \ --cache=true \ --cache-repo "${IMAGE}/cache" \ --compressed-caching=false \ --snapshot-mode=redo \ --verbosity=info echo "Publicado ${IMAGE}:${BRANCH_TAG} y :${SHORT_SHA}" - name: Cómo desplegar shell: sh run: | echo "En groucho: cd /data/tcef-staging && docker compose pull web && docker compose up -d web"