552299a761
Anlass: feature/cleanse-skill-index — ein Werkstatt-Worker hatte git init statt
Klonen gemacht (Orphan-Commit ohne gemeinsamen Vorfahren, Inhalt: ~/.hermes-Index-
Dateien statt Repo-Dateien). Solche Karten sahen aus wie normale Vorschlaege,
liefen bei Annahme aber IMMER in 'refusing to merge unrelated histories' — und
die Fehlermeldung ('bitte am PC aufloesen') war eine Sackgasse. Das passiert
oefter; darum drei Schichten dagegen:
1) ERKENNEN (Auftragsbuch): neue Karten-Flags 'verwaist' (kein merge-base mit
main -> rote Markierung 'kein gemeinsamer Ursprung', Annehmen-Knopf fehlt,
API+Runner halten zusaetzlich dicht) und 'leer' (Diff gegen main leer ->
'bringt nichts'-Badge). Empfehlung auf der Karte: ablehnen mit Grund,
Idee frisch in die Queue.
2) HEILEN (beide Runner, mc2 + lucy): kollidiert der Merge, weil main weiter-
gelaufen ist, versucht der Runner automatisch einen Rebase des Branches auf
main (isolierter Worktree; Gates laufen danach normal, Merge-Message sagt
'auto-rebased'). Nur wenn auch der Rebase kollidiert, faellt die Karte durch —
mit ehrlicher Meldung statt 'am PC aufloesen'.
3) VERHINDERN (werkstatt-SOUL): voll klonen (nie git init/--depth), Selbstcheck
'git merge-base HEAD origin/main' + fetch/rebase vor JEDEM Push, nie
~/.hermes-Artefakte committen.
Doku: AUFTRAGSBUCH.md + FALLEN.md (Erkennungsmuster: Diff 0 Dateien + behind ~
ganze Historie) + OFFENE-FAEDEN. Geprueft: py_compile gruen, bash -n beide
Runner gruen, tsc+vite build gruen (dist dabei); merge-base-Verhalten am echten
kaputten Branch auf der Box verifiziert.
229 lines
12 KiB
Bash
229 lines
12 KiB
Bash
#!/usr/bin/env bash
|
|
# Auftragsbuch: einen LUCY-Vorschlags-Branch ANNEHMEN — das PC-Gegenstück zu auftrag-annehmen.sh.
|
|
# Lucy (Electron) lebt auf dem Windows-PC (F:\Coding Stuff\lucy); dist liegt NICHT im Repo.
|
|
# Annehmen heißt darum: Merge auf main (hier, Box) → der PC zieht main, baut dist neu und
|
|
# startet Lucy neu, WENN sie lief (User-Entscheid: Lucy startet nur, wenn der Commander sie will).
|
|
# Schlägt am PC irgendetwas fehl, wird der Merge auf main automatisch revertiert — die laufende
|
|
# Lucy bleibt die alte (der PC-Runner stellt dist aus seinem Backup wieder her).
|
|
#
|
|
# Der PC wird über den PC-Executor (client/hermes-pc/executor.py, :7777, Bearer-Token aus
|
|
# ~/.hermes/config.yaml) gesteuert. /shell hat 60 s Timeout → der Build läuft DETACHED
|
|
# (deploy/lucy-annahme.ps1 im Lucy-Repo schreibt eine Status-Datei, wir pollen sie).
|
|
#
|
|
# Läuft wie der MC2-Runner als EIGENE systemd-Unit und als /tmp-Kopie (Selbst-Reset-Falle).
|
|
# Status-Schlüssel in der Auftragsbuch-JSON: "lucy:<branch>".
|
|
#
|
|
# Nutzung: lucy-annahme.sh <branch> (z. B. wartung/turncheck-default-aus)
|
|
set -uo pipefail
|
|
|
|
BRANCH="${1:?Nutzung: lucy-annahme.sh <branch>}"
|
|
SRC="${MC2_SRC:-$HOME/mission-control-v2}"
|
|
LUCY="${MC2_LUCY_DIR:-$HOME/lucy}"
|
|
STATUS="${MC2_AUFTRAG_STATUS:-/srv/models/mc2-auftragsbuch.json}"
|
|
HERMES_CONF="${HERMES_CONF:-$HOME/.hermes/config.yaml}"
|
|
SLUG="lucy-$(echo "$BRANCH" | tr '/' '-')"
|
|
WT="/tmp/annahme-$SLUG"
|
|
LOG="/tmp/annahme-$SLUG.log"
|
|
|
|
# Windows-Seite (Pfade im PC-Dateisystem; einfach gehalten — EIN PC, EIN Checkout).
|
|
PC_REPO='F:\Coding Stuff\lucy'
|
|
PC_STATUSFILE='F:\Coding Stuff\lucy\.lucy-annahme.json'
|
|
PC_RUNNER='F:\Coding Stuff\lucy\deploy\lucy-annahme.ps1'
|
|
BUILD_TIMEOUT_S=1500 # 25 min Deckel für npm ci + tsc + electron-builder + Stimm-Warmup
|
|
|
|
notify(){ bash "$SRC/deploy/notify.sh" -s "[Auftragsbuch]" "$1" || true; }
|
|
|
|
# Status-Fortschritt für die UI (atomar via tmp+replace; die API liest die Datei nur).
|
|
status(){ # $1=state $2=detail
|
|
python3 - "$STATUS" "lucy:$BRANCH" "$1" "$2" <<'PY'
|
|
import json, os, sys, time
|
|
p, key, state, detail = sys.argv[1:5]
|
|
try:
|
|
d = json.load(open(p, encoding="utf-8"))
|
|
except Exception:
|
|
d = {}
|
|
d.setdefault("branches", {})[key] = {"state": state, "detail": detail, "ts": time.time()}
|
|
tmp = p + ".tmp"
|
|
json.dump(d, open(tmp, "w", encoding="utf-8"), ensure_ascii=False)
|
|
os.replace(tmp, p)
|
|
PY
|
|
}
|
|
|
|
# PC-Executor: PowerShell-Kommando ausführen. stdout des PCs → stdout; rc 0 nur wenn
|
|
# Transport UND Kommando grün. Token/URL kommen aus der Hermes-Config (kein zweiter Ablageort).
|
|
PC_URL="$(sed -n 's/^[[:space:]]*PC_EXECUTOR_URL:[[:space:]]*//p' "$HERMES_CONF" | head -1 | tr -d "\"'" | tr -d '[:space:]')"
|
|
PC_TOKEN="$(sed -n 's/^[[:space:]]*PC_EXECUTOR_TOKEN:[[:space:]]*//p' "$HERMES_CONF" | head -1 | tr -d "\"'" | tr -d '[:space:]')"
|
|
pc_shell(){ # $1 = PowerShell-Kommando
|
|
python3 - "$PC_URL" "$PC_TOKEN" "$1" <<'PY'
|
|
import json, sys, urllib.request
|
|
url, token, cmd = sys.argv[1:4]
|
|
req = urllib.request.Request(url + "/shell", data=json.dumps({"command": cmd}).encode(),
|
|
headers={"Authorization": "Bearer " + token,
|
|
"Content-Type": "application/json"})
|
|
try:
|
|
r = json.load(urllib.request.urlopen(req, timeout=58))
|
|
except Exception as e:
|
|
print(f"(PC-Executor nicht erreichbar: {e})")
|
|
sys.exit(2)
|
|
out = (r.get("stdout") or "").strip()
|
|
if out:
|
|
print(out)
|
|
err = (r.get("stderr") or "").strip()
|
|
if err:
|
|
print(err, file=sys.stderr)
|
|
sys.exit(0 if r.get("returncode") == 0 else 1)
|
|
PY
|
|
}
|
|
|
|
cleanup_wt(){ git -C "$LUCY" worktree remove --force "$WT" 2>/dev/null || true; }
|
|
|
|
fail(){
|
|
status "fehlgeschlagen" "$1"
|
|
notify "Lucy-Vorschlag '$BRANCH' konnte NICHT eingespielt werden: $1 — nichts wurde verändert, der Branch bleibt liegen."
|
|
cleanup_wt
|
|
exit 1
|
|
}
|
|
|
|
# Revert nach schon gepushtem Merge (PC-Phase rot): main zurückdrehen, PC-Checkout nachziehen.
|
|
revert_main(){ # $1 = Grund für die Meldung
|
|
status "rollback" "PC-Phase rot — Merge wird automatisch zurückgedreht"
|
|
git -C "$LUCY" fetch -q origin || true
|
|
cleanup_wt
|
|
if git -C "$LUCY" worktree add --detach "$WT" origin/main >/dev/null 2>&1 \
|
|
&& git -C "$WT" -c user.name="Auftragsbuch" -c user.email="auftragsbuch@box.local" \
|
|
revert -m 1 --no-edit HEAD >>"$LOG" 2>&1 \
|
|
&& git -C "$WT" push origin HEAD:main >>"$LOG" 2>&1; then
|
|
cleanup_wt
|
|
# PC-Checkout best-effort auf den revertierten Stand nachziehen (Quelle = laufende Lucy).
|
|
pc_shell "git -C '$PC_REPO' fetch origin main 2>&1; git -C '$PC_REPO' merge --ff-only FETCH_HEAD 2>&1" >>"$LOG" 2>&1 || true
|
|
status "zurueckgerollt" "$1 — Merge automatisch revertiert, Lucy läuft in der alten Version weiter"
|
|
notify "Lucy-Vorschlag '$BRANCH' hat den PC-Build/Neustart GERISSEN: $1. Merge automatisch zurückgedreht — Lucy läuft in der alten Version weiter. Der Branch bleibt zur Analyse liegen. Log: $LOG"
|
|
exit 1
|
|
fi
|
|
cleanup_wt
|
|
status "kritisch" "Revert fehlgeschlagen — Lucy-Repo und PC von Hand prüfen"
|
|
notify "KRITISCH: Lucy-Annahme '$BRANCH' scheiterte UND der automatische Revert schlug fehl — bitte Lucy-Repo (Gitea main) und PC prüfen. Log: $LOG"
|
|
exit 2
|
|
}
|
|
|
|
[ -n "$PC_URL" ] && [ -n "$PC_TOKEN" ] || fail "PC-Executor-Zugang fehlt in $HERMES_CONF (PC_EXECUTOR_URL/TOKEN)"
|
|
|
|
# ── Vorprüfungen am PC (VOR dem Merge — solange ist nichts kaputtzumachen) ──
|
|
status "laeuft" "Vorprüfung: ist der PC bereit?"
|
|
curl -sf -m 8 "$PC_URL/health" >/dev/null 2>&1 \
|
|
|| fail "PC-Executor nicht erreichbar — Lucy-Annahme braucht den eingeschalteten PC (Executor-Task läuft bei Anmeldung)"
|
|
|
|
PC_BRANCH="$(pc_shell "git -C '$PC_REPO' rev-parse --abbrev-ref HEAD" 2>>"$LOG")" \
|
|
|| fail "PC-Arbeitskopie nicht lesbar (git am PC): $PC_BRANCH"
|
|
[ "$PC_BRANCH" = "main" ] || fail "PC-Arbeitskopie steht auf '$PC_BRANCH' statt main — bitte am PC aufräumen"
|
|
|
|
PC_DIRTY="$(pc_shell "git -C '$PC_REPO' status --porcelain -uno" 2>>"$LOG")" \
|
|
|| fail "PC-Arbeitskopie nicht prüfbar"
|
|
[ -z "$PC_DIRTY" ] || fail "PC-Arbeitskopie hat ungespeicherte Änderungen — bitte erst am PC committen/aufräumen: $(echo "$PC_DIRTY" | head -3 | tr '\n' ' ')"
|
|
|
|
WAS_RUNNING="$(pc_shell "if (Get-Process Lucy -ErrorAction SilentlyContinue) { '1' } else { '0' }" 2>>"$LOG")" || WAS_RUNNING="0"
|
|
case "$WAS_RUNNING" in 1|0) ;; *) WAS_RUNNING="0" ;; esac
|
|
|
|
# ── Merge auf main (Box, isolierter Worktree — wie beim MC2-Runner) ─────────
|
|
status "laeuft" "Merge wird vorbereitet"
|
|
git -C "$LUCY" fetch -q origin || fail "git fetch (Gitea) fehlgeschlagen"
|
|
git -C "$LUCY" rev-parse --verify -q "refs/remotes/origin/$BRANCH" >/dev/null \
|
|
|| fail "Branch origin/$BRANCH existiert nicht (schon gemergt/gelöscht?)"
|
|
|
|
# Kaputt aufgesetzte Branches (kein gemeinsamer Vorfahre) → Merge kann NIE gelingen.
|
|
git -C "$LUCY" merge-base origin/main "origin/$BRANCH" >/dev/null 2>&1 \
|
|
|| fail "Branch hat keinen gemeinsamen Ursprung mit main (kaputt aufgesetzt, z. B. git init statt Klonen) — bitte ablehnen (mit Grund) und die Idee neu in die Queue geben"
|
|
|
|
cleanup_wt
|
|
git -C "$LUCY" worktree add --detach "$WT" origin/main >/dev/null 2>&1 || fail "Worktree konnte nicht angelegt werden"
|
|
MERGE_MSG="Auftragsbuch: Lucy-Vorschlag '$BRANCH' angenommen (Ein-Klick-Gate)"
|
|
if ! git -C "$WT" -c user.name="Auftragsbuch" -c user.email="auftragsbuch@box.local" \
|
|
merge --no-ff "origin/$BRANCH" -m "$MERGE_MSG" >>"$LOG" 2>&1; then
|
|
git -C "$WT" merge --abort 2>/dev/null || true
|
|
# Selbstheilung: veralteten Branch mechanisch auf main rebasen (wie im MC2-Runner).
|
|
status "laeuft" "Merge-Konflikt — Auto-Rebase wird versucht"
|
|
WT2="/tmp/annahme-rebase-$SLUG"
|
|
git -C "$LUCY" worktree remove --force "$WT2" 2>/dev/null || true
|
|
REBASED=""
|
|
if git -C "$LUCY" worktree add --detach "$WT2" "origin/$BRANCH" >/dev/null 2>&1 \
|
|
&& git -C "$WT2" -c user.name="Auftragsbuch" -c user.email="auftragsbuch@box.local" \
|
|
rebase origin/main >>"$LOG" 2>&1; then
|
|
REBASED="$(git -C "$WT2" rev-parse HEAD)"
|
|
else
|
|
git -C "$WT2" rebase --abort 2>/dev/null || true
|
|
fi
|
|
git -C "$LUCY" worktree remove --force "$WT2" 2>/dev/null || true
|
|
[ -n "$REBASED" ] || fail "Merge-Konflikt mit main, Auto-Rebase scheiterte ebenfalls — echter Inhaltskonflikt; am einfachsten ablehnen und die Idee neu in die Queue geben (frischer Branch von aktuellem main)"
|
|
git -C "$WT" -c user.name="Auftragsbuch" -c user.email="auftragsbuch@box.local" \
|
|
merge --no-ff "$REBASED" -m "$MERGE_MSG — auto-rebased (Branch war veraltet)" >>"$LOG" 2>&1 \
|
|
|| { git -C "$WT" merge --abort 2>/dev/null || true; fail "Merge nach Auto-Rebase fehlgeschlagen (unerwartet) — Log: $LOG"; }
|
|
fi
|
|
|
|
status "laeuft" "Push nach main läuft"
|
|
PUSHED=0
|
|
for _ in 1 2 3; do
|
|
git -C "$WT" push origin HEAD:main >>"$LOG" 2>&1 && { PUSHED=1; break; }
|
|
sleep 5
|
|
done
|
|
[ "$PUSHED" = 1 ] || fail "Push nach main fehlgeschlagen (Gitea nicht erreichbar / Token?)"
|
|
cleanup_wt
|
|
|
|
# ── PC: main ziehen, Build detached starten ─────────────────────────────────
|
|
status "laeuft" "PC zieht den neuen Stand"
|
|
pc_shell "git -C '$PC_REPO' fetch origin main 2>&1; if (\$LASTEXITCODE -ne 0) { exit 1 }; git -C '$PC_REPO' merge --ff-only FETCH_HEAD 2>&1" >>"$LOG" 2>&1 \
|
|
|| revert_main "PC konnte main nicht ziehen (git fetch/merge am PC rot)"
|
|
|
|
pc_shell "if (Test-Path '$PC_RUNNER') { exit 0 } else { exit 1 }" >>"$LOG" 2>&1 \
|
|
|| revert_main "PC-Runner-Skript fehlt im Lucy-Repo (deploy/lucy-annahme.ps1)"
|
|
|
|
# WICHTIG: -File-Pfad MUSS eingebettete Anführungszeichen tragen — Start-Process fügt
|
|
# die ArgumentList-Elemente OHNE Quoting zusammen; 'F:\Coding Stuff\…' zerbricht sonst am
|
|
# Leerzeichen und die gespawnte powershell stirbt still (erster Live-Lauf, 12.07.).
|
|
pc_shell "Remove-Item -Force '$PC_STATUSFILE' -ErrorAction SilentlyContinue; Start-Process powershell -WindowStyle Hidden -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File','\"$PC_RUNNER\"','-WasRunning','$WAS_RUNNING'" >>"$LOG" 2>&1 \
|
|
|| revert_main "PC-Build ließ sich nicht starten"
|
|
|
|
# ── Status-Datei pollen, Fortschritt in die UI spiegeln ─────────────────────
|
|
status "laeuft" "PC baut Lucy (dist) — das dauert ein paar Minuten"
|
|
DEADLINE=$(( $(date +%s) + BUILD_TIMEOUT_S ))
|
|
LAST_DETAIL=""
|
|
FINAL=""
|
|
while [ "$(date +%s)" -lt "$DEADLINE" ]; do
|
|
sleep 10
|
|
RAW="$(pc_shell "if (Test-Path '$PC_STATUSFILE') { Get-Content -Raw '$PC_STATUSFILE' }" 2>>"$LOG")" || continue
|
|
[ -n "$RAW" ] || continue
|
|
# Parse OHNE f-String-Escapes: \" in f-String-Ausdruecken ist seit Python 3.12/3.14
|
|
# ein SyntaxError (alter Pre-3.12-Stil) — genau das legte den ersten Live-Lauf lahm.
|
|
PARSED="$(printf '%s' "$RAW" | python3 -c '
|
|
import json, sys
|
|
try:
|
|
d = json.loads(sys.stdin.read())
|
|
except Exception:
|
|
sys.exit(0)
|
|
detail = str(d.get("detail", "")).replace("|", "/").replace("\n", " ")
|
|
done = "1" if d.get("done") else "0"
|
|
ok = "1" if d.get("ok") else "0"
|
|
print(done + "|" + ok + "|" + detail)
|
|
')"
|
|
[ -n "$PARSED" ] || continue
|
|
DONE="${PARSED%%|*}"; REST="${PARSED#*|}"; OK="${REST%%|*}"; DETAIL="${REST#*|}"
|
|
if [ "$DETAIL" != "$LAST_DETAIL" ] && [ -n "$DETAIL" ]; then
|
|
LAST_DETAIL="$DETAIL"
|
|
status "laeuft" "PC: $DETAIL"
|
|
fi
|
|
if [ "$DONE" = "1" ]; then FINAL="$OK"; break; fi
|
|
done
|
|
|
|
[ -n "$FINAL" ] || revert_main "PC-Build-Timeout nach $((BUILD_TIMEOUT_S/60)) Minuten (Status-Datei blieb offen)"
|
|
[ "$FINAL" = "1" ] || revert_main "${LAST_DETAIL:-PC meldet Fehler ohne Detail}"
|
|
|
|
# ── Grün: Branch aufräumen, ehrlich melden ──────────────────────────────────
|
|
git -C "$LUCY" push origin --delete "$BRANCH" >>"$LOG" 2>&1 || true
|
|
git -C "$LUCY" fetch -q --prune origin 2>/dev/null || true
|
|
status "eingespielt" "${LAST_DETAIL:-Build grün}"
|
|
if [ "$WAS_RUNNING" = "1" ]; then
|
|
notify "Lucy-Vorschlag '$BRANCH' ist LIVE — dist am PC neu gebaut, Lucy neu gestartet, Stimme meldet sich. ✅"
|
|
else
|
|
notify "Lucy-Vorschlag '$BRANCH' ist eingespielt — dist am PC neu gebaut. Lucy war aus und bleibt aus; die neue Version startet beim nächsten Öffnen. ✅"
|
|
fi
|
|
exit 0
|