ci(web): run the server suite before building the image

The build job now needs: test, so an image cannot be published with the suite in
red — which is the whole point of this phase. Both jobs live in the same file
because needs: only links jobs within one workflow.

settings-ci.json is committed and contains no secrets: settings-development.json
is gitignored, so CI had nothing to pass to --settings. The one test that cannot
work without a secret is the node-red iron fixture, sealed with the deployment's
own ironPassword; it now reports itself as skipped instead of failing on an hmac
mismatch, and still runs locally against settings-development.json.

Measured locally: 22s and a ~2.0 GB peak for the whole `meteor test` run (its own
mongod included), hence --memory=3g. The meteor-tool download is not cached yet;
the note in the workflow says what that would take.
This commit is contained in:
vjrj 2026-08-01 18:02:31 +02:00
parent e61efc681c
commit 3b04ebceb3
4 changed files with 174 additions and 3 deletions

View file

@ -16,16 +16,112 @@
# 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:`.