Skip to content

Déploiement (staging)

This content is not available in your language yet.

Ce qu’il faut retenir. L’environnement déployé tient sur une machine Hetzner (arm64, Allemagne). bo deploy up construit le code de votre checkout sur la machine, applique les migrations et redémarre ce qui a changé. La décision est dans l’ADR-0016.

  • postgres, api, worker, mcp, et edge (Caddy, qui sert aussi le desk et l’application joueur).
  • Chaque club a son hôte <club>.rebond.dev, et tout y passe : l’application joueur à la racine, le desk sous /desk, l’API sur ses chemins (/graphql, /auth/…, /oauth/…, /pay/…, /webhooks/…), le serveur MCP sur /mcp.
  • Le site public (ce site) sur le domaine lui-même, https://rebond.dev (site. et www. y redirigent), servi par Caddy depuis le volume site : bo deploy up ne le touche pas, bo deploy site le remplace, depuis apps/site/dist (bo site build ; captures seulement si bo site shots a tourné) ou, avec --run latest (ou --run <id>), depuis l’artefact d’un run de site.yml (bo site publish, gardé 14 jours). Non indexé par les moteurs de recherche. Après l’envoi, la commande attend que le site serve bien la nouvelle version. Ses liens canoniques suivent SITE_URL au moment du build : la variable du dépôt pour site.yml (https://rebond.dev tant que le domaine n’est pas choisi), et en local SITE_URL=https://rebond.dev pnpm bo site build ; bo deploy site refuse un build dont les liens canoniques ne visent pas rebond.dev.
  • Le Storybook (système de design, desk, appli joueur, site) sur https://design.rebond.dev, servi par Caddy depuis le volume design : bo deploy design le remplace, depuis dist/design (bo ui storybook --build), à défaut depuis celui du dernier bo ci main vert, ou, avec --run latest (ou --run <id>), depuis l’artefact design du dernier run réussi de ci.yml lancé à la main sur main (gardé 14 jours). Public et non indexé, comme le site ; les captures du site n’y sont pas (des emplacements « Capture à venir » à la place). Le premier envoi demande un bo deploy up (le volume et le bloc Caddy). Après l’envoi, la commande attend que design.rebond.dev/version.json serve la nouvelle version.
  • Un certificat *.rebond.dev et un pour rebond.dev (Let’s Encrypt, validés par DNS chez Cloudflare).
  • mailpit : tant que SEND_EMAILS / SEND_SMS sont à false, chaque e-mail et SMS arrive dans la boîte de test https://mail.rebond.dev, ouverte sans mot de passe : n’importe qui peut y lire les liens de connexion de staging, qui ne garde donc que des données de test. Brevo, pour les vrais envois, se prépare avec bo deploy notify : il crée l’expéditeur de BO_EMAIL_FROM et les webhooks des statuts de remise (e-mail et SMS), et écrit son jeton dans .env.staging.local. Il faut d’abord que le domaine de l’expéditeur soit authentifié dans Brevo (ses enregistrements DNS sur Cloudflare) ; --check dit ce qui manque sans rien changer. Sur staging, les vrais envois ne durent qu’un moment, pour un essai : avec SEND_EMAILS=true, bo deploy up refuse tant que Brevo n’est pas prêt, et retire la page « comptes de test » (l’api ne démarre pas avec les deux). Pendant ce temps, les rappels des clubs de démonstration partent aussi, vers des adresses @<slug>.example.test qui n’existent pas. Avec BO_CAPTURE_WEBHOOKS=true, l’api écrit dans ses journaux chaque statut de remise tel que Brevo l’envoie (bo deploy logs api), pour remplacer les exemples des tests (fait pour les e-mails ; ceux des SMS suivent la doc de Brevo jusqu’à une vraie capture, #415). Revenir à SEND_EMAILS=false (et BO_CAPTURE_WEBHOOKS=false) puis bo deploy up rétablit Mailpit et la page.
  • SMS : SEND_SMS=true demande en plus BO_SMS_SENDER (11 lettres ou chiffres au plus, ou un numéro) et des crédits SMS achetés dans Brevo. Les numéros des clubs de démonstration sont dans la plage fictive de l’ARCEP : leurs rappels échouent, mais consomment des crédits.
  • Sauvegardes : celles de Hetzner chaque jour, plus un pg_dump chaque nuit sur la machine, gardé 7 jours.

