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.
207 lines
9.1 KiB
YAML
207 lines
9.1 KiB
YAML
# 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"
|