censar: 26 repositorios existen SÓLO en la máquina que se va a borrar — el paso 1 del plan
Buscando cómo escribir la receta de `tawasuyu` apareció que su ÚNICO remoto es el gitea de gioser (`ssh://gitea@git.tawasuyu.net:2345/…`, y ese nombre resuelve a 204.168.193.248 = gioser). Al mirar el resto: **28 repositorios con remoto en esta máquina, 26 SIN NINGUNA copia fuera**. Sólo `takana` y `llimphi-standalone` tienen espejo externo. Apagar el origen no borra unos servicios: borra EL CÓDIGO CON EL QUE SE VOLVERÍAN A CONSTRUIR, y los clones de trabajo están en el mismo disco que también muere. Entre los 26 está `tawasuyu`, que produce 10 de los 13 binarios que nadie más provee — la dependencia circular completa. No se ve desde ninguna otra parte del censo: un repo no es un proceso, ni un puerto, ni un dominio. Se descubre cuando ya no hay de dónde sacarlo. · `censar.py` lo mira ahora (`[[repo]]`), y reporta NO «tiene remoto» sino si alguno de sus remotos NO es esta máquina: un remoto que apunta afuera es la prueba de que el código sobrevive. La lista de «esta máquina» sale del censo mismo (sus IPs + los dominios que sirve), no de nombres cableados. · `planear.py` lo emite como PASO 1, por delante del rescate de binarios: aquello pierde un servicio, esto pierde la posibilidad de reconstruirlo. El arreglo ya está escrito en el repo —takana usa `pushurl` doble por `scripts/espejo-setup.sh`. Y una corrección de algo que dije antes: la perilla por receta que vi en `recipe.rs` es `strip_debug`, no una versión de rust. NO existe `rust_version`; sólo `zig_version`. Pinear rustc por receta para honrar el `rust-toolchain.toml` de tawasuyu (1.96.0, contra 1.97.0 del lab) sería código nuevo en takana-core, no una opción ya disponible. De paso, medido el subárbol de los diez binarios por separado: cuatro (`willay-daemon`, `sandokan-seguridad-core`, `pacha-secretos`, `tupu-cli`, entre 84 y 153 deps) NO arrastran criptografía en C; cuatro traen `ring` y dos `aws-lc-sys`. O sea que hay un escalón por donde empezar sin pelear con cmake. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RomoxEGZUhaT4pob1QSX5x
This commit is contained in:
@@ -322,6 +322,50 @@ _CONFS = [("/etc/caddy/Caddyfile", "caddy"),
|
||||
("/etc/httpd/conf.d/*", "apache")]
|
||||
|
||||
|
||||
def repos_git(t, raices, ips, dominios):
|
||||
"""Repositorios git bajo los datos, y si su remoto vive EN ESTA MÁQUINA.
|
||||
|
||||
── LA DEPENDENCIA CIRCULAR QUE MATA LA MUDANZA AL FINAL ─────────────────────────────────────
|
||||
Medido en gioser: **25 repositorios cuyo único remoto es el gitea de la propia máquina** — entre
|
||||
ellos `tawasuyu`, que produce 10 de los 13 binarios que nadie más provee. Apagar el origen no
|
||||
borra sólo unos servicios: borra **el código con el que se volverían a construir**, y los clones
|
||||
de trabajo están en el mismo disco que también muere.
|
||||
|
||||
Es un fallo que no se ve desde ninguna otra parte del censo —el repo no es un proceso, ni un
|
||||
puerto, ni un dominio— y que sólo se descubre cuando ya no hay de dónde sacarlo.
|
||||
|
||||
Un remoto que apunta AFUERA es la prueba de que el código sobrevive. Por eso lo que se reporta no
|
||||
es «tiene remoto» sino **si alguno de sus remotos NO es esta máquina**."""
|
||||
if not raices:
|
||||
return []
|
||||
busca = " ".join(shlex.quote(r) for r in raices)
|
||||
out, ok = t.run(f"find {busca} -maxdepth 4 -name .git -prune 2>/dev/null | head -300")
|
||||
if not ok or not out.strip():
|
||||
return []
|
||||
mios = set(dominios) | set(ips) | {"localhost", "127.0.0.1"}
|
||||
res = []
|
||||
for g in out.split():
|
||||
r = g[:-5] if g.endswith("/.git") else g
|
||||
urls, ok2 = t.run(f"git -C {shlex.quote(r)} config --get-regexp "
|
||||
f"'remote\\..*\\.(url|pushurl)' 2>/dev/null")
|
||||
if not ok2 or not urls.strip() or urls.startswith("(sonda falló"):
|
||||
continue
|
||||
dentro, fuera = [], []
|
||||
for l in urls.splitlines():
|
||||
p = l.split(None, 1)
|
||||
if len(p) != 2:
|
||||
continue
|
||||
u = p[1]
|
||||
# `ssh://gitea@host:2345/x.git`, `gitea@host:x.git`, `https://host/x.git`
|
||||
h = u.split("://", 1)[-1].split("/", 1)[0].split("@")[-1].split(":")[0]
|
||||
(dentro if h in mios else fuera).append(u)
|
||||
if dentro:
|
||||
res.append({"path": r, "dentro": " ".join(dentro),
|
||||
"fuera": " ".join(fuera),
|
||||
"solo_aqui": "si" if not fuera else "no"})
|
||||
return res
|
||||
|
||||
|
||||
def sitios_web(t):
|
||||
"""Los sitios declarados en la config, con su raíz y si esa raíz EXISTE.
|
||||
|
||||
@@ -694,6 +738,11 @@ def emitir_toml(c):
|
||||
L.append(f"paquete = {toml_escape(s.get('paquete',''))}")
|
||||
L.append('decision = "" # muda | muere | (vacío = sin decidir)')
|
||||
L.append("")
|
||||
for x in c.get("repos", []):
|
||||
L.append("[[repo]]")
|
||||
for k, v in x.items():
|
||||
L.append(f"{k} = {toml_escape(v)}")
|
||||
L.append("")
|
||||
for x in c.get("sitios", []):
|
||||
L.append("[[sitio]]")
|
||||
for k, v in x.items():
|
||||
@@ -845,10 +894,13 @@ def main():
|
||||
doms.append(sondear_dominio(d, set(ident["ips"]), http_ok))
|
||||
raices = a.data_root or ["/var/www", "/var/lib", "/srv", "/opt", "/home", "/store"]
|
||||
dat = datos(t, raices)
|
||||
# Después de `raices`, que es de dónde salen los repos a mirar. (Estaba antes y `raices` todavía
|
||||
# no existía: habría reventado con NameError en la primera corrida con datos.)
|
||||
repos = repos_git(t, raices + ["/mnt/vvv"], ident.get("ips", []), nombres)
|
||||
censo = {
|
||||
"fecha": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
|
||||
"identidad": ident, "servicios": svc, "huerfanos": huerfanos, "puertos": pts,
|
||||
"dominios": doms, "datos": dat, "contenido": cont, "sitios": sitios, "montajes": montajes(t),
|
||||
"dominios": doms, "datos": dat, "contenido": cont, "sitios": sitios, "repos": repos, "montajes": montajes(t),
|
||||
"fuera_de_git": fuera_de_git(t),
|
||||
}
|
||||
resumen(censo)
|
||||
|
||||
@@ -638,6 +638,30 @@ def pasos(censo, target, key):
|
||||
muere = [(t, n, e) for t, n, e in entradas(censo) if e.get("decision") == "muere"]
|
||||
contenido = censo.get("contenido", [])
|
||||
|
||||
# ── 0. CÓDIGO QUE SÓLO EXISTE ACÁ ───────────────────────────────────────────────────────────
|
||||
# El primer paso del plan entero, por delante incluso del rescate de binarios: aquello es un
|
||||
# servicio que se pierde, esto es EL CÓDIGO CON EL QUE SE VOLVERÍA A CONSTRUIR. Y no se ve desde
|
||||
# ninguna otra parte del censo —un repo no es un proceso, ni un puerto, ni un dominio—, así que
|
||||
# se descubre cuando ya no hay de dónde sacarlo.
|
||||
solos = [r for r in censo.get("repo", []) if r.get("solo_aqui") == "si"]
|
||||
if solos:
|
||||
det = ["# Estos repositorios NO tienen ningún remoto fuera de esta máquina. Apagarla borra el",
|
||||
"# código, y los clones de trabajo están en el mismo disco que también muere.",
|
||||
"#",
|
||||
"# Por cada uno: agregarle un remoto que viva en otro lado y empujar. El propio takana",
|
||||
"# lo hace así (`scripts/espejo-setup.sh`: pushurl doble a gitea Y a un espejo privado).",
|
||||
"#"]
|
||||
for r in solos:
|
||||
det.append(f"# {r['path']}")
|
||||
det.append(f"# remoto actual (en esta máquina): {r['dentro'][:88]}")
|
||||
paso("codigo", f"{len(solos)} repositorio(s) que sólo existen en la máquina que se va a borrar",
|
||||
"\n".join(det),
|
||||
verifica="revisión humana: cada uno tiene que tener un remoto FUERA antes de apagar nada",
|
||||
verifica_tipo="humano",
|
||||
nota="⚠ Es la dependencia circular de esta mudanza: `tawasuyu` produce 10 de los 13 "
|
||||
"binarios que nadie más provee, y su único remoto es el gitea de esta misma "
|
||||
"máquina. Sin esto, apagar el origen deja servicios que no se pueden reconstruir.")
|
||||
|
||||
# ── 0 ter. COHERENCIA ───────────────────────────────────────────────────────────────────────
|
||||
incoh = incoherencias(censo)
|
||||
if incoh:
|
||||
|
||||
Reference in New Issue
Block a user