Communitygithub.com

Déploiement Afresto

Déploie le backend Go, l'application web React et l'application mobile React Native/Expo du projet Afresto Next, avec des règles strictes pour le travail multi-agent et les Pull Requests.

Qu'est-ce que Déploiement Afresto ?

Ce skill est dédié aux agents IA travaillant sur le projet Afresto Next, un SaaS éducatif utilisant une pile backend Go, frontend web React (Vite/Cloudflare Workers) et mobile React Native/Expo (EAS OTA). Il automatise le déploiement complet en respectant des contraintes de sécurité et de coordination propres à un environnement multi-agent : gestion sélective du staging Git pour éviter de piétiner le travail d'autres agents, interdiction de commit/push direct sur `main` (obligation de PR), et vérification des migrations de base de données. Le skill exige que toute modification significative soit d'abord documentée via une GitHub Issue avant d'écrire du code. Il détaille également les procédures de déploiement pour le backend via Docker (GHCR + Watchtower), le web via Wrangler depuis un worktree propre, et le mobile via EAS Update. Le skill s'applique aux agents utilisant Claude Code, Cursor ou Codex interagissant avec le dépôt GitHub `Afresto-Next/next` et ses dépendances.

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

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Déploiement Afresto Next

Pile : backend Go (push → GHCR → Watchtower ~5 min) · web React (Vite → Cloudflare Worker, manuel wrangler deploy) · mobile RN/Expo dans dépôt séparé <repo-mobile> (EAS OTA). Le déploiement en production nécessite toujours l'autorisation préalable de l'utilisateur (il approuve chaque fois).

🔴 Règle #1 — ce dépôt est souvent utilisé par PLUSIEURS AGENTS SIMULTANÉMENT

