Communitygithub.com

afresto-deploy

Automatisiert das Deployment von Afresto Next: Backend Go (GHCR→Watchtower), Web React (Cloudflare Worker), mobiles RN/Expo per EAS OTA – mit strengen Regeln für Multi-Agenten-Klone.

Was ist afresto-deploy?

Die Skill 'afresto-deploy' ist für das Deployment der Afresto-Next-Plattform zuständig, die aus einem Go-Backend, einem React-Web-Frontend (Vite, Cloudflare Worker) und einer mobilen React Native/Expo-App in einem separaten Repository besteht. Sie stellt sicher, dass Produktions-Deployments immer eine explizite Benutzerfreigabe erfordern. Ein zentrales Merkmal ist die Unterstützung von Multi-Agent-Umgebungen, in denen mehrere KI-Agenten gleichzeitig am selben Repository arbeiten können, ohne sich gegenseitig zu behindern. Die Skill enthält detaillierte Anweisungen zur selektiven Staging von Dateien, um unbeabsichtigtes Committen von Änderungen anderer Sitzungen zu vermeiden, sowie zur Verwendung von `git worktree` für isolierte Builds und Deployments. Sie setzt Qualitäts-Gates für Go-Code durch (gofmt, go vet, go build) und erzwingt Workflow-Regeln wie Issue-Erstellung vor bedeutenden Änderungen und Pull-Request-basierte Zusammenarbeit (keine direkten Commits auf main). Die Skill deckt auch die Migrationsverwaltung (automatisch via GitHub Actions), die Handhabung von GHCR-Image-Pushes, Watchtower-Updates, 502-Fehlerbehebung und OTA-Updates für mobile Apps ab.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add Afresto-Next/afresto-claude-skills

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Afresto Next deployen

Stack: Backend Go (push→GHCR→Watchtower ~5 Min.) · Web React (Vite→Cloudflare Worker, manuell wrangler deploy) · Mobile RN/Expo in separatem Repo <repo-mobile> (EAS OTA). Produktions-Deployment = immer zuerst Benutzer um Erlaubnis fragen (sie stimmen jedes Mal zu).

🔴 Regel #1 — Dieses Repo wird oft von VIELEN TAB-AGENTEN gleichzeitig verwendet

git status zeigt oft modifizierte Dateien von anderen Sitzungen an. Deshalb:

  • NIEMALS git add -a / git commit -a / git add . — das würde unfertige Arbeiten anderer Sitzungen in deinen Commit ziehen.
  • SELEKTIV stagen nur deine Dateien, dann prüfen: git diff --cached --name-only — stelle sicher, dass keine fremden Dateien dabei sind (häufige Irrgäste: web/src/pages/ChatPage.tsx, backend/internal/db/dbgen/models.go, wenn nicht von dir regeneriert).
  • Dateien, die von zwei Sitzungen gemeinsam genutzt werden (z. B. ExamPage) und du nur deine Hunks willst: nicht stash/reset (das würde die Datei unter dem Fuß des anderen Agenten wegziehen). Bearbeite stattdessen den Git-Index über Blobs: git show :path > tmp → deren Hunks aus tmp entfernen → SHA=$(git hash-object -w tmp) → git update-index --cacheinfo 100644,$SHA,path. Die Datei auf der Platte bleibt unberührt.
  • 🚨 cwd des Bash-Tools kann „kleben bleiben“ vom vorherigen cd .../web (oder Worktree) aus einem vorherigen Aufruf. Dann schlägt git add backend/... fehl (pathspec did not match) oder staged den falschen Baum. Vor selektivem Stagen: cd <repo-root> && (oder absolute Pfade verwenden). Prüfe mit git diff --cached --name-only, ob die relativen Pfade korrekt sind (nicht ../backend, src/...).
  • Nach Deployment: git status prüfen — sicherstellen, dass Arbeitsdateien anderer Sitzungen noch intakt sind.

Go-Qualitätsgate (vor Backend-Commit)

Entspricht Pint + PHPStan für Go bei Laravel. Lokal ausführen, bevor Backend-Dateien gestaged werden:

  • Format: cd backend && gofmt -l . → muss LEER sein. Gibt es Ausgabe → gofmt -w . (automatische Korrektur), das Ergebnis stagen. (Entspricht pint --test.)
  • Build + Vet: go build ./... && go vet ./... grün.
  • Statische Analyse (Ziel): golangci-lint run ./... (fasst gofmt/govet/staticcheck/errcheck/ineffassign/unused/misspell zusammen). Entspricht phpstan analyse.
  • ⚠️ Status: CI-Gate noch nicht aktiv. Weder backend/.golangci.yml noch .github/workflows/ci.yml vorhanden, und staticcheck ist veraltet → daher Format+Build+Vet vorerst manuell. Geplant: Erzwingen in CI + vollständige Konfiguration: docs/plan/00-tooling-quality-gate.md. Generierte Code in internal/db/dbgen vom Lint ausgenommen.
  • Tests begleiten die Änderungen → Skill afresto-testing (go test ./...; Integration überspringt ohne TEST_DATABASE_URL).