Tout est dans .env.staging.local à la racine du checkout principal (les clés attendues sont dans .env.staging.example) (ignoré par git, lié dans chaque worktree) : les jetons Hetzner et Cloudflare, les clés de la sandbox Stripe « Rebond Staging » et ses deux secrets de webhook (écrits par bo deploy webhooks), les clés R2 du bucket média (bo site media, elles ne quittent jamais votre poste), et les secrets que bo deploy init génère (session, mots de passe Postgres). Les autres clés de ce fichier (SEND_EMAILS, PLATFORM_FEE_BP…) partent telles quelles vers les applications.

Fenêtre de terminal
pnpm bo deploy init # la machine, son pare-feu, ses sauvegardes, le DNS rebond.dev et *.rebond.dev (idempotent)
pnpm bo deploy up # construit, migre, redémarre, vérifie que seul Caddy publie des ports, attend /health
pnpm bo deploy prune # libère le disque : garde l'image en service et la plus récente des autres, vide le cache de build au-delà de 4 Go
pnpm bo deploy site # le site public sur rebond.dev, depuis apps/site/dist
pnpm bo deploy site --run latest # depuis l'artefact du dernier run réussi de site.yml (ou --run <id>), captures comprises
pnpm bo deploy design # le Storybook sur design.rebond.dev, depuis dist/design (bo ui storybook --build ; sinon celui du dernier bo ci main vert)
pnpm bo deploy design --run latest # depuis l'artefact design du dernier ci.yml lancé à la main (gh workflow run) et réussi sur main
pnpm bo deploy webhooks # les deux webhooks Stripe (plateforme, Connect) sur api.rebond.dev, version d'API figée (idempotent)
pnpm bo deploy notify # Brevo pour les vrais envois : l'expéditeur, les webhooks des statuts de remise, e-mail et SMS (idempotent ; --check : sans rien changer)
pnpm bo deploy status # les services, la santé de l'API, le disque libre (alerte sous 20 %) ; vérifie que seuls ssh et Caddy (22, 80, 443) sont ouverts
pnpm bo deploy logs api --follow
pnpm bo deploy psql # la base, en propriétaire
pnpm bo platform clubs --target staging # une commande bo sur la machine, à côté de sa base

Chaque bo deploy up construit ses images (application et Caddy) sous le nom du commit : une fois la nouvelle version en ligne et /health vert, il supprime nos autres images, en gardant celles de la version qui tournait avant (pour revenir en arrière) ; le cache de build est ramené à 4 Go. Un up qui échoue après la construction ne supprime rien. up et status signalent un disque libre sous 20 % ; sous ce seuil, up fait d’abord le ménage avant de construire (en gardant la version en service). bo deploy prune le fait sans déployer.

Créer un club sur staging : pnpm bo club create <slug> --owner <e-mail> --legal-name … --siret … --legal-address … --target staging. Le propriétaire reçoit une invitation (dans mail.rebond.dev tant que SEND_EMAILS=false) : le lien ouvre /desk/invitation, où il choisit son mot de passe. Pour inviter le propriétaire d’un club existant : pnpm bo club invite-owner <slug> <e-mail> --target staging (le lien donne un premier mot de passe, il ne remplace jamais un mot de passe existant). Configurer un club sans terrains d’un coup : pnpm bo club setup <slug> --preset bo|padel|tennis|squash|demo --target staging (ou --preset dès club create). Les quatre premiers posent la configuration : horaires, sports, terrains, périodes, tarifs, formules ; le club garde son nom de site et son identité ; ni utilisateurs ni argent ; refusé si le club a déjà des terrains, sports, périodes, tarifs, formules ou options. demo pose la configuration de Bø puis donne vie au club : environ 90 joueurs fictifs en @<slug>.example.test, sans mot de passe, une semaine de réservations passées et à venir, paiements au comptoir, portefeuilles, abonnements, formules vendues, un cours collectif ; il faut un propriétaire. Les joueurs se connectent par lien magique, reçu dans Mailpit. Mot de passe des comptes de test : avec --test-password, club create --owner et club invite-owner donnent au propriétaire le mot de passe BO_TEST_ACCOUNT_PASSWORD de .env.staging.local (8 caractères ou plus), sauf s’il en a déjà un ; son invitation ne s’accepte alors qu’avec ce mot de passe. Réservé aux comptes de test, jamais ailleurs que sur staging ou en local. Avec club invite-owner, --test-password --reset remplace le mot de passe du compte nommé dans la commande, et de lui seul. Sur staging, club create sans --owner donne au club le propriétaire owner.<slug>@rebond.dev, une adresse du domaine de staging : un envoi réel ne lui parvient que si ce domaine a une messagerie (enregistrements MX, boîte « catch-all »). Un compte accueil et un compte coach pour essayer chaque rôle : pnpm bo club test-accounts <slug> --target staging fait de staff.<slug>@rebond.dev un compte accueil et de coach.<slug>@rebond.dev un coach du club, sans invitation, avec le mot de passe de test (il faut BO_TEST_ACCOUNT_PASSWORD ; refusé ailleurs qu’en local ou sur staging ; --reset remplace le mot de passe qu’ils ont déjà ; un responsable du club le reste ; en local : staff@<slug>.test, coach@<slug>.test). Donner à un utilisateur existant les droits de la plateforme (tous les clubs, comme leur responsable) : pnpm bo platform grant-admin <e-mail> --target staging (journalisé). Page « comptes de test » : https://comptes.rebond.dev liste chaque club (desk, appli joueur), son responsable, son accueil, son coach et trois joueurs, avec un lien vers leurs e-mails dans Mailpit ; le bouton « Envoyer le lien » envoie à un joueur son lien de connexion. On s’y connecte avec un compte administrateur de la plateforme : pnpm bo platform grant-admin admin@rebond.dev --test-password --target staging le crée avec BO_TEST_ADMIN_PASSWORD de .env.staging.local, un secret à part, jamais celui des comptes de test donné aux testeurs (refusé pour un compte existant sans mot de passe ; un compte à part : un responsable de club qui devient administrateur voit chaque desk avec ces droits). Chaque lien envoyé est journalisé. Hors staging (BO_ACCOUNTS_HOST vide), pas de page ; l’api refuse de démarrer avec la page si SEND_EMAILS=true. Changer l’adresse de connexion d’un utilisateur, pour tous ses clubs : pnpm bo platform change-email <ancienne> <nouvelle> --target staging (journalisé ; mot de passe, sessions et rôles restent ; la nouvelle adresse reste à vérifier ; les invitations en attente la suivent ; refusé si elle est déjà prise). Paiement par carte d’un club de staging : pnpm bo stripe connect --club <slug> --target staging crée son compte Standard dans la sandbox de staging, pré-rempli des données de test de Stripe ; reste à accepter les conditions sur la page Stripe affichée. --email <adresse> choisit l’adresse du compte (Stripe y envoie sa vérification ; une adresse en .test ne reçoit rien). --replace crée un nouveau compte puis supprime de la sandbox celui resté en attente ; refusé si le compte a déjà été activé. Les données de test (bo e2e setup) refusent toute base qui n’est pas locale : ses mots de passe sont connus. --target staging n’accepte que club, platform, stripe connect et stripe reconcile. La commande tourne sur la machine, dans un conteneur à côté de la base, avec le code du dernier bo deploy up : une commande modifiée localement n’y arrive qu’après un bo deploy up. Ses réglages, eux, sont ceux de votre .env.staging.local au moment de la commande.