git status affiche souvent des fichiers modifiés appartenant à d'autres sessions. Par conséquent :

  • NE PAS utiliser git add -a / git commit -a / git add . — cela inclurait le travail inachevé d'autres sessions dans votre commit.
  • Stagez SÉLECTIVEMENT chaque fichier qui vous appartient, puis vérifiez : git diff --cached --name-only — assurez-vous qu'aucun fichier étranger n'est présent (fichiers fréquemment inclus par erreur : web/src/pages/ChatPage.tsx, backend/internal/db/dbgen/models.go s'ils ne résultent pas de votre propre régénération).
  • Fichier partagé par deux sessions (ex. ExamPage) et vous ne voulez que vos hunks : ne faites pas stash/reset (cela retirerait le fichier sous les pieds d'un autre agent). Modifiez l'index Git via un blob : git show :path > tmp → supprimez leurs hunks de tmp → SHA=$(git hash-object -w tmp) → git update-index --cacheinfo 100644,$SHA,path. Le fichier sur le disque reste intact.
  • 🚨 L'outil cwd du Bash peut PERSISTER après un cd .../web (ou worktree) lors d'un appel précédent. Alors git add backend/... échoue avec pathspec did not match (ou stage l'arbre erroné). Avant un stage sélectif, faites d'abord cd <repo-root> && (ou utilisez un chemin absolu). Vérifiez que git diff --cached --name-only affiche les chemins relatifs à la racine corrects (pas ../backend, src/...).
  • Après le déploiement : vérifiez git status — assurez-vous que les fichiers de travail des autres sessions sont toujours intacts.

Qualité Go (avant commit backend)

Équivalent de Pint + PHPStan de Laravel pour Go. Exécutez localement avant de stage les fichiers backend :

  • Format : cd backend && gofmt -l . → doit être VIDE. S'il y a du contenu → gofmt -w . (correction automatique), stagez le résultat. (Équivalent de pint --test.)
  • Build + vet : go build ./... && go vet ./... vert.
  • Analyse statique (cible) : golangci-lint run ./... (englobe gofmt/govet/staticcheck/errcheck/ineffassign/unused/misspell). Équivalent de phpstan analyse.
  • ⚠️ Statut : qualité CI non activée. Il n'y a pas encore de backend/.golangci.yml ni de .github/workflows/ci.yml, et staticcheck est obsolète → donc format+build+vet sont manuels pour l'instant. Prévision d'activation dans la CI + configuration complète : docs/plan/00-tooling-quality-gate.md. Le code généré internal/db/dbgen est exclu du lint.
  • Les tests accompagnent les changements → skill afresto-testing (go test ./... ; les tests d'intégration sont ignorés sans TEST_DATABASE_URL).

🔴 Règle #00 — tout travail SIGNIFICATIF passe par une GitHub Issue D'ABORD (depuis le 5 Sep 2026)

S'applique avant la Règle #0 (branche + PR) : avant de toucher au code.

Le but n'est pas une formalité. Les demandes formulées par chat ne sont connues que de deux personnes — l'équipe ne peut pas les lire, le propriétaire n'a pas d'endroit pour les assigner, et le module ne laisse aucune trace de qui fait quoi.

Quand une issue est OBLIGATOIRE

Nouvelle fonctionnalité · changement visible par l'école · modification du schéma DB · tout ce qui nécessite une décision produit · un travail qui s'étend sur plus d'un PR.

Quand ce n'est PAS nécessaire

Bug évident et limité · correction de texte/étiquette · documentation · suivi direct d'un commentaire de revue de PR.

En cas de doute → créez une issue. C'est peu coûteux, et on peut la fermer. C'est ce seuil qui maintient la règle en vie : si chaque demande insignifiante devient une issue, les gens commenceront à la contourner en silence, et une fois contournée sans conséquence, la règle est morte.

Ce que fait l'agent

  1. Explorez suffisamment le code pour remplir les "constatations préliminaires" et identifier les blocages — ne devinez pas.
  2. Créez l'issue en utilisant le modèle .github/ISSUE_TEMPLATE/pekerjaan.md, ajoutez l'étiquette modul:* (c'est ce qui constitue l'historique par module), ainsi que butuh-keputusan si quelque chose est en suspens.
  3. ARRÊTEZ-VOUS. Ne codez pas, ne créez pas de branche, ne vous assignez pas la tâche. L'assignation est une action du propriétaire ; l'équipe a aussi besoin d'espace pour donner son avis/réviser les idées.
  4. Signalez le numéro de l'issue au propriétaire, ainsi que les décisions toujours en suspens (le cas échéant).

🔑 Le propriétaire peut contourner cette règle en disant "fais-le maintenant" — l'issue est tout de même créée comme trace, le travail continue. Cette règle régit le comportement par défaut, elle n'emprisonne pas.

Blocage découvert EN COURS DE réalisation

Prévenez avant de continuer : commentez dans l'issue (pour que l'équipe voie) et informez le propriétaire. Si la décision interrompt le travail → ajoutez l'étiquette butuh-keputusan. Ne choisissez pas silencieusement votre propre interprétation et continuez — c'est ce qui produit un travail "terminé" mais incorrect.

Boucler la boucle

Un PR doit obligatoirement contenir Closes #<numéro> dans son corps. C'est ce qui connecte l'issue → PR → personne, de sorte que gh issue list --label modul:ujian --state all devienne la trace de qui a fait quoi dans ce module. Sans cette ligne, la piste est rompue.

🔴 Règle #0 — NE PAS commit/push directement sur main (équipe de 4 personnes, depuis le 25 Jul 2026)

Tout travail passe par branche + Pull Request ; seul le relecteur fusionne dans main (la fusion déclenche le déploiement).

  1. Avant de commencer : git checkout main && git pull — s'il y a de nouvelles migrations, exécutez d'abord les migrations.
  2. Créez/vérifiez une branche thématique AVANT de toucher au code :
    • fonctionnalité → feat/<nom> · bogue → bugfix/<nom> · bogue avec issue → nouvelle branche + étudiez l'issue.
  3. Commit sélectif → git push -u origin <branche> (PAS sur main).
  4. Ouvrez un PR vers main : gh pr create si gh est connecté ; sinon, donnez à l'utilisateur l'URL https://github.com/Afresto-Next/next/compare/main...<branche>?expand=1.
  5. Le relecteur approuve & fusionne → déploiement. NE PAS fusionner votre propre PR.
  • La section "Push backend / Deploy WEB" ci-dessous décrit la mécanique qui s'exécute APRÈS la fusion du PR dans main (généralement par la personne chargée du déploiement), ce n'est pas une autorisation à push directement sur main.
  • Les modifications du skill passent également par un PR (CODEOWNERS).

Ordre des étapes

1. Commit (Outil Bash = Git Bash / sh, PAS PowerShell)

  • Message multi-lignes : heredoc git commit -q -F - <<'EOF' … EOF. NE PAS utiliser @'…'@ (c'est un here-string PowerShell → le sujet serait pollué par @ ...).
  • Terminez le message : Co-Authored-By: Claude Opus 4.8 <[email protected]>.
  • Avertissement "CRLF will be replaced by LF" = normal, ignorez-le.
  • Commitez sur votre branche de travail (feat/* · bugfix/*), pas sur main (voir Règle #0). L'utilisateur commit quand on lui demande.

🔴 NE PAS utiliser git add -A / git add <dir>/ — cela a déjà supprimé les fichiers de l'utilisateur 3 fois

Ce dépôt contient de nombreux fichiers de travail non suivis de l'utilisateur : docs/bskap/ (images+PDF), docs/analisis butir soal/, elibrary/, cloudflare/**/.wrangler/ (cache de compte). Une fois avec -A, tout est inclus.

Cas réels : git add docs/ a inclus des PDF/xls/images BSKAP (25 Jul & 2 Agu) ; git add -A a inclus 446 fichiers dont wrangler-account.json (9 Agu).

git add chemin/vers/fichier1 chemin/vers/fichier2        # UN PAR UN, toujours
git commit -q -F - <<'EOF'
...
EOF
git show --name-only --format="" HEAD | wc -l   # correspond à ce que vous vouliez ?

Si le nombre est erroné : git reset --soft HEAD~1 && git reset, refaite le stage correctement. Si déjà poussé vers votre propre branche, git push --force-with-lease est sûr.

2. Migration DB — OBLIGATOIREMENT dans le commit SOMMET

.github/workflows/migrate.yml détecte les migrations via git diff --name-only HEAD~1 HEAD (uniquement le dernier commit), s'exécute après le succès de "Build & Push Docker Images" (workflow_run).

  • Si le push contient une migration → la migration doit être dans le commit SOMMET (un commit contenant la migration + le code = sûr).
  • La migration s'exécute automatiquement sur le runner auto-hébergé de la VM après le build. run --rm migrate se synchronise → le job ÉCHOUE FORTEMENT en cas d'erreur (pas silencieux).
  • Vérifiez la migration : consultez l'onglet Actions → "Migrate DB" vert pour ce commit (gh CLI disponible : gh run list --workflow="Migrate DB (self-hosted)" --limit 1 — l'agent peut vérifier lui-même, sans demander à l'utilisateur). Watchtower remplace l'image api lors du prochain sondage (~5 min), presque toujours après la fin de la migration.
  • AVANT de pousser une migration, vérifiez son SQL via psql BEGIN; … ROLLBACK; sur la DB locale (v102 = prod). Voir le skill afresto-db-change / jebakan-rekayasa §4.

🔴 Numéro de migration : vérifiez à nouveau JUSTE AVANT DE FUSIONNER, pas lors de la création de la branche

Un collègue peut fusionner une autre série de migrations pendant que votre branche est ouverte. Des numéros identiques font que goose PANIQUE dans sortAndConnectMigrations — ce n'est pas une erreur SQL, donc facile à mal interpréter. ✅ Aucune corruption de données : la panique se produit lors de la collecte de la liste, avant qu'une seule ne soit exécutée.

Cas réel (9 Agu 2026) : 00138 a été choisi lors de la création de la branche ; lors de la fusion, l'équipe utilisait déjà 00138–00140. La première correction l'a renommé 00140 et était toujours en conflit — parce qu'on a deviné à nouveau. Comptez, ne devinez pas :

git pull                                        # OBLIGATOIRE d'abord, pour voir les migrations des collègues
MAX=$(ls backend/migrations/ | sed 's/_.*//' | sort -n | tail -1)
NEXT=$(printf "%05d" $((10#$MAX + 1)))
ls backend/migrations/ | sed 's/_.*//' | sort | uniq -d   # DOIT ÊTRE VIDE

Si c'est déjà fait : git mv vers un nouveau numéro, petit PR, fusionnez, puis relancez le workflow "Migrate DB (self-hosted)" depuis l'onglet Actions.

3. Push backend (APRÈS la fusion du PR dans main — voir Règle #0)

Fusion du PR → main mis à jour → GHCR → Watchtower ~5 min remplace l'image. Aucun changement backend = pas besoin d'attendre Watchtower. (Pendant le développement : git push -u origin <branche> puis PR — ne poussez pas main.)

⚠️ APRÈS la fusion backend : VÉRIFIEZ l'erreur 502 (déjà survenue 6 fois)

La recréation du conteneur api/worker déclenche souvent une panne totale — tous les conteneurs sont Up mais toutes les routes donnent 502. En pratique, chaque fusion backend est un coup de dés.

curl -s -o /dev/null -w "%{http_code}\n" --max-time 15 https://next.afresto.co/api/v1/auth/me
# 401 = EN BONNE SANTÉ (authentification requise) · 502 = panne

Si 502 → Actions → "Ops — restaurer l'API" → restart-docker (sans SSH, restauration en <1 minute). Ne cherchez pas à analyser la fonctionnalité nouvellement déployée : 502 + conteneur Up = redémarrage du démon Docker. Détails : docs/ops-auto-pulih.md.

4. Déployer le WEB — depuis un WORKTREE PROPRE (à cause des nombreux agents)

npm run build lit L'ARBRE DE TRAVAIL ENTIER → le travail inachevé d'autres sessions est également publié. Construisez et déployez donc depuis un worktree sur votre commit :

rm -rf /c/wtd 2>/dev/null; git worktree add --detach /c/wtd <SHA-de-votre-commit>
cd /c/wtd && git status --short   # doit être VIDE (propre)
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
  • 🔥 Le chemin du worktree DOIT ÊTRE COURT (/c/wtd) — le scratchpad interne + les longs noms de fichier PDF kaldik DKI → erreur Windows "Filename too long" lors du checkout.
  • Vérifiez que le bundle est correct si nécessaire : grep -rl "<marqueur-de-votre-fonctionnalité>" /c/wtd/web/dist/assets/*.js — mais méfiez-vous des faux positifs (un marqueur commun peut provenir d'une autre fonctionnalité).
  • Le cache Cloudflare dure ~20 s → testez avec Ctrl+F5. (200 mais taille ~397 octets = fallback, pas l'actif réel.)

5. Mobile OTA — dépôt séparé, --environment OBLIGATOIRE

cd "<repo-mobile>"
npx eas update --branch preview --environment preview --message "…" --non-interactive
  • 🔥 --environment OBLIGATOIRE (preview/production) — sans cela, la mise à jour concerne le mauvais environnement/échoue.
  • Le dépôt mobile a un dépôt distant GitHub (Afresto-Next/afresto-next-mobile) → la Règle #0 s'applique : branche → PR → fusion, pas de push direct sur main.
  • OTA concerne uniquement le JS/les actifs. Un changement de module natif → CRASH si fait via OTA → nécessite une reconstruction.

🔥 eas update envoie L'ARBRE DE TRAVAIL, pas le commit

Exactement comme npm run build à l'étape 4 — tout le répertoire de travail est inclus dans le bundle, y compris le travail inachevé d'autres sessions. Le symbole * sur la ligne Commit <hash>* dans le résultat de publication = arbre sale.

OBLIGATOIRE juste avant la publication — imprimez, puis comparez avec la ligne Commit dans le résultat :

echo "publié depuis : $(git branch --show-current) @ $(git log --oneline -1 --format=%h)"
git log --oneline HEAD..origin/main   # VIDE = aucun travail d'autrui n'est oublié
  • Publiez depuis main, pas depuis une branche de fonctionnalité. Depuis une branche, le travail des autres déjà fusionné DISPARAÎT du canal — OTA remplace l'intégralité du bundle, il ne le corrige pas.
  • Production : mettez de côté d'abord les modifications non commitées des autres sessions (git stash push -- <fichiers> … git stash pop). La production ne doit pas transporter de code non examiné.

🚨 La branche peut CHANGER en cours de travail (plusieurs agents, un seul clone)

Cas 20 Agu 2026 : la publication preview provenait clairement de main @ d0938fa ; la commande suivante a trouvé HEAD déjà sur une autre branche @ 8126a50 — une autre session a changé de branche sur le même clone. La production a exécuté du code de 3 PR de plus pendant ce temps, sans aucune erreur.

➡️ Ne supposez pas que la branche est la même d'une commande à l'autre. Imprimez-la à chaque fois (commande ci-dessus). ➡️ Prévention réelle : OTA depuis un WORKTREE, comme pour le déploiement web à l'étape 4 — les autres sessions ne peuvent pas changer sa branche :

rm -rf /c/wtm 2>/dev/null; git worktree add --detach /c/wtm <SHA-de-votre-commit>
cd /c/wtm && git status --short          # doit être VIDE
npm ci && npx eas update --branch <ch> --environment <env> --message "…" --non-interactive
cd <repo-mobile> && git worktree remove --force /c/wtm && git worktree prune

🪤 Chemin du worktree court (/c/wtm) — même raison qu'à l'étape 4. 🪤 npm ci dans le worktree prend du temps ; pour les petites mises à jour OTA, vous pouvez rester sur le clone principal à condition que la vérification de la branche+commit ci-dessus soit effectuée et que le résultat corresponde.

🏢 GHCR & organisation GitHub — le dépôt est désormais dans l'org Afresto-Next

Les dépôts (next, afresto-next-mobile, afresto-claude-skills) ont été déplacés de ristology/* → org Afresto-Next (20 Jul 2026). Le runbook de transfert complet : docs/runbook-transfer-next-org.md.

  • 🚨 Transférer un dépôt vers une org = le chemin GHCR suit le propriétaire → Watchtower SE BLOQUE SILENCIEUSEMENT. La CI tague l'image ghcr.io/${GITHUB_REPOSITORY_OWNER}/afresto-next-{api,web,migrate} — le propriétaire suit le propriétaire du dépôt. La section suivante…

État CI — dépôt racine next

WorkflowStatut (actuel)RunnerDéclencheurDécrit dans
Build & Push Docker Images✅ OKubuntu-latestpush sur main (backend)§3 ci-dessus
Migrate DB (self-hosted)✅ OKself-hostedworkflow_run sur Build & Push§2 ci-dessus
Deploy Web (Workers)🚧 À faireubuntu-latestpush sur main (web)§4 ci-dessus
Lint & Test (Go)🚧 À faireubuntu-latestpull request (pull_request)docs/plan/…

Statut auto-explicatif : ✅ OK = workflow actif et stable. 🚧 À faire = workflow pas encore écrit (la qualité manuelle s'applique).

Tous les workflows sont dans .github/workflows/.

🧩 Dépendances & Prérequis

# Actif de ce dépôt requis pour le déploiement
# (fichiers de configuration : wrangler.toml, Dockerfile, etc.)

# Dépôts externes (déjà clonés sur la machine ? `cd <chemin> && git remote -v`)
# - <repo-mobile> : Afresto-Next/afresto-next-mobile

# Outils à vérifier (tooling GitHub)
# - gh CLI : `gh --version` (authentifié : `gh auth status`)
# - npx : inclus avec Node.js (LTS)
# - wrangler : `npx wrangler --version`
# - eas-cli : `npx eas --version`

Commandes de base par dépôt (scripts package.json/setup)

DépôtCommandeUsage
next (web)npm run buildBuild web React
next (cloudflare/web-worker)npx wrangler deployDéploiement Worker
afresto-next-mobilenpx eas update --branch <br> --environment <env> --message "…"Mise à jour OTA

Versioning & Règles Backwards Compatibility

Par principe : compatibilité ascendante entre versions. Si une modification est cassante, elle doit être annoncée via une issue et documentée. Les déploiements OTA mobile ne doivent pas casser les fonctionnalités existantes. Toute modification non rétrocompatible doit être planifiée.

Gestion des erreurs de déploiement

Tous les déploiements sont suivis via les Actions GitHub. En cas d'échec :

  • Backend : Actions → Build & Push Docker Images → Actions → (redéclencher) ou git revert du commit problématique, puis git push sur main. Watchtower remettra l'ancienne image.
  • Web : Actions → (redéclencher) ou git revert. Si le code du Worker est défectueux, déployez la version précédente : npx wrangler rollback.
  • Mobile : eas update --branch preview --message "Revert to <hash>" avec le code précédent.

Attention : ne revertez jamais un commit de migration de base de données. Écrivez plutôt une migration inverse (down migration).

🔴 Règle #2 — ne laissez jamais un WORKTREE derrière vous

git worktree list le prouve. Si vous avez créé un worktree, supprimez-le toujours après usage : git worktree remove --force <chemin> puis git worktree prune. Un worktree oublié == fuite de disque + plantages Git.

📍 Côté utilisateur — les agents n'ont PAS de terminal interactif

L'agent utilise un outil Bash (sh), pas un terminal interactif. Pas de sessions persistantes, pas de export qui reste entre les appels, pas de cd qui persiste en dehors du même bloc. Toujours spécifier les chemins absolus ou cd <dossier> && au début de chaque bloc.


Pile & Prérequis

  • Backend : Go, Docker, GHCR, Watchtower
  • Web : Node.js, npm, Vite, Wrangler (Cloudflare Workers) – pas de Docker pour le web
  • Mobile : Node.js, npm, Expo, EAS CLI
  • Déploiement : gh CLI (GitHub), git, psql

Skills associés