Files
fn_registry/.claude/commands/full-git-push.md
T
2026-05-06 00:13:11 +02:00

6.8 KiB

/full-git-push — Push automático de fn_registry + todos los sub-repos + fn sync

Pushea el repo principal fn_registry y todos los sub-repos git anidados (apps y analyses, cada uno como repo independiente bajo dataforge/<name> en Gitea), y luego ejecuta fn sync para empujar la metadata no regenerable (proposals, apps, projects, analysis, vaults, pc_locations) al registry_api.

Estandar: todo apps/<name>, analysis/<name>, projects/*/apps/<name> y projects/*/analysis/<name> debe tener su propio repo Gitea bajo dataforge/<basename>. Los subrepos/ de la raiz NO entran (mirrors upstream). Los vaults/ tampoco.

Modo automático (preferencia del usuario): este comando NO pregunta. Si hay dirty trees, commitea automáticamente con un mensaje generado a partir de los cambios. Prioridad: hacer commits frecuentes y pushear rápido. Único límite: no commitear archivos que parezcan secrets (.env, *credentials*, *.key, *.pem, id_rsa*) — si se detectan, abortar y avisar.

Argumento

$ARGUMENTS — opcional. Si se pasa texto, se usa como mensaje de commit. Sin argumento, se genera uno automáticamente con el patrón:

chore: auto-commit (<N> archivos modificados, <N> nuevos, <N> borrados)

- <ruta1>
- <ruta2>
...

Si los cambios tienen un patrón claro (todos en un mismo dominio/dir), usar ese patrón en el subject:

  • todo bajo python/functions/<dom>/feat(<dom>): auto-commit con N cambios
  • todo bajo dev/issues/chore(issues): auto-commit
  • mezclado → chore: auto-commit

Pasos

1. Descubrir repos git + apps/analyses sin git

cd /home/lucas/fn_registry

REPOS=$(find . -name ".git" -type d \
  -not -path "./.git" -not -path "./.git/*" \
  -not -path "*/node_modules/*" -not -path "*/.venv/*" \
  -not -path "*/cpp/vendor/*" -not -path "*/cpp/build/*" \
  -not -path "*/sources/*" -not -path "*/temp/*" -not -path "*/subrepos/*" 2>/dev/null \
  | sed 's|/.git$||')
REPOS=". $REPOS"

# Apps/analyses sin .git — auto-inicializar como dataforge/<basename>
MISSING=()
for d in apps/*/ analysis/*/ projects/*/apps/*/ projects/*/analysis/*/; do
  d="${d%/}"
  [[ -d "$d/.git" ]] || MISSING+=("$d")
done

1b. Auto-inicializar repos faltantes (sin pedir confirmación)

Para cada $d en MISSING:

export GITEA_URL=$(pass agentes/gitea-url | head -n1)
export GITEA_TOKEN=$(pass gitea/dataforge-git-token | head -n1)
export FN_REGISTRY_INFRA_DIR=/home/lucas/fn_registry/bash/functions/infra

bash -c "
  source $FN_REGISTRY_INFRA_DIR/ensure_repo_synced.sh
  ensure_repo_synced '$d' dataforge \"\$(basename '$d')\" master 'chore: initial sync'
"

Si $d/.gitignore no existe antes de inicializar, escribir uno apropiado (ver .claude/rules/apps_vs_functions.md). Solo abortar la inicialización de ese repo concreto si falla; seguir con el resto.

2. Detectar secrets antes de commitear

Para cada repo dirty, listar archivos modificados/nuevos y comprobar nombres sospechosos:

for r in $REPOS; do
  ( cd "$r" \
    && git status --porcelain | awk '{print $2}' \
    | grep -E '(^|/)(\.env(\..*)?$|.*credentials.*|.*\.key$|.*\.pem$|id_rsa.*|.*secret.*|.*token.*\.txt$)' \
    | head -5
  )
done

Si la lista de coincidencias no está vacía, abortar el push completo, listar los archivos sospechosos y pedir al usuario que los gestione (añadir a .gitignore, mover, o decidir explícitamente que entren).

3. Auto-commitear dirty trees

Para cada repo con cambios sin commitear:

for r in $REPOS; do
  ( cd "$r"
    [ -z "$(git status --porcelain)" ] && exit 0  # limpio, nada que hacer
    git add -A
    if [ -n "$ARGUMENTS" ]; then
      MSG="$ARGUMENTS"
    else
      MSG="$(generate_auto_message)"  # patrón descrito en sección 'Argumento'
    fi
    git commit -m "$MSG" \
      -m "Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>" 2>&1 | tail -3
  )
done

generate_auto_message debe inspeccionar git diff --cached --stat y producir un subject como feat(notebook): N cambios cuando todos los paths comparten prefijo, o chore: auto-commit si están dispersos.

4. Push solo de repos con cambios locales (NO usar git push ciego)

Filtrar primero, pushear despues. Sobre 30+ repos, hacer git push en todos (incluso "Everything up-to-date") cuesta 30-90s en handshakes SSH. Usar refs locales (sin red) para decidir si hay algo que pushear:

for r in $REPOS; do
  ( cd "$r" \
    && BRANCH=$(git rev-parse --abbrev-ref HEAD) \
    && UPSTREAM_OK=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null) \
    && if [ -z "$UPSTREAM_OK" ]; then
         echo "[push -u] $r ($BRANCH)"
         git push -u origin "$BRANCH" 2>&1 | tail -3
       else
         AHEAD=$(git rev-list --count @{u}..HEAD 2>/dev/null)
         if [ "${AHEAD:-0}" -gt 0 ]; then
           echo "[push] $r ($BRANCH, $AHEAD commits ahead)"
           git push origin "$BRANCH" 2>&1 | tail -3
         else
           echo "[skip] $r (up-to-date local refs)"
         fi
       fi
  )
done

Reglas:

  • Sin upstream → git push -u (siempre).
  • Con upstream y rev-list @{u}..HEAD > 0 → push.
  • Con upstream y 0 ahead → skip (ni siquiera intentar).

rev-list @{u}..HEAD solo lee refs locales, no toca la red. Esto es seguro porque cualquier commit local hecho en este PC ya esta a partir del paso 3 (auto-commit). Si en otro PC se hizo push y aqui no se ha hecho pull, sigue siendo correcto: no tenemos nada local que pushear.

Si push rechaza por non-fast-forward (rama behind), no abortar el resto. Reportar ese repo concreto y sugerir /full-git-pull antes; seguir con los demás repos.

5. fn sync

USER=$(pass registry/basicauth-user | head -1)
PASSWD=$(pass registry/basicauth-pass | head -1)
TOKEN=$(pass registry/api-token | head -1)
export FN_REGISTRY_API="https://${USER}:${PASSWD}@registry.organic-machine.com"
export REGISTRY_API_TOKEN="$TOKEN"
./fn sync

Si pass falla con "decryption failed" → gpg-agent bloqueado. Pedir al usuario que ejecute pass show registry/api-token en su terminal real (Bash tool no tiene TTY) y reintentar.

6. Resumen

Tabla concisa: por repo, commits creados (cuántos y subject), commits pusheados, o "ya estaba al día". Y resultado de fn sync (sent / received / imported).

Notas

  • Modo no-interactivo por diseño. El usuario prefiere commits frecuentes y push rápido. No se pregunta si commitear ni se pide mensaje (salvo que se pase via $ARGUMENTS).
  • Secrets son la única razón para abortar antes de commitear. Cualquier patrón sospechoso (.env, credenciales, claves) detiene el flujo y se reporta al usuario.
  • Submodules cpp/vendor/ (mirrors upstream) se ignoran.
  • Si un sub-repo va behind el remote, su push se omite con un mensaje (no se aborta el resto). El usuario corre /full-git-pull cuando le convenga.