From 4bbfc127ad6bebde5c3abdd1e1c89b41f331e82b Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Mon, 1 Jun 2026 22:31:57 +0200 Subject: [PATCH] fix(infra): auto-recover de push rechazado + limpieza de gitlinks fantasma full_git_push: el auto-recover de non-fast-forward nunca se disparaba por dos bugs. (1) git_push_if_ahead hacia `tail -1` del error de push y se quedaba con la linea final de `hint:`, perdiendo "[rejected]" y "Updates were rejected", de modo que el pipeline no detectaba el rechazo. Ahora se preservan las lineas con los keywords (rejected/fast-forward/fetch first). (2) El paso 4 capturaba stdout y stderr juntos (2>&1), metiendo lineas "[push]" de stderr al principio del status y rompiendo el glob `== "[error]"*` anclado al inicio; ahora solo captura stdout y la deteccion cubre todas las redacciones de git. (3) El merge auto evaluaba el exit de `tail` (siempre 0) en vez del de `git merge`; un merge con conflictos se reportaba como exito. Ahora se evalua el exit real del merge y se aborta limpio ante conflictos. Limpieza: cpp/apps/chart_demo y cpp/apps/shaders_lab estaban commiteados como gitlinks (submodulos, mode 160000) sin URL en .gitmodules, directorios vacios fantasma que rompian el paso 3 de full_git_pull con "fatal: No url found for submodule path". Eliminados del indice. Las apps C++ reales viven en apps/ como sub-repos Gitea independientes (regla cpp_apps.md; cpp/apps/ deprecado por issue 0096). Co-Authored-By: Claude Opus 4.8 (1M context) --- bash/functions/infra/git_push_if_ahead.sh | 9 +++++++- bash/functions/pipelines/full_git_push.sh | 25 ++++++++++++++++++----- cpp/apps/chart_demo | 1 - cpp/apps/shaders_lab | 1 - 4 files changed, 28 insertions(+), 8 deletions(-) delete mode 160000 cpp/apps/chart_demo delete mode 160000 cpp/apps/shaders_lab diff --git a/bash/functions/infra/git_push_if_ahead.sh b/bash/functions/infra/git_push_if_ahead.sh index c16b579a..18b0e16d 100644 --- a/bash/functions/infra/git_push_if_ahead.sh +++ b/bash/functions/infra/git_push_if_ahead.sh @@ -51,7 +51,14 @@ git_push_if_ahead() { echo "[push] $repo_name ($branch, $ahead commits ahead)" >&2 local push_out push_out=$(git -C "$abs_repo" push origin "$branch" 2>&1) || { - echo "[error] $repo_name: $(echo "$push_out" | tail -1)" + # Preservar las lineas con los keywords que el orquestador usa para + # decidir el auto-recover (rejected / fast-forward / non-fast-forward). + # Un `tail -1` plano se quedaba con la linea final de `hint:` y perdia + # "[rejected]" + "Updates were rejected", impidiendo el recover. + local reason + reason=$(echo "$push_out" | grep -iE 'rejected|fast-forward|denied|permission|error:' | head -3 | tr '\n' ' ') + [[ -z "$reason" ]] && reason=$(echo "$push_out" | tail -1) + echo "[error] $repo_name: $reason" return 0 } echo "$push_out" | tail -3 >&2 diff --git a/bash/functions/pipelines/full_git_push.sh b/bash/functions/pipelines/full_git_push.sh index 8fb6698d..f990bcf9 100644 --- a/bash/functions/pipelines/full_git_push.sh +++ b/bash/functions/pipelines/full_git_push.sh @@ -191,21 +191,36 @@ full_git_push() { [[ -z "$repo" ]] && continue local repo_name repo_name="$(basename "$repo")" + # Captura SOLO stdout como status_line (la decision de control); el + # stderr (logs de git) va a la terminal. Mezclar ambos con 2>&1 metia + # lineas "[push] ..." de stderr al principio del string y rompia el + # glob `== "[error]"*` (anclado al inicio) del recover. local status_line - status_line=$(git_push_if_ahead "$repo" 2>&1 || true) + status_line=$(git_push_if_ahead "$repo" 2>/dev/null || true) - if [[ "$status_line" == *"non-fast-forward"* || "$status_line" == *"Updates were rejected"* || "$status_line" == "[error]"* ]]; then + # Detectar push rechazado por remoto adelantado. Cubrimos las distintas + # redacciones de git: "[rejected]", "non-fast-forward", "fetch first", + # "Updates were rejected", "Note about fast-forwards", o cualquier + # linea que git_push_if_ahead haya marcado como [error]. + if [[ "$status_line" == *"[error]"* || "$status_line" == *"rejected"* || "$status_line" == *"fast-forward"* || "$status_line" == *"fetch first"* ]]; then echo " [recover] $repo_name: push rechazado, intentando merge auto" >&2 # Fetch para tener origin actualizado. git -C "$repo" fetch --quiet 2>/dev/null || true local upstream upstream=$(git -C "$repo" rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null || true) if [[ -n "$upstream" ]]; then - if git -C "$repo" merge --no-ff --no-edit "$upstream" 2>&1 | tail -3 >&2; then - status_line=$(git_push_if_ahead "$repo" 2>&1 || true) + # Evaluar el exit del MERGE, no el de tail. Antes + # `git merge ... | tail -3` evaluaba el exit de tail (siempre 0), + # asi un merge con conflictos se reportaba como exito. + local merge_out merge_rc + merge_out=$(git -C "$repo" merge --no-ff --no-edit "$upstream" 2>&1) + merge_rc=$? + echo "$merge_out" | tail -3 >&2 + if [[ "$merge_rc" -eq 0 ]]; then + status_line=$(git_push_if_ahead "$repo" 2>/dev/null || true) else git -C "$repo" merge --abort 2>/dev/null || true - status_line="[error] $repo_name: merge auto fallo, requiere intervencion manual" + status_line="[error] $repo_name: merge auto fallo (conflictos), requiere intervencion manual" fi else status_line="[error] $repo_name: sin upstream, no se puede recuperar" diff --git a/cpp/apps/chart_demo b/cpp/apps/chart_demo deleted file mode 160000 index 026f514b..00000000 --- a/cpp/apps/chart_demo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 026f514bb72d5fb00c57493ea09101a3d2cf4894 diff --git a/cpp/apps/shaders_lab b/cpp/apps/shaders_lab deleted file mode 160000 index dc9a970a..00000000 --- a/cpp/apps/shaders_lab +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dc9a970aff759837bfe12c5e640a66dc64edfbd2