Git workflow
Trunk-based, branches, commits conventionnels, PRs, reviews, tags, releases
Trunk-based development : une seule branche longue (
main), feature branches courtes (< 2 jours), PRs obligatoires, merge squash.
Principes
mainest toujours déployable — aucun commit surmainqui casse les tests ou le build- Branches courtes (< 48h) — pas de long-lived feature branches qui dérivent
- PRs obligatoires — même pour le lead, même pour l'IA, pas de commit direct sur
main - CI verte avant merge — typecheck + biome + tests + file-thresholds
- Squash merge — chaque PR = un commit propre sur main (historique linéaire)
Structure des branches
main # trunk, toujours green
├── feat/cardex-bulk-vip # feature branch
├── fix/check-in-race # bug fix branch
├── refactor/... # refacto
└── chore/... # deps, CI, configPas de develop, pas de staging. Un seul trunk. Les déploiements staging/prod se font depuis main avec des tags.
Naming des branches
Format : <type>/<module>-<description-courte>.
| Type | Usage |
|---|---|
feat/ | nouvelle feature |
fix/ | bug fix |
refactor/ | refacto sans changement de comportement |
docs/ | doc uniquement (fumadocs, CLAUDE, README) |
test/ | ajout / correction tests |
chore/ | maintenance (deps, CI, config biome) |
perf/ | optimisation perf |
style/ | formatage uniquement (rare, souvent inclus dans autre) |
Exemples :
feat/cardex-bulk-vip
feat/chat-sse-stream
fix/check-in-race-condition
fix/mews-webhook-hmac-verification
refactor/integrations-bridge-retry
docs/adr-state-machines
test/mews-adapter-contract
chore/upgrade-elysia-1.5
perf/cardex-query-indexTout en kebab-case, pas d'underscore, pas de CamelCase.
Commits
Format imposé : Conventional Commits
<type>(<scope>): <description>
[body optionnel]
[footer optionnel — refs issue, breaking change]Types
| Type | Quand |
|---|---|
feat | nouvelle fonctionnalité |
fix | bug fix |
docs | doc uniquement |
refactor | refacto |
test | tests |
chore | maintenance |
perf | optimisation |
style | formatage (rare) |
ci | CI/CD config |
Règles sur le message
- Pas de majuscule au début du sujet
- Pas de point final
- Mode impératif ("add" pas "added" pas "adds")
- Sujet ≤ 72 caractères
- Body : expliquer le pourquoi plus que le quoi
- Scope : le module principal concerné (
cardex,mews-adapter,check-in-machine, …)
Exemples
feat(cardex): add bulk VIP toggle
fix(check-in): prevent race on linkGuestToUser when session hydration lags
docs(adr): add state machines decision
test(mews-adapter): cover contract test for reservations
chore(ci): bump bun to 1.3.8
refactor(integrations/bridge): extract retry logic into shared helper
perf(cardex): add composite index (org_id, check_in_status)Commits locaux
En local, tu peux commiter en vrac (wip, fix typo, etc.). À la PR, on squash merge vers main → un seul commit propre. Pas besoin de rebase interactive pour nettoyer.
Flow complet d'une feature
1. git pull --rebase origin main
2. git checkout -b feat/cardex-bulk-vip
3. code + tests + docs
4. git commit -m "wip" (localement, libre)
5. ... itérations ...
6. git rebase -i main (clean local si tu veux, optionnel)
7. git push origin feat/cardex-bulk-vip
8. Open PR on GitHub
9. CI runs → green
10. Code review demandée
11. Réponses aux remarques + commits supplémentaires
12. Approval → Squash merge avec titre = feat(cardex): add bulk VIP toggle
13. Delete branchPull Requests
Template PR
## Quoi
<en 1-3 phrases : ce que ça apporte fonctionnellement>
## Pourquoi
<contexte, lien vers ADR si décision structurante, ticket client si applicable>
## Changements
- [ ] Changement 1
- [ ] Changement 2
## Tests
- [ ] Unitaires ajoutés / mis à jour
- [ ] Intégration ajoutés / mis à jour
- [ ] E2E impact (si oui, lesquels)
## Doc
- [ ] CLAUDE.md mis à jour si changement structurel
- [ ] PROGRESS.md cochée si jalon
- [ ] Fumadocs mis à jour si API/archi changée
- [ ] ADR créé si décision structurante
## Breaking change
- [ ] Oui / Non — si oui détailler
## Screenshots / GIFs
<si UI>
## Comment tester
<instructions précises pour le reviewer>Merge bloqué tant que la checklist n'est pas respectée (ou justifié en commentaire si case inutile).
Reviews
- Chaque PR = 1 reviewer minimum
- Le lead review toutes les PRs feat/refactor
- Les PRs docs/chore/tests peuvent être review par un pair
- Aucun self-merge — même le lead fait review par quelqu'un
Répondre aux remarques
- Adresser chaque commentaire : soit en modifiant le code, soit en expliquant pourquoi pas (poliment)
- Push les modifs sur la même branche, la PR est update automatiquement
- Ne pas résoudre les threads soi-même — c'est le reviewer qui résout après vérification
CI obligatoire
Blocage du merge si une des checks fail :
bun check-types: typecheck strictbun check: biome format + lintbun test: unitaires + intégration (Postgres éphémère)scripts/check-file-sizes.sh: fichiers ≤ 500 lignesbunx playwright test: 3 flows E2E
La CI tourne en ~3-5 min au total.
Hooks locaux (Husky + lint-staged)
Déjà configurés dans le monorepo :
"lint-staged": {
"*.{js,ts,cjs,mjs,d.cts,d.mts,jsx,tsx,json,jsonc}": [
"biome check --write ."
]
},
"scripts": {
"prepare": "husky"
}Au pre-commit : Biome format + lint fix auto sur les fichiers modifiés.
Au commit-msg : (à ajouter) validation conventionnelle via @commitlint/config-conventional.
Merge strategy
Squash merge par défaut sur main. Pourquoi :
- Historique linéaire et lisible (un commit = une feature)
- Message de commit bien rédigé (celui de la PR)
- Rollback facile (revert d'un commit = revert d'une feature entière)
Pas de merge commit (évite les branches en étoile dans le graphe).
Pas de rebase merge (on perd le contexte de la PR et les SHAs changent).
Tags et releases
Tags
Format : v<MAJOR>.<MINOR>.<PATCH> (SemVer).
v0.1.0 # MVP interne HOAIY
v0.2.0 # ajout du module AI concierge
v0.2.1 # fix bug check-in race
v1.0.0 # première release client productionCréés depuis main après QA OK :
git checkout main
git pull
git tag -a v0.2.1 -m "fix(check-in): prevent race on linkGuestToUser"
git push origin v0.2.1Changelog
Généré automatiquement depuis les commits conventionnels via git-cliff ou release-please. Stocké dans CHANGELOG.md racine.
Releases GitHub
Une release par tag, avec :
- Titre :
v0.2.1 — 2026-04-25 - Notes auto-générées depuis le changelog
- Liés aux PRs concernées
Déploiement
- Staging : déployé automatiquement à chaque push sur
main(via Dokploy webhook) - Prod : déployé manuellement sur tag (trigger Dokploy avec tag spécifique)
Rollback
Revert d'une feature
git revert <commit-sha> # crée un commit qui annule
git push origin mainOu :
- Dokploy rollback → image précédente (code précédent)
- Si changement de schema DB :
drizzle-kit migrate:rollback
Rebase d'une branche obsolète
Si main a bougé pendant ta PR :
git checkout feat/cardex-bulk-vip
git fetch origin
git rebase origin/main
# résoudre les conflits si besoin
git push --force-with-lease--force-with-lease au lieu de --force — refuse de push si quelqu'un d'autre a push entre temps (évite d'écraser le travail de quelqu'un).
Branches protégées
main est protégée sur GitHub :
- Require PR avant merge
- Require status checks to pass
- Require up-to-date avec
mainavant merge - No force push
- No deletion
- Lineair history required (cohérent avec squash merge)
Secrets
- Jamais de secret commité en clair
.env*dans.gitignore(sauf.env.example)- En cas de commit accidentel :
git-filter-repooubfg-repo-cleaner+ rotation immédiate du secret - Pre-commit hook (optionnel) :
gitleakspour détecter les secrets avant push
Lien
- File thresholds — vérifié en CI
- Testing — tests obligatoires
- Code style — Biome config
- Onboarding hôtel — étape go-live après deploy prod