🔴 Regel #00 — Bedeutungsvolle Arbeit kommt ZUERST in ein GitHub Issue (seit 5. Sep. 2026)

Gilt vor Regel #0 (Branch + PR): Bevor du überhaupt Code anfasst.

Das Ziel ist keine Formalität. Anfragen, die nur im Chat kommen, sind nur zwei Parteien bekannt — das Team kann sie nicht lesen, der Eigentümer hat keinen Ort zum Zuweisen, und das Modul hinterlässt keine Spur, wer was gemacht hat.

Wann Issue PFLICHT ist

Neue Features · Änderungen, die von der Schule gesehen werden · DB-Schema-Änderungen · alles, was eine Produktentscheidung erfordert · Arbeit, die mehr als einen PR umfasst.

Wann NICHT nötig

Klar abgegrenzte Bugs · Text-/Label-Korrekturen · Dokumentation · direkte Folgen aus PR-Review-Kommentaren.

Im Zweifel → Issue anlegen. Ist billig und kann wieder geschlossen werden. Diese Schwelle hält die Regel am Leben: Wenn jede Kleinigkeit zum Issue wird, fangen die Leute an, sie stillschweigend zu umgehen, und einmal umgangen ohne Konsequenz stirbt die Regel.

Was der Agent tut

  1. Code ausreichend durchsuchen, um „erste Erkenntnisse“ zu sammeln und Blocker zu finden — nicht raten.
  2. Issue anlegen mit der Vorlage .github/ISSUE_TEMPLATE/pekerjaan.md, mit Label modul:* (das bildet die Aufzeichnung pro Modul), plus butuh-keputusan, falls etwas offen ist.
  3. AUFHÖREN. Nicht coden, keinen Branch erstellen, sich nicht selbst zuweisen. Zuweisung ist Sache des Eigentümers; das Team braucht Raum für Input/Revision der Idee.
  4. Melde die Issue-Nummer dem Eigentümer, zusammen mit noch offenen Entscheidungen (falls vorhanden).

🔑 Der Eigentümer darf das überspringen mit einem „Mach jetzt weiter“ — das Issue wird trotzdem als Notiz angelegt, die Arbeit läuft weiter. Diese Regel steuert den Standard, sie ist kein Gefängnis.

Blocker, die erst WÄHREND der Bearbeitung auffallen

Vor dem Weitermachen Bescheid geben: Kommentar im Issue (damit das Team es sieht) und dem Eigentümer mitteilen. Falls die Entscheidung die Arbeit stoppt → Label butuh-keputusan hinzufügen. Nicht stillschweigend eine eigene Interpretation wählen und weitermachen — das führt zu Arbeit, die „fertig“ ist, aber falsch.

Den Kreis schließen

PR muss Closes #<nummer> im Body enthalten. Das verbindet Issue → PR → Person, so dass gh issue list --label modul:ujian --state all zur Aufzeichnung wird, wer was in diesem Modul gemacht hat. Ohne diese Zeile ist die Spur unterbrochen.

🔴 Regel #0 — NIEMALS direkt auf main commiten/pushen (Team von 4, seit 25. Jul. 2026)

Alle Arbeit läuft über Branch + Pull Request; nur Reviewer mergen in main (Merge = Deployment-Trigger).

  1. Vor Beginn: git checkout main && git pull — falls neue Migrationen da sind, Migrationen zuerst ausführen.
  2. Themenbezogenen Branch erstellen/auschecken, BEVOR Code angefasst wird:
    • Feature → feat/<name> · Bug → bugfix/<name> · Bug mit Issue → neuen Branch + Issue studieren.
  3. Selektiv commiten → git push -u origin <branch> (NICHT auf main).
  4. PR zu main öffnen: gh pr create, falls gh eingeloggt; sonst dem Benutzer die URL geben: https://github.com/Afresto-Next/next/compare/main...<branch>?expand=1.
  5. Reviewer approved & merged → Deployment. PR niemals selbst mergen.
  • Der Abschnitt „Backend pushen / WEB deployen“ unten = Mechanik, die NACH PR-Merge in main läuft (normalerweise von der deployenden Person), keine Erlaubnis, direkt auf main zu pushen.
  • Skill-Änderungen ebenfalls per PR (CODEOWNERS).

Schritt-für-Schritt-Anleitung

1. Commit (Bash-Tool = Git Bash / sh, NICHT PowerShell)

  • Mehrzeilige Nachricht: Heredoc git commit -q -F - <<'EOF' … EOF. NICHT @'…'@ (das ist PowerShell-Here-String → Betreff wird zu @ ...).
  • Nachricht beenden: Co-Authored-By: Claude Opus 4.8 <[email protected]>.
  • Warnung „CRLF wird durch LF ersetzt“ = normal, ignorieren.
  • Commit in deinen Aufgabenbranch (feat/* · bugfix/*), nicht main (siehe Regel #0). Benutzer committet auf Anfrage.

🔴 NIEMALS git add -A / git add <dir>/ — hat bereits 3x Benutzerdateien mitgenommen

Dieses Repo enthält viele ungetrackte Benutzer-Arbeitsdateien: docs/bskap/ (Bilder+PDFs), docs/analisis butir soal/, elibrary/, cloudflare/**/.wrangler/ (Account-Cache). Ein einziges -A und alles ist dabei.

Tatsächliche Vorfälle: git add docs/ hat BSKAP-PDFs/xls/Bilder mitgenommen (25. Jul & 2. Aug); git add -A hat 446 Dateien mitgenommen, darunter wrangler-account.json (9. Aug).

git add pfad/zu/datei1 pfad/zu/datei2        # EINE NACH DER ANDEREN, immer
git commit -q -F - <<'EOF'
...
EOF
git show --name-only --format="" HEAD | wc -l   # stimmt mit dem überein, was du beabsichtigt hast?

Weicht die Anzahl ab: git reset --soft HEAD~1 && git reset, richtig neu stagen. Bereits auf eigenen Branch gepusht: git push --force-with-lease ist sicher.

2. DB-Migration — MUSS im TIP-commit sein

.github/workflows/migrate.yml erkennt Migrationen über git diff --name-only HEAD~1 HEAD (nur letzter Commit), läuft nach erfolgreichem „Build & Push Docker Images“ (workflow_run).

  • Enthält der Push eine Migration → Migration muss im TIP-commit sein (ein Commit mit Migration+Code = sicher).
  • Migration läuft automatisch auf dem Self-Hosted-Runner-VM nach dem Build. run --rm migrate synchronisiert → Job SCHLÄGT FEHL, wenn Fehler auftreten (nicht still).
  • Migration prüfen: Tab Actions → „Migrate DB“ grün für diesen Commit (gh CLI verfügbar: gh run list --workflow="Migrate DB (self-hosted)" --limit 1 — Agent kann selbst prüfen, ohne den Benutzer zu bitten). Watchtower tauscht das api-Image beim nächsten Poll (~5 Min.), fast immer nach Abschluss der Migration.
  • VOR dem Pushen der Migration: SQL über psql BEGIN; … ROLLBACK; in der lokalen DB prüfen (v102 = prod). Siehe Skill afresto-db-change / Engineering-Fallen §4.

🔴 Migrationsnummer: GENAU VOR DEM MERGE prüfen, nicht beim Branch-Erstellen

Teammitglieder können während dein Branch offen ist eine andere Migrationsserie mergen. Doppelte Nummern lassen goose in sortAndConnectMigrations PANIKEN — kein SQL-Fehler, also leicht falsch zu deuten. ✅ Keine Datenbeschädigung: Die Panik tritt beim Sammeln der Liste auf, bevor eine einzige ausgeführt wird.

Tatsächlicher Vorfall (9. Aug. 2026): 00138 beim Branch-Erstellen gewählt; beim Merge hatte das Team bereits 00138–00140 verwendet. Erste Korrektur benannte es in 00140 und immer noch Kollision — wieder geraten. Zählen, nicht raten:

git pull                                        # ZUERST, um Migrationen der Kollegen zu sehen
MAX=$(ls backend/migrations/ | sed 's/_.*//' | sort -n | tail -1)
NEXT=$(printf "%05d" $((10#$MAX + 1)))
ls backend/migrations/ | sed 's/_.*//' | sort | uniq -d   # MUSS LEER sein

Falls schon passiert: git mv auf die neue Nummer, kleinen PR, mergen, dann Workflow „Migrate DB (self-hosted)“ aus dem Actions-Tab erneut ausführen.

3. Backend pushen (NACH PR-Merge in main — siehe Regel #0)

PR-Merge → main aktualisiert → GHCR → Watchtower ~5 Min. tauscht das Image. Keine Backend-Änderung = kein Warten auf Watchtower nötig. (Während der Entwicklung: git push -u origin <branch> dann PR — nicht auf main pushen.)

⚠️ NACH Backend-Merge: AUF 502 PRÜFEN (bereits 6x aufgetreten)

Neuerstellen des api/worker-Containers verursacht oft einen Totalausfall — alle Container Up aber sämtliche Routen 502. Praktisch jeder Backend-Merge ist ein Würfelwurf.

curl -s -o /dev/null -w "%{http_code}\n" --max-time 15 https://next.afresto.co/api/v1/auth/me
# 401 = GESUND (fordert Authentifizierung) · 502 = Ausfall

Bei 502 → Actions → „Ops — API wiederherstellen“ → restart-docker (ohne SSH, <1 Min. Wiederherstellung). Nicht das neu deployte Feature analysieren: 502 + Container Up = Docker-Daemon-Neustart. Details: docs/ops-auto-pulih.md.

4. WEB deployen — AUS SAUBEREM WORKTREE (wegen vieler Tab-Agenten)

npm run build liest den GESAMTEN Working Tree → unfertige Arbeiten anderer Sitzungen werden mit veröffentlicht. Also Build und Deployment aus einem Worktree deines Commits:

rm -rf /c/wtd 2>/dev/null; git worktree add --detach /c/wtd <SHA-deines-Commits>
cd /c/wtd && git status --short   # muss LEER sein (sauber)
cd /c/wtd/web && npm ci && npm run build
cd /c/wtd/cloudflare/web-worker && npx wrangler deploy
cd <repo-projectTwo> && git worktree remove --force /c/wtd && git worktree prune
  • 🔥 Worktree-Pfad MUSS KURZ sein (/c/wtd) — das Scratchpad + langer DKI-Kalender-PDF-Name → Windows-Fehler „Dateiname zu lang“ beim Checkout.
  • Bundle bei Bedarf prüfen: grep -rl "<merkmal-deines-features>" /c/wtd/web/dist/assets/*.js — aber Vorsicht vor falsch Positiven (allgemeine Merkmale könnten von anderen Features stammen).
  • Cloudflare-Cache der Assets ~20 Sek. → Test mit Strg+F5. (200 aber Größe ~397 Byte = Fallback, nicht echtes Asset.)

5. Mobile OTA — separates Repo, --environment PFLICHT

cd "<repo-mobile>"
npx eas update --branch preview --environment preview --message "…" --non-interactive
  • 🔥 --environment PFLICHT (preview/production) — ohne wird das Update in der falschen Umgebung veröffentlicht/schlägt fehl.
  • Mobile Repo hat ein GitHub-Remote (Afresto-Next/afresto-next-mobile) → Regel #0 gilt: Branch → PR → Merge, nicht direkt auf main pushen.
  • OTA nur JS/Assets. Änderungen an Native-Modulen → CRASH bei OTA → Neubuild erforderlich.

🔥 eas update sendet den WORKING TREE, nicht den Commit

Genau wie npm run build in Schritt 4 — das gesamte Arbeitsverzeichnis wird gebündelt, einschließlich unfertiger Arbeiten anderer Sitzungen. Das Zeichen * in der Zeile Commit <hash>* im Veröffentlichungsergebnis = unsauberer Tree.

UNBEDINGT direkt vor der Veröffentlichung — ausgeben, dann abgleichen mit der Zeile Commit im Ergebnis:

echo "veröffentliche von: $(git branch --show-current) @ $(git log --oneline -1 --format=%h)"
git log --oneline HEAD..origin/main   # LEER = keine unerledigte Arbeit anderer zurückgelassen
  • Von main veröffentlichen, nicht von Feature-Branches. Bei einem Branch gehen bereits gemergte Arbeiten anderer im Channel VERLOREN — OTA ersetzt das gesamte Bundle, es wird nicht geflickt.
  • Produktion: uncommittete Änderungen anderer Sitzungen kurz weglegen (git stash push -- <dateien> … git stash pop). Produktion darf keinen ungeprüften Code enthalten.

🚨 Branches können sich WÄHREND der Arbeit verschieben (viele Tab-Agenten, eine Klon)

Vorfall 20. Aug. 2026: Preview-Veröffentlichung eindeutig von main @ d0938fa; der nächste Befehl stellte fest, dass HEAD bereits auf einem anderen Branch @ 8126a50 war — eine andere Sitzung hatte im selben Klon den Branch gewechselt. Produktion lief zeitweise mit Code von 3 PRs länger, ohne einen einzigen Fehler.

➡️ Nicht annehmen, der Branch sei zwischen Befehlen gleich geblieben. Jedes Mal neu ausgeben (obiger Befehl). ➡️ Eigentliche Prävention: OTA aus WORKTREE, genau wie Web-Deployment in Schritt 4 — andere Sitzungen können den Branch nicht verschieben:

rm -rf /c/wtm 2>/dev/null; git worktree add --detach /c/wtm <SHA-deines-Commits>
cd /c/wtm && git status --short          # muss LEER sein
npm ci && npx eas update --branch <ch> --environment <env> --message "…" --non-interactive
cd <repo-mobile> && git worktree remove --force /c/wtm && git worktree prune

🪤 Worktree-Pfad kurz (/c/wtm) — gleicher Grund wie Schritt 4. 🪤 npm ci im Worktree braucht Zeit; für kleine OTA-Änderungen kann man im Hauptklon bleiben, solange die obige Branch+Commit-Prüfung durchgeführt wird und das Ergebnis übereinstimmt.

🏢 GHCR & GitHub-Organisation — Repo jetzt in der Org Afresto-Next

Repos (next, afresto-next-mobile, afresto-claude-skills) sind von ristology/* → Org Afresto-Next umgezogen (20. Jul. 2026). Vollständiges Runbook zum Transfer: docs/runbook-transfer-next-org.md.

  • 🚨 Repo-Transfer in Org = GHCR-Pfad ändert den Owner → Watchtower BLEIBT STILL STEHEN. CI taggt Images als ghcr.io/${GITHUB_REPOSITORY_OWNER}/afresto-next-{api,web,migrate} — Owner folgt dem Repo-Eigentümer. Seit dem Transfer ist der Org-Name der Owner. Watchtower, der noch im alten Pfad läuft, zieht keine neuen Images mehr. Behoben durch docker service update --image … auf dem Server (Details im Runbook). Die Änderung der CI-Tag-Struktur (${GITHUB_REPOSITORY_OWNER} → dynamisch) ist nicht rückgängig zu machen — selbst wenn das Repo in eine andere Org/eigenes Konto wechselt, passt sich der Pfad automatisch an.
  • ⚠️ Org-Account hat Login-Limit: 1 Sitzung → Ein anderer Entwickler, der in der Org arbeitet, kann dich abmelden. gh auth status prüfen, bevor es wichtig wird.

🔴 Regel der letzten Ausfahrt — Möglichst ALLE Schritte in EINEM Befehl

Jeder Befehl ist eine Gelegenheit für eine andere Sitzung, git status zu verändern, den Branch zu verschieben, git add für andere Dateien mitten drin auszuführen. Kein Teilbefehl ist vor dem nächsten sicher.

&&-Ketten, Heredocs, Subshells. Wenn du cd musst, mach es in einer Subshell (cd … && …).

Schlecht (anfällig für Unterbrechungen):

git checkout main
git pull
# → Unterbrechung hier: andere Sitzung erstellt Datei X
git checkout -b feat/my-feature

Besser:

git checkout main && git pull && git checkout -b feat/my-feature
# alles in einer Zeile — keine Unterbrechung

Am besten — Minimales Blocken:

  • Vor Selektivem Stagen: cd <repo-root> && git add … (sicherer cwd)
  • Vor gh pr create: git push -u origin <branch> im selben Befehl
  • Vor npm run build: git worktree add und cd worktree && … – Worktree ist immun gegen Branch-Wechsel

Alte Vorfälle (für Agenten-Lernkontext)

DatumVorfallAuswirkung
25 Jul 2026git add docs/ zog BSKAP-PDFs mit15 unerwünschte Dateien im Commit
9 Aug 2026git mv einer Migration in Kollisionsnummer (00140) — Behebung im selben PR2 CI-Fehlschläge + manuelles erneutes Ausführen der Aktion
9 Aug 2026git add -A zog wrangler-account.json und 445 andere Dateien mitCommit zurückgesetzt, Entschuldigung ans Team
20 Aug 2026OTA-Veröffentlichung von main, aber nächster Befehl war auf anderem Branch (andere Sitzung)Produktion 30 Min. mit Code von 3 PRs zu weit — ohne Fehler, also unbemerkt

Letzte Worte

Jeder Befehl kann die letzte Aktion sein, bevor eine andere Sitzung dazwischenfunkt. && verhindert, dass die nächste Zeile auf falschem Stand läuft. Worktrees machen Builds immun. Branch-Prüfungen machen OTA immun. Die 502-Prüfung macht Backend-Merges immun. Benutzerfreigabe = Firewall.

Wenn das Rückgängigmachen eines Fehlers mehr als 5 Minuten dauert, wurde eine Regel verletzt.

Verwandte Skills