My App

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

  1. main est toujours déployable — aucun commit sur main qui casse les tests ou le build
  2. Branches courtes (< 48h) — pas de long-lived feature branches qui dérivent
  3. PRs obligatoires — même pour le lead, même pour l'IA, pas de commit direct sur main
  4. CI verte avant merge — typecheck + biome + tests + file-thresholds
  5. 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, config

Pas 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>.

TypeUsage
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-index

Tout 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

TypeQuand
featnouvelle fonctionnalité
fixbug fix
docsdoc uniquement
refactorrefacto
testtests
choremaintenance
perfoptimisation
styleformatage (rare)
ciCI/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 branch

Pull Requests

Template PR

.github/pull_request_template.md
## 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 strict
  • bun check : biome format + lint
  • bun test : unitaires + intégration (Postgres éphémère)
  • scripts/check-file-sizes.sh : fichiers ≤ 500 lignes
  • bunx 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 :

package.json
"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 production

Créé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.1

Changelog

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 main

Ou :

  1. Dokploy rollback → image précédente (code précédent)
  2. 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 main avant 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-repo ou bfg-repo-cleaner + rotation immédiate du secret
  • Pre-commit hook (optionnel) : gitleaks pour détecter les secrets avant push

Lien

On this page