Sin tocar gioser (todo lectura) y en la VM desechable. Dentro de la VM: `<title>GioSer Gitea: Git with a cup of tea</title>`, 200, `/explore/repos` listando los repos de verdad (sergio/takana, tawasuyu/agora, card, chasqui, cosmos, khipu, llimphi…) y el clon de uno de ellos con 478 commits y HEAD correcto. El snapshot de la DB se saca EN CALIENTE y sale consistente: `sqlite3 gitea.db ".backup …"` con el servidor vivo, 1,5 s para 314 M, `integrity_check` ok, 44 filas en `repository`. Copiar el fichero a pelo mientras el servidor escribe es justo lo que no hay que hacer. Tres detalles que sólo aparecen con los datos puestos: · **El uid del origen no es el del destino.** Los ficheros llegan con su uid NUMÉRICO y el gitea de gioser es 969, no el 916 que declaraba la receta. O se chownean 2 G —lento, y hay que acordarse— o gitea no puede leer sus datos, y eso no falla al copiar: falla al arrancar. La receta pasa a 969, alineada con el origen. El ArtifactHash no se mueve (`[[user]]` está fuera de hash_inputs). · El `app.ini` de gioser escucha en `127.0.0.1:3002` porque allá caddy hace de proxy: la caja sirve, pero sólo desde dentro. `HTTP_ADDR`/`ROOT_URL` son adaptación, no copiado. · 🧨 **El `git` de la distro no puede clonar por HTTP**: `git: 'remote-http' is not a git command`. Medido sobre el artefacto sellado: `git-core/` trae `git-remote-ext`, `git-remote-fd` y `git-http-backend` (el lado SERVIDOR) y no `git-remote-http`; `strings` da 0 referencias a libcurl pese a que `curl` está en `[deps] build`. No se había notado porque en el hub se usa el git de Artix, no el sellado. Un hub nuevo no podría clonar el repo por HTTPS. Re-sellar `git` es raíz de `perfil.base` ⇒ unidad propia. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
145 lines
9.6 KiB
TOML
145 lines
9.6 KiB
TOML
# gitea — el servidor git que hoy corre en gioser y que la mudanza tiene que levantar del otro lado.
|
|
#
|
|
# ══ POR QUÉ EL TARBALL DE RELEASE Y NO `repo` + `commit` ═══════════════════════════════════════
|
|
# El clon del repo NO alcanza para construir un gitea que sirva: la UI se compila con Node
|
|
# (`make frontend` ⇒ `public/assets/`) y las deps Go no están vendoreadas en git. El tarball
|
|
# `gitea-src-<ver>.tar.gz` que publica upstream trae las dos cosas ya hechas — medido sobre el de
|
|
# 1.27.0: `public/assets/` 764 entradas, `vendor/` 11246, `templates/` 663 — así que se construye
|
|
# con el lab tal cual, sin Node y sin red (`GOFLAGS=-mod=vendor GOPROXY=off`). Es también lo que
|
|
# hacen Alpine y nixpkgs. La URL es locator y no entra en `hash_inputs` (ADR 0013): lo que ancla
|
|
# la identidad es el `sha256`.
|
|
#
|
|
# ══ LOS TRES TAGS, Y POR QUÉ SIN ELLOS EL BINARIO SELLA Y NO SIRVE ═════════════════════════════
|
|
# La receta anterior (1.26.4, sin tags, CGO off) producía un gitea que construye, reproduce y
|
|
# **no puede abrir la base de datos**. Medido contra el binario sellado `b3:35bb4f04…`:
|
|
#
|
|
# $ gitea --config app.ini migrate
|
|
# Error: sqlite3 requires: -tags sqlite,sqlite_unlock_notify
|
|
# this Gitea binary was not built with SQLite3 support
|
|
#
|
|
# y `gitea --version` decía `version development` (sin el `-X main.Version`). gioser guarda sus 28
|
|
# repos con `DB_TYPE = sqlite3`, así que ese artefacto no sirve para la mudanza: es
|
|
# [[subcomando-sin-driver]] otra vez — sellado, con contenido, reproducible e inútil.
|
|
#
|
|
# · `sqlite` + `sqlite_unlock_notify` → el driver de mattn, que es C ⇒ obliga a `cgo = true`.
|
|
# · `bindata` → embebe `public/` y `templates/` en el binario. Sin él el
|
|
# servidor los busca en disco y la receta sólo publica el
|
|
# ejecutable: arrancaría sin UI.
|
|
#
|
|
# ⚠ La versión se PASA por `-ldflags -X`: no se deduce del árbol. Y no es cosmética — gitea migra
|
|
# el esquema hacia adelante al arrancar y se planta si la DB trae migraciones más nuevas que el
|
|
# binario, así que el pin tiene que ir por delante del que corre en el origen (gioser: 1.27.0).
|
|
name = "gitea"
|
|
version = "1.27.0"
|
|
license = "MIT"
|
|
|
|
[source]
|
|
tarball = "https://github.com/go-gitea/gitea/releases/download/v1.27.0/gitea-src-1.27.0.tar.gz"
|
|
sha256 = "012df875bfa7764ade92301ac5e4225fc2c2aab7b3b40b4d6e7149a926253496"
|
|
|
|
[build]
|
|
compiler = "zig-cc"
|
|
target = "x86_64-linux-musl"
|
|
link = "static"
|
|
flags = []
|
|
cgo = true # driver sqlite3 (mattn) con C bundled — necesita cgo, igual que usql/sq
|
|
|
|
[deps]
|
|
build = ["go"]
|
|
|
|
[build.phases]
|
|
configure = "true"
|
|
compile = """
|
|
export GOCACHE=/src/.gocache GOTMPDIR=/src/.gotmp GOPATH=/tmp/gopath GOTOOLCHAIN=local \
|
|
CGO_ENABLED=1 CC="zig cc -mcpu=baseline" CXX="zig c++ -mcpu=baseline" GOFLAGS=-mod=vendor GOPROXY=off
|
|
mkdir -p /src/.gotmp /out/usr/bin
|
|
go build -trimpath -tags 'bindata sqlite sqlite_unlock_notify' \
|
|
-ldflags '-buildid= -linkmode=external -extldflags=-static -X "main.Version=1.27.0" -X "main.Tags=bindata sqlite sqlite_unlock_notify"' \
|
|
-o /out/usr/bin/gitea .
|
|
"""
|
|
install = "true"
|
|
|
|
# ── LA CUENTA QUE ESTE PAQUETE NECESITA (SDD 30) ────────────────────────────────────────────────
|
|
# Fuera de `hash_inputs`: declarar esto NO re-hashea gitea.
|
|
#
|
|
# El `uid` se DECLARA y no se asigna, igual que el ULID de la Card: un «primero libre a partir de
|
|
# 1000» hace que dos imágenes del mismo perfil salgan con dueños distintos y el rootfs deje de
|
|
# reproducir **sin que nada falle** — los ficheros se ven iguales y `ls -l` dice otro número.
|
|
#
|
|
# ⚠ **969 NO es un número arbitrario: es el uid que gitea tiene EN GIOSER**, y alinearlo es lo que
|
|
# hace barata la mudanza. Los 2,0 G de `/var/lib/gitea` llegan con su uid NUMÉRICO puesto; si el
|
|
# destino usara otro, o se chownean 2 G (lento, y hay que acordarse) o gitea no puede leer sus
|
|
# propios datos — y eso no falla al copiar, falla al arrancar. Medido en el ensayo con datos reales
|
|
# (SDD 28 §6.13): los ficheros llegaron como `1001` y hubo que chownear a mano.
|
|
# `shell = /bin/false` por defecto: una cuenta de servicio a la que alguien pueda entrar es una
|
|
# puerta que nadie declaró.
|
|
[[user]]
|
|
name = "gitea"
|
|
uid = 969
|
|
home = "/var/lib/gitea"
|
|
|
|
# ── EL SERVICIO QUE ESTE PAQUETE TRAE (SDD 30) ──────────────────────────────────────────────────
|
|
# Fuera de `hash_inputs`: declarar esto NO re-hashea gitea.
|
|
#
|
|
# ⚠ GITEA SE NIEGA A CORRER COMO ROOT, y no es un aviso: es un `[F]` y sale. Medido:
|
|
#
|
|
# [F] Gitea is not supposed to be run as root. If you need to use privileged TCP ports
|
|
# please instead use `setcap` and the `cap_net_bind_service` permission.
|
|
#
|
|
# Por eso el `argv` hace `setuidgid gitea` (applet de busybox, comprobado presente) en vez de
|
|
# encarnar el binario directo. Existe `GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT`; el nombre de la
|
|
# variable ya dice por qué no se usa.
|
|
#
|
|
# ══ LAS DOS GUARDAS, Y POR QUÉ FALLAN RUIDOSAMENTE ═════════════════════════════════════════════
|
|
# Un servidor git sin su `app.ini` y sin su usuario **arranca igual** y se planta más tarde, o peor:
|
|
# arranca con la config de instalación y ofrece el asistente de «crear administrador» a quien pase.
|
|
# Las dos cosas que faltan son del SITIO, no del paquete —vienen de la mudanza junto con los datos—
|
|
# así que la receta no las inventa: comprueba que estén y muere con un mensaje que las nombra.
|
|
#
|
|
# · `/etc/gitea/app.ini` ⇒ es config del sitio: dice dónde está la DB y con qué dominio se sirve.
|
|
# · el usuario `gitea` ⇒ dueño de `/var/lib/gitea`. **HOY NO EXISTE EN EL PRODUCTO**: el
|
|
# `/etc/passwd` de la imagen es la constante `takana_bootstrap::PRODUCT_PASSWD` y sólo trae
|
|
# `root` y `sshd`. El SDD 30 sacó las Cards de una constante de Rust y las puso en la receta; los
|
|
# USUARIOS siguen donde estaban las Cards. Mientras eso no se declare, el usuario entra con los
|
|
# datos del sitio (el origen ya lo tiene: `gitea:x:…:/var/lib/gitea`).
|
|
#
|
|
# `arje` no tiene readiness por unidad, así que —igual que el card de `sshd`— lo que haya que esperar
|
|
# o comprobar va DENTRO del `argv`, a la vista, y no escondido en un wrapper.
|
|
#
|
|
# ⚠ TODO con ruta ABSOLUTA (`/bin/grep`, `/usr/bin/setuidgid`), y no por estilo: el `sh` de busybox
|
|
# de la imagen NO está compilado con `FEATURE_SH_STANDALONE`, así que **no despacha sus propios
|
|
# applets** — los resuelve por `PATH`, y PID 1 no garantiza ninguno. Medido: `exec setuidgid …` a
|
|
# secas muere con `sh: exec: line 0: setuidgid: not found`, y el servicio quedaría reintentando para
|
|
# siempre por un `PATH` vacío. En la imagen los symlinks existen (`/usr/bin/setuidgid -> busybox`);
|
|
# nombrarlos completos es lo que hace que eso no dependa del entorno que herede el proceso.
|
|
#
|
|
# ══ LOS DOS QUE SÓLO APARECEN CON EL ENTORNO DE VERDAD (medidos 2026-09-14 en el worker) ═══════
|
|
# arje pasa un `envp` EXPLÍCITO y no hereda el del padre. Corriendo la card con `env -i` —que es lo
|
|
# que de verdad va a pasar— salieron dos fallos que con una shell normal no se ven nunca:
|
|
#
|
|
# 1. `PATH` en el `envp`. **gitea lanza `git` como SUBPROCESO** (`git.InitFull`). Sin `PATH` muere
|
|
# con `git not found: executable file not found in $PATH`, que se lee como «falta git» cuando
|
|
# git está instalado y es raíz de `perfil.base`. El envp vacío no es «limpio»: es sin PATH.
|
|
# 2. `cd /var/lib/gitea` en el `argv`. El cwd se HEREDA, y gitea corre `git config` en él: con el
|
|
# cwd del padre el arranque muere con `fatal: error reading '/root/.git'` — un mensaje que
|
|
# habla de git y de root, y no de que a la card le falte un directorio de trabajo. `Service` no
|
|
# tiene campo `cwd`, así que va en el argv, a la vista.
|
|
#
|
|
# ⚠ Y `app.ini` tiene que ser ESCRIBIBLE por `gitea`: si le falta un secreto (p. ej. `JWT_SECRET`)
|
|
# lo genera y lo guarda **en el propio fichero**; con el ini de root muere con `permission denied`.
|
|
# En una instalación mudada ya vienen todos, pero el modo correcto es `gitea:gitea 0660`.
|
|
#
|
|
# ══ VERIFICADO DE PUNTA A PUNTA ════════════════════════════════════════════════════════════════
|
|
# Con este argv exacto y `env -i`: escucha en su puerto, `GET /` responde **200**, y el proceso
|
|
# corre como **gitea**. Las dos guardas probadas por separado: sin `app.ini` ⇒ 78 con su mensaje;
|
|
# con `app.ini` y sin el usuario ⇒ 78 con el suyo.
|
|
[[service]]
|
|
label = "gitea"
|
|
id = "01M2EKDA00NKYGE2BB5DFH1EFF"
|
|
exec = "/bin/busybox"
|
|
argv = ["sh", "-c", "test -f /etc/gitea/app.ini || { echo 'gitea: falta /etc/gitea/app.ini — es config del SITIO, viene con los datos' >&2; exit 78; }; /bin/grep -q '^gitea:' /etc/passwd || { echo 'gitea: no existe el usuario `gitea` en /etc/passwd y gitea NO corre como root' >&2; exit 78; }; cd /var/lib/gitea || exit 78; exec /usr/bin/setuidgid gitea /usr/bin/gitea web --config /etc/gitea/app.ini"]
|
|
envp = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/gitea"], ["GITEA_WORK_DIR", "/var/lib/gitea"], ["USER", "gitea"]]
|
|
networking = "full"
|
|
cgroup = "arje.slice/gitea"
|
|
restart = { initial_ms = 1000, max_ms = 30000 }
|