takana: el worker pasa a /opt/takana y takana-farm.service

Renombrado el directorio del worker y la unit, con las tres units del repo
(farm, deadman service y timer). Todas las rutas /opt/hammer del hub pasan a
/opt/takana: eran defaults REMOTE= y HAMMER_DIR=, mas los systemctl/journalctl
que nombraban la unit sin el sufijo .service, que el primer sed no casaba.

Orden, para que no quedara partido: el hub siembra por rsync a una ruta fija,
asi que se saco el cron ANTES de mover. Si se movia el worker con el hub
apuntando a la ruta vieja, la siguiente cosecha recreaba /opt/hammer y quedaban
dos arboles.

Verificado de punta a punta:
- el worker cerro un ciclo de build REAL desde /opt/takana (dunst sellado, 1/1)
- una cosecha completa del hub contra la ruta nueva: siembra, manifiesto, los
  nueve grafos, static-audit (691 estaticos, MIENTEN 0) y estado pusheado
- la unit vieja quedo deshabilitada y borrada; /opt tiene una sola entrada

NO se tocan dos referencias a /opt/hammer que siguen siendo CORRECTAS:
docs/23-plan-rehasheo.md describe rutas EMBEBIDAS en artefactos ya construidos
—que literalmente dicen /opt/hammer/work en sus secciones .debug— y el HANDOFF
de la noche de KDE es registro. Cambiarlas haria que los documentos mientan.
This commit is contained in:
Sergio
2026-09-09 20:20:16 +00:00
parent c423abadea
commit aacee245d6
25 changed files with 65 additions and 65 deletions
+4 -4
View File
@@ -93,7 +93,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
- ADRs y docs de diseño: texto, y `hammer` sigue funcionando. Van con la etapa 5.
**Cómo converge el worker** (medido el 2026-09-09, no supuesto). Su checkout vive en
`/opt/hammer`, **no es un clon git** —lo pone el rsync de la siembra— y `hammer-farm.service`
`/opt/hammer`, **no es un clon git** —lo pone el rsync de la siembra— y `takana-farm.service`
está `enabled` allá, corriendo `farm-worker-loop.sh` como servicio largo. En el momento del
cambio el worker tenía scripts viejos y binario del 6-sep: **coherente**. Los dos estados
intermedios también lo son, y por eso la 3a iba primero:
@@ -105,7 +105,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
La unit apunta a la RUTA DEL SCRIPT, no al binario, así que nada de la etapa 3 la toca. El
nombre del fichero de unit y `/opt/hammer` son etapa 6.
**Convergido y comprobado (2026-09-09 18:4018:45Z).** Reiniciado `hammer-farm.service`, el
**Convergido y comprobado (2026-09-09 18:4018:45Z).** Reiniciado `takana-farm.service`, el
worker compiló los dos binarios y **cerró un ciclo de build real con el nombre nuevo**:
`✓ dunst b3:29a79859…`, BUILD-YIELD 1/1, 1 promovido. No es que «no se rompió nada visible»: el
camino de construir y sellar se ejercitó entero.
@@ -197,7 +197,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
reescribir historia es peor que un mensaje incompleto. El detalle vive acá.)*
**Congelados y verificados con controles:** `/opt/hammer`, `/var/lib/hammer`, `/usr/bin/hammer`,
`/mnt/vvv/hammer`, la URL de gitea, `hammer-farm.service`, `hammer-live-install.sh`,
`/mnt/vvv/hammer`, la URL de gitea, `takana-farm.service`, `hammer-live-install.sh`,
`BRIEFING-hammer.md`, `hammerd`, `hammer-recover`, `HAMMER_LIVE`, `.hammer-zig-cc`,
`.hammer-cargo-vendor` y dos identificadores más que aparecieron al barrer:
- `hammer-build-state/1` — el `schema` del estado generado. Nadie lo valida, pero el valor de un
@@ -236,7 +236,7 @@ simplemente deja de cosechar. El directorio es lo **último** que se toca, y con
- 🔒 **`/usr/bin/hammer` dentro del rootfs del producto** — está **dentro de un árbol que se
hashea**. Moverlo cambia el hash del producto y obliga a rehacer el baseline del selfhost.
Va junto con esa deuda, no antes.
-**`hammer-farm.service` y `/opt/hammer`** — despliegue coordinado en el worker: parar,
-**`takana-farm.service` y `/opt/hammer`** — despliegue coordinado en el worker: parar,
renombrar, `daemon-reload`, arrancar. Reversible, pero es hacia afuera.
-**`/mnt/vvv/hammer``/mnt/vvv/takana`, y el repo renombrado en gitea a `sergio/takana`**
(2026-09-09 ~20:00Z). Se coordinó primero: se avisó a las dos sesiones `hammer-*` y `hammer-9f`
+2 -2
View File
@@ -54,8 +54,8 @@ while [ "$i" -le "$N" ]; do
for _ in $(seq 1 30); do $SSH "root@$ip" true 2>/dev/null && break; sleep 6; done
rsync -az -e "$SSH" --exclude /work --exclude /store --exclude '/store-*' --exclude /target \
--exclude /dist --exclude /tandas --exclude /.dev-fs --exclude /.git --exclude /.scratch \
--exclude '*.png' --exclude '/content*' ./ "root@$ip:/opt/hammer/" >/dev/null 2>&1
hN=$($SSH "root@$ip" "export PATH=\$HOME/.cargo/bin:\$PATH; cd /opt/hammer && \
--exclude '*.png' --exclude '/content*' ./ "root@$ip:/opt/takana/" >/dev/null 2>&1
hN=$($SSH "root@$ip" "export PATH=\$HOME/.cargo/bin:\$PATH; cd /opt/takana && \
cargo build --release -q -p takana-cli 2>/dev/null; \
timeout ${POR:-900} ./target/release/takana build $RECETA --store \$PWD/store 2>/dev/null \
| grep -oE '^b3:[a-f0-9]{64}' | head -1" 2>/dev/null | tr -d '\r')
+3 -3
View File
@@ -21,11 +21,11 @@
# Uso (en el worker): scripts/farm/campana-deuda.sh <receta> [receta…]
# DEUDA="glib cairo" scripts/farm/campana-deuda.sh
# Lanzarlo desde el hub para que sobreviva al cierre del SSH:
# ssh root@<ip> 'systemd-run --unit=campana-deuda --working-directory=/opt/hammer \
# /opt/hammer/scripts/farm/campana-deuda.sh <recetas…>'
# ssh root@<ip> 'systemd-run --unit=campana-deuda --working-directory=/opt/takana \
# /opt/takana/scripts/farm/campana-deuda.sh <recetas…>'
# ssh root@<ip> 'journalctl -u campana-deuda -f'
set -u
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
# Ruta absoluta a MÍ MISMO antes del `cd`: acá el `cd` va a HAMMER_DIR, que ni siquiera tiene por
# qué contener a este script, así que un `$0` relativo se pierde seguro.
YO="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
+3 -3
View File
@@ -21,7 +21,7 @@
#
# Uso: scripts/farm/cosecha-cron.sh # un ciclo (lo que dispara el cron)
# */30 * * * * cd /home/sergio/takana && scripts/farm/cosecha-cron.sh >>work/cosecha-cron.log 2>&1
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer), NO_COMMIT=1 (regenera pero no commitea)
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana), NO_COMMIT=1 (regenera pero no commitea)
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
@@ -62,7 +62,7 @@ if [ -z "${COSECHA_CRON_CON_LOCK:-}" ]; then
fi
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
FLEET="$ROOT/scripts/farm/.fleet"
# ── SIEMBRA DE LA FLOTA FIJA ────────────────────────────────────────────────────────────────────
# `.fleet` es estado de EJECUCIÓN (gitignored, lo reescribe el reaper cada ciclo); `flota-permanente`
@@ -156,7 +156,7 @@ else
fi
# 2.5 REAPER HUB-SIDE (2026-07-23). El dead-man DEL WORKER probó FRÁGIL: un worker quedó 3.5h idle
# quemando € porque su `/etc/hammer-deadman.env` no tenía token válido ⇒ el dead-man llegaba a
# quemando € porque su `/etc/takana-deadman.env` no tenía token válido ⇒ el dead-man llegaba a
# matarse pero salía 1 "sin HCLOUD_TOKEN" cada tick. La garantía "un vps idle es inadmisible" NO
# puede depender de que cada worker se auto-provisione bien un token (falla en silencio). El HUB sí
# tiene un token que funciona, y este latido corre cada 30 min aunque no haya sesión (setsid) ⇒ el
+7 -7
View File
@@ -47,15 +47,15 @@
set -eu
IDLE_MAX_TICKS="${IDLE_MAX_TICKS:-6}" # 6 ticks × 10 min = 1 h sin trabajo → borrar
ESTADO="/var/lib/hammer-deadman.ticks"
ESTADO="/var/lib/takana-deadman.ticks"
HEARTBEAT="/run/hammer-heartbeat"
HEARTBEAT_MAX_AGE=1800 # 30 min: más viejo que esto no cuenta como vivo
LOG="/var/log/hammer-deadman.log"
LOG="/var/log/takana-deadman.log"
log() { echo "$(date -u +%FT%TZ) deadman: $*" >> "$LOG"; }
# TOKEN ROBUSTO A CADA CAMINO DE CREACIÓN (incidente 2026-07-23). Hay DOS caminos que crean workers y
# ponen el token en ficheros DISTINTOS: `farm-up` → /etc/hammer-deadman.env (EnvironmentFile de la
# ponen el token en ficheros DISTINTOS: `farm-up` → /etc/takana-deadman.env (EnvironmentFile de la
# service); `harkaq-vol.sh` → /root/.hcloud-token (cloud-init). La service del snapshot lee sólo el
# primero ⇒ un worker nacido por el OTRO camino quedaba SIN token en su env, disparaba pero salía 1
# "sin HCLOUD_TOKEN" y NUNCA se borraba (3.5h idle quemando €). El dead-man NO puede depender del hub
@@ -65,8 +65,8 @@ log() { echo "$(date -u +%FT%TZ) deadman: $*" >> "$LOG"; }
# La service lo recibe vía EnvironmentFile, pero corrido A MANO (farm-up --puede-borrar) ese fichero
# NO se carga solo ⇒ hay que sourcearlo, o la verificación da falso-negativo y destruye un worker que
# SÍ puede matarse (lo cazó el 1er worker real, 2026-07-23). Es formato KEY=value.
if [ -z "$HCLOUD_TOKEN" ] && [ -r /etc/hammer-deadman.env ]; then
. /etc/hammer-deadman.env 2>/dev/null || true
if [ -z "$HCLOUD_TOKEN" ] && [ -r /etc/takana-deadman.env ]; then
. /etc/takana-deadman.env 2>/dev/null || true
: "${HCLOUD_TOKEN:=}"
fi
# Y ficheros de token DESNUDO (otros caminos de creación): el volumen primero, que es lo persistente.
@@ -92,8 +92,8 @@ fi
if [ "${1:-}" = "--verificar" ]; then
echo "══ ¿el dead-man switch está REALMENTE armado?"
echo "── timer:"; systemctl is-active hammer-deadman.timer 2>/dev/null || echo " INACTIVO ⚠"
systemctl list-timers hammer-deadman.timer --no-pager 2>/dev/null | head -3
echo "── timer:"; systemctl is-active takana-deadman.timer 2>/dev/null || echo " INACTIVO ⚠"
systemctl list-timers takana-deadman.timer --no-pager 2>/dev/null | head -3
echo "── ticks idle acumulados: $(cat "$ESTADO" 2>/dev/null || echo 0) / $IDLE_MAX_TICKS"
echo "── últimas líneas del log (evidencia de que DISPARA):"
tail -5 "$LOG" 2>/dev/null | sed 's/^/ /' || echo " (sin log todavía ⇒ NUNCA disparó)"
+1 -1
View File
@@ -15,7 +15,7 @@ if [ -s "$FLEET" ]; then
echo "$name ($ip)"
$SSH "root@$ip" '
echo " $(uptime | sed "s/.*load average/load/")"
n=$(ls /opt/hammer/store 2>/dev/null | wc -l); echo " store: $n artefactos"
n=$(ls /opt/takana/store 2>/dev/null | wc -l); echo " store: $n artefactos"
b=$(pgrep -af "release/hammer" | grep -E "release/hammer (build|--store|hash)" | grep -oE "[^ /]+\.toml" | sort -u | tr "\n" " ")
echo " moliendo: ${b:-(nada — idle o entre colas)}"
' 2>/dev/null || echo " ⚠ sin respuesta (¿dead-man lo mató? ¿aún arrancando?)"
+2 -2
View File
@@ -9,11 +9,11 @@
#
# Uso: scripts/farm/farm-down.sh # cosecha + destruye toda la flota
# scripts/farm/farm-down.sh hworker-2 # sólo ese
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer), NO_PROMOTE=1 (sólo cosecha+destruye)
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana), NO_PROMOTE=1 (sólo cosecha+destruye)
set -uo pipefail
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
FLEET="$ROOT/scripts/farm/.fleet"
# Ver nota en farm-up.sh: workers efímeros reciclan IPs ⇒ known_hosts a /dev/null (hub-and-spoke,
+1 -1
View File
@@ -26,7 +26,7 @@ set -euo pipefail
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
IP="${1:?falta la IP del worker}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
IMG="$ROOT/.dev-fs/lab-image.tar.zst"
TESTIGO="${TESTIGO:-recipes/zlib.toml}"
+1 -1
View File
@@ -31,7 +31,7 @@ FLEET="$ROOT/scripts/farm/.fleet"
SSH="ssh -i $SSH_KEY -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o LogLevel=ERROR -o ConnectTimeout=10"
cd "$ROOT"
cycles() { $SSH "root@$1" 'journalctl -u hammer-farm -o cat --no-pager 2>/dev/null | grep -c "ciclo terminado"' 2>/dev/null || echo 0; }
cycles() { $SSH "root@$1" 'journalctl -u takana-farm -o cat --no-pager 2>/dev/null | grep -c "ciclo terminado"' 2>/dev/null || echo 0; }
echo "==> [farm-run] levantando $N worker(s) y alineando la cola…"
scripts/farm/farm-up.sh "$N"
+2 -2
View File
@@ -9,12 +9,12 @@
#
# Uso: scripts/farm/farm-sync.sh root@1.2.3.4
# scripts/farm/farm-sync.sh root@1.2.3.4 --no-promote # sólo sincroniza, no firma
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer)
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana)
set -euo pipefail
VPS="${1:?uso: farm-sync.sh user@host [--no-promote]}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new"
+10 -10
View File
@@ -15,7 +15,7 @@
# Env: IMAGE (snapshot, def 417847948=takana-golden-rust-go-2026-08-08), TYPE (def ccx23),
# LOCATION
# (def hel1), SSHKEY (llave del proyecto Hetzner, def desarrollo@jlsoltech.com), SSH_KEY
# (privada local p/ SSH, def ~/.ssh/github5), REMOTE (def /opt/hammer).
# (privada local p/ SSH, def ~/.ssh/github5), REMOTE (def /opt/takana).
set -euo pipefail
N="${1:-1}"
@@ -40,7 +40,7 @@ TYPE="${TYPE:-ccx23}"
LOCATION="${LOCATION:-hel1}"
SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
# MISMO volumen que harkaq-vol.sh, a propósito: eran DOS caminos de crear workers y sólo uno tenía
# volumen+automuerte. hworker-4 nació por ÉSTE (farm-up) y por eso quedó 5 días idle con 37G que
# nadie salvó — la protección existía y este camino la esquivaba. Compartir el volumen hace que
@@ -141,7 +141,7 @@ for k in $(seq 1 "$N"); do
# mientras eso no se reordene, esto garantiza al menos que se llegue a armarlo.
[ "$rc" -ne 0 ] && echo " ⚠ rsync de código salió $rc — el worker puede tener la cola incompleta; sigo para ARMAR EL DEAD-MAN"
# el servicio ya arrancó en el boot (baked/enabled); reiniciar fuerza rescan inmediato de la cola
$SSH root@"$ip" 'systemctl restart hammer-farm' 2>/dev/null || true
$SSH root@"$ip" 'systemctl restart takana-farm' 2>/dev/null || true
# VOLUMEN PERSISTENTE + STORE DENTRO. Sin esto la automuerte perdería lo construido: hworker-4
# tenía 438 artefactos (37G) SÓLO en su disco local. El store vive en /mnt/cosecha/store ⇒ cada
@@ -188,9 +188,9 @@ for k in $(seq 1 "$N"); do
# fusionar es seguro por construcción: \`mv -n\` no pisa lo que el volumen ya tiene.
# ⚠ mountpoint -q NO ES OPCIONAL — sin él este bloque BORRA EL VOLUMEN.
# La guarda original sólo comprobaba «es directorio y no es symlink». Eso basta en un server
# recién creado desde la golden ORIGINAL, donde /opt/hammer/store es un directorio normal.
# recién creado desde la golden ORIGINAL, donde /opt/takana/store es un directorio normal.
# Pero al re-snapshotear un worker (2026-08-08) la imagen se llevó también la CONFIGURACIÓN DE
# MONTAJE: el server nuevo monta el volumen en /opt/hammer/store al arrancar, ANTES de que
# MONTAJE: el server nuevo monta el volumen en /opt/takana/store al arrancar, ANTES de que
# corra este rescate. Entonces rm -rf $REMOTE/store borra A TRAVÉS DEL MONTAJE y se lleva el
# store del volumen entero — que es justo lo que el volumen existe para proteger.
# Medido: el volumen pasó de ~900 artefactos a 2. No se perdió el filesystem (lost+found es de
@@ -210,7 +210,7 @@ for k in $(seq 1 "$N"); do
# el store resuelve a /mnt/cosecha/store ⇒ busca /mnt/cosecha/.dev-fs, que no existe, y muere
# con 'no encuentro el ejecutable zig en …' apuntando al lugar equivocado. Un bind-mount deja
# los datos en el volumen igual, pero \$REMOTE/store sigue siendo una ruta REAL bajo
# \$REMOTE ⇒ el padre vuelve a ser /opt/hammer y .dev-fs se encuentra. Mantiene las dos
# \$REMOTE ⇒ el padre vuelve a ser /opt/takana y .dev-fs se encuentra. Mantiene las dos
# invariantes a la vez: sellar = persistir, y el lab donde takana lo espera.
if [ -L $REMOTE/store ]; then rm -f $REMOTE/store; fi
mkdir -p $REMOTE/store
@@ -239,11 +239,11 @@ for k in $(seq 1 "$N"); do
scp -q -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
scripts/farm/deadman.sh "root@$ip:/usr/local/bin/deadman.sh" 2>/dev/null
scp -q -i "$SSH_KEY" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null \
scripts/farm/hammer-deadman.service scripts/farm/hammer-deadman.timer \
scripts/farm/takana-deadman.service scripts/farm/takana-deadman.timer \
"root@$ip:/etc/systemd/system/" 2>/dev/null
$SSH root@"$ip" "chmod +x /usr/local/bin/deadman.sh
printf 'HCLOUD_TOKEN=%s\n' '$tok' > /etc/hammer-deadman.env; chmod 600 /etc/hammer-deadman.env
systemctl daemon-reload && systemctl enable --now hammer-deadman.timer" >/dev/null 2>&1
printf 'HCLOUD_TOKEN=%s\n' '$tok' > /etc/takana-deadman.env; chmod 600 /etc/takana-deadman.env
systemctl daemon-reload && systemctl enable --now takana-deadman.timer" >/dev/null 2>&1
# EVIDENCIA, no fe: que el timer esté ARMADO se comprueba, no se asume (el cron de aquella vez
# estaba "validado" y nunca disparó porque el PID 1 no tenía crond).
# FIRING ≠ CAN-DELETE (2026-07-23): un worker quedó idle 3.5h quemando € porque su token no
@@ -252,7 +252,7 @@ for k in $(seq 1 "$N"); do
# SOLO, sin el hub — así que si NO PUEDE, no lo dejamos vivo: lo DESTRUIMOS acá mismo. Un worker
# que no se puede autodestruir es exactamente lo que el usuario prohíbe. `deadman.sh --puede-borrar`
# busca el token en cadena (env, volumen, /root) y prueba que ve su id en la API.
timer_ok=$($SSH root@"$ip" 'systemctl is-active hammer-deadman.timer' 2>/dev/null | grep -q active && echo 1)
timer_ok=$($SSH root@"$ip" 'systemctl is-active takana-deadman.timer' 2>/dev/null | grep -q active && echo 1)
puede=$($SSH root@"$ip" '/usr/local/bin/deadman.sh --puede-borrar' 2>/dev/null)
if [ -n "$timer_ok" ] && printf '%s' "$puede" | grep -q '^SÍ'; then
echo " ✓ dead-man ARMADO y PUEDE borrarse solo ($puede) — no depende del hub"
+2 -2
View File
@@ -7,9 +7,9 @@
# Idempotente: cada ciclo re-escanea incoming/; lo ya-sellado sale por cache-hit (instantáneo),
# sólo construye lo nuevo. Cuando la cola está toda construida, duerme IDLE_SLEEP y reintenta.
#
# Lo lanza systemd (hammer-farm.service). Manual: JOBS=2 ./scripts/farm/farm-worker-loop.sh
# Lo lanza systemd (takana-farm.service). Manual: JOBS=2 ./scripts/farm/farm-worker-loop.sh
set -uo pipefail
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
cd "$HAMMER_DIR"
. "$HOME/.cargo/env" 2>/dev/null || true
# JOBS=1 MIENTRAS EL ADR 0012 ESTÉ SIN DECIDIR (2026-07-22). `build-farm.sh` usa `xargs -P$JOBS`, y
+1 -1
View File
@@ -10,7 +10,7 @@
set -u
MI_NOMBRE="${1:?uso: harkaq-campana-vol.sh <mi-nombre>}"
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
cd "$HAMMER_DIR"
LISTA="${LISTA:-work/harkaq-cola.txt}"
VOL_OUT="/mnt/cosecha"
+1 -1
View File
@@ -18,7 +18,7 @@
# script existe para no tener.
set -u
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
cd "$HAMMER_DIR"
LISTA="${1:-work/harkaq-cola.txt}"
OUT="work/harkaq-campana"
+1 -1
View File
@@ -30,7 +30,7 @@ HELPER_TYPE="${HELPER_TYPE:-ccx13}" # para collect: el más barato que
LOCATION="${LOCATION:-hel1}"
SSHKEY="${SSHKEY:-desarrollo@jlsoltech.com}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
IDLE_CICLOS="${IDLE_CICLOS:-3}" # ciclos idle antes de auto-eliminarse
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
+2 -2
View File
@@ -16,7 +16,7 @@
# Todo se loguea a work/harvest-go.log para revisar el lunes.
#
# Uso (cron): */DohubVPS cd /home/sergio/takana && scripts/farm/harvest-go.sh root@1.2.3.4 >>work/harvest-go.log 2>&1
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer), STORE (def ./store), REPO (def dist/repo),
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/takana), STORE (def ./store), REPO (def dist/repo),
# KEY (def dist/keys/release.ed25519), DISTRO (def dev), HAMMER (def target/release/takana),
# SEAL_TIMEOUT (def 60s: techo del cache-hit; más = sería build local ⇒ skip).
set -u
@@ -24,7 +24,7 @@ set -u
VPS="${1:?uso: harvest-go.sh user@host}"
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
STORE="${STORE:-$ROOT/store}"
REPO="${REPO:-$ROOT/dist/repo}"
KEY="${KEY:-$ROOT/dist/keys/release.ed25519}"
+1 -1
View File
@@ -49,7 +49,7 @@ if [ -z "${HARVEST_HARKAQ_CON_LOCK:-}" ]; then
exit "$rc"
fi
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
REVIEW="tandas/needs-review-harkaq"
mkdir -p "$REVIEW" work
@@ -1,10 +1,10 @@
[Unit]
Description=hammer: tick del dead-man switch (borra el worker tras 1h idle)
# El token se lee de /etc/hammer-deadman.env (0600, lo escribe farm-up al provisionar). Sin él el
# El token se lee de /etc/takana-deadman.env (0600, lo escribe farm-up al provisionar). Sin él el
# script LOGUEA EL ERROR y sale 1: no hace un poweroff "por si acaso", porque un server apagado en
# Hetzner SIGUE COBRANDO y daría una falsa sensación de ahorro.
[Service]
Type=oneshot
EnvironmentFile=-/etc/hammer-deadman.env
EnvironmentFile=-/etc/takana-deadman.env
ExecStart=/usr/local/bin/deadman.sh
@@ -1,5 +1,5 @@
[Unit]
Description=hammer build farm worker (loop autónomo)
Description=takana build farm worker (loop autónomo)
After=network-online.target
Wants=network-online.target
# ── FRENO AL CRASHLOOP (2026-08-31) ──────────────────────────────────────────────────────────────
@@ -18,7 +18,7 @@ StartLimitBurst=3
[Service]
Type=simple
WorkingDirectory=/opt/hammer
WorkingDirectory=/opt/takana
Environment=HOME=/root
# ADR 0016: se fijan LAS DOS mientras dure el renombre. Los scripts leen
# ${TAKANA_DIR:-${HAMMER_DIR:-…}}, pero un script VIEJO —o una copia del repo
@@ -27,12 +27,12 @@ Environment=HOME=/root
# es lo que hoy impide retirar la caída. Cuando el worker corra con TAKANA_DIR
# y el resto de la flota también, se borra la segunda línea y recién ahí se
# puede retirar la caída del código (etapa 6).
Environment=TAKANA_DIR=/opt/hammer
Environment=HAMMER_DIR=/opt/hammer
Environment=TAKANA_DIR=/opt/takana
Environment=HAMMER_DIR=/opt/takana
# JOBS no se fija: el loop usa nproc por defecto (cada build usa 1 core por codegen-units=1).
# Así, al REPOTENCIAR la caja (CCX13→CCX23/33), el worker auto-escala los slots sin tocar config.
# La línea CCX da ~4GB RAM/core ⇒ nproc slots quedan con margen para builds Rust + swap.
ExecStart=/opt/hammer/scripts/farm/farm-worker-loop.sh
ExecStart=/opt/takana/scripts/farm/farm-worker-loop.sh
Restart=always
RestartSec=30
# El OOM del worker NO se reintenta como si fuera un fallo transitorio: si la receta no entra en la
+3 -3
View File
@@ -17,11 +17,11 @@
# Uso (en el worker):
# scripts/farm/tandas.sh "gui:pixman,cairo,pango" "kernels:linux,linux-generic"
# Desde el hub:
# ssh root@<ip> 'systemd-run --unit=tandas --working-directory=/opt/hammer \
# /opt/hammer/scripts/farm/tandas.sh "gui:pixman,cairo" …'
# ssh root@<ip> 'systemd-run --unit=tandas --working-directory=/opt/takana \
# /opt/takana/scripts/farm/tandas.sh "gui:pixman,cairo" …'
# ssh root@<ip> 'tail -f /var/log/tandas.log'
set -u
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
cd "$HAMMER_DIR"
LOG="${LOG:-/var/log/tandas.log}"
ESPERA="${ESPERA:-30}"
+7 -7
View File
@@ -23,7 +23,7 @@
# falla se avisa RUIDOSO, porque sin swap un build Rust grande no se pone lento:
# lo mata el OOM killer.
#
# Uso (en el VPS, como root): HAMMER_DIR=/opt/hammer ./scripts/farm/vps-setup.sh
# Uso (en el VPS, como root): HAMMER_DIR=/opt/takana ./scripts/farm/vps-setup.sh
#
# ── CAJA FRESCA (no golden): DOS PASOS DEL HUB ANTES DE ESTO ────────────────────────────────
# La imagen golden de Hetzner ya trae rsync y el lab horneados. Una caja virgen, no:
@@ -35,13 +35,13 @@
# tira del Storage Box y eso exige credenciales — que el worker NO debe tener (modelo
# hub-and-spoke: compute puro, sin secretos). El hub le manda el tarball ya verificado
# y el worker lo re-verifica por sha256 al extraerlo:
# rsync -a .dev-fs/lab-image.tar.zst caja:/opt/hammer/.dev-fs/lab-<SHA>.tar.zst
# rsync -a .dev-fs/tools/ caja:/opt/hammer/.dev-fs/tools/
# rsync -a .dev-fs/lab-image.tar.zst caja:/opt/takana/.dev-fs/lab-<SHA>.tar.zst
# rsync -a .dev-fs/tools/ caja:/opt/takana/.dev-fs/tools/
# Mandar `tools/` entero (zig 0.13.0 + 0.16.0 + go) y no dejar que se descarguen solos
# evita el zig-skew: el worker usa EXACTAMENTE las versiones del hub.
set -euo pipefail
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/hammer}}"
HAMMER_DIR="${TAKANA_DIR:-${HAMMER_DIR:-/opt/takana}}"
cd "$HAMMER_DIR"
# ── detección de familia ────────────────────────────────────────────────────────────────────
@@ -144,9 +144,9 @@ echo "==> 6. rootfs Alpine edge + zig (bootstrap reproducible; varios min la 1ra
./scripts/bootstrap-devfs.sh
echo "==> 7. systemd unit del loop"
cp scripts/farm/hammer-farm.service /etc/systemd/system/hammer-farm.service
cp scripts/farm/takana-farm.service /etc/systemd/system/takana-farm.service
systemctl daemon-reload
echo "✓ worker provisionado."
echo " arrancar: systemctl enable --now hammer-farm"
echo " logs: journalctl -u hammer-farm -f"
echo " arrancar: systemctl enable --now takana-farm"
echo " logs: journalctl -u takana-farm -f"
+1 -1
View File
@@ -18,7 +18,7 @@
# DENTRO de una misma corrida: con un rsync por directorio, cada artefacto viaja como si fuera único
# y el buzón llega inflado. Una sola invocación con la lista completa los ve todos.
set -uo pipefail
STORE="${STORE:-/opt/hammer/store}"
STORE="${STORE:-/opt/takana/store}"
KEY="${KEY:-/root/.ssh/sb_sub}"
RO_HOST="${RO_HOST:-u647150-sub1.your-storagebox.de}"; RO_USER="${RO_USER:-u647150-sub1}"
BZ_HOST="${BZ_HOST:-u647150-sub2.your-storagebox.de}"; BZ_USER="${BZ_USER:-u647150-sub2}"
+1 -1
View File
@@ -11,7 +11,7 @@ set -eu
IP="${1:?uso: harkaq-farm-setup.sh <ip>}"
KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
REMOTE="${REMOTE:-/opt/takana}"
SSH="ssh -i $KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o BatchMode=yes"
HERE="$(cd "$(dirname "$0")" && pwd)"
+2 -2
View File
@@ -153,9 +153,9 @@ def _base(n):
# El primer predictor "obvio" (prefijo del hub) mentía con confianza — la lección de siempre.
PAGINA_OVERLAY = 4096 # tope del string de opciones de un mount overlayfs = PAGE_SIZE
# Prefijo que bwrap ve en runtime (bajo /oldroot, ruta del worker). Ajustable por host.
_PREF_SANDBOX = os.environ.get("PREF_STORE_SANDBOX", "/oldroot/opt/hammer/store/")
_PREF_SANDBOX = os.environ.get("PREF_STORE_SANDBOX", "/oldroot/opt/takana/store/")
_OVERHEAD_OVERLAY = len("upperdir=/tmp-overlay-upper-0,workdir=/tmp-overlay-work-0,") \
+ len("/oldroot/opt/hammer/.dev-fs/alpine") + len(",userxattr")
+ len("/oldroot/opt/takana/.dev-fs/alpine") + len(",userxattr")
def _lowerdir_estimado(deps):