rubis/.claude/deploy-memory.md
ordinarthur cc047013c6
All checks were successful
Build & Deploy App / build-and-deploy (push) Successful in 30s
fix(deploy): init container migrate utilise build/ace.js (compilé)
Le pod plantait en Init:CrashLoopBackOff parce que le init container
tournait depuis /app/apps/api avec \`node ace\` — qui charge le shim
ace.js → bin/console.ts (TS source). Sans devDeps en runtime, pas de
loader TS → ERR_UNKNOWN_FILE_EXTENSION.

Fix : workingDir /app/apps/api/build + command \`node ace.js
migration:run --force\`. build/ contient les .js compilés.

Memory mise à jour pour documenter ce piège.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 02:50:26 +02:00

5.9 KiB

Deploy memory — rubis

Ce repo contient deux déploiements distincts sur la même infra K3s, namespace rubis :

  1. Landing statique (rubis.arthurbarre.fr) — image nginx-alpine.
  2. App SaaS (app.rubis.arthurbarre.fr) — image AdonisJS + React (V1).

Chacun a sa propre image, son propre Dockerfile, son propre workflow CI.


1. Landing (rubis.arthurbarre.fr)

Infra

  • Deployment : rubis · Container : rubis · NodePort : 30109
  • Image : git.arthurbarre.fr/ordinarthur/rubis
  • Domaine : https://rubis.arthurbarre.fr (sous-domaine temporaire — domaine définitif rubis-sur-l-ongle.fr pas encore acheté)
  • Manifests : k3s/{namespace,deployment,service}.yml
  • Route Traefik : ~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis.yml.j2
  • Workflow CI : .gitea/workflows/deploy.yml
  • Source : landing/ (servi par nginx)

Mise à jour

Push git, le CI build + rollout auto (filter sur landing/**, Dockerfile, k3s/{namespace,deployment,service}.yml).


2. App SaaS (app.rubis.arthurbarre.fr)

Infra

  • Deployment : rubis-app · Container : app · NodePort : 30110
  • Init container migrate : node ace.js migration:run --force depuis /app/apps/api/build (idempotent)
  • Sidecar Redis : Deployment rubis-redis (ClusterIP, PVC 1Gi local-path, backend BullMQ + cache)
  • Image : git.arthurbarre.fr/ordinarthur/rubis-app
  • Domaine : https://app.rubis.arthurbarre.fr
  • Manifests : k3s/app/{deployment.yml,service.yml,redis.yml}
  • Route Traefik : ~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis-app.yml.j2
  • Workflow CI : .gitea/workflows/deploy-app.yml
  • Source : apps/api, apps/web, packages/shared (monorepo pnpm)

Dépendances externes (déjà déployées)

  • Postgres : 10.10.10.3:5432, base rubis_prod, user rubis
  • MinIO : minio.minio.svc.cluster.local:9000, bucket rubis-prod-invoices, creds = root MinIO
  • Redis : interne au namespace rubis, pas de dépendance externe

Secrets K3s (rubis-app-secrets)

Posés une fois manuellement via kubectl create secret generic (jamais dans les manifests). Re-pose si rotation ou ajout :

kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
  -n rubis create secret generic rubis-app-secrets \
  --from-literal=APP_KEY=... \
  --from-literal=PG_PASSWORD=... \
  --from-literal=S3_ACCESS_KEY=... \
  --from-literal=S3_SECRET_KEY=... \
  --from-literal=RESEND_API_KEY=... \
  --from-literal=MISTRAL_API_KEY=... \
  --from-literal=REDIS_PASSWORD="" \
  --dry-run=client -o yaml | kubectl apply -f -

Mise à jour

Push git, le CI build l'image (Dockerfile.app, multi-stage), apply les manifests k3s/app/, set image, rollout auto. Filter sur apps/**, packages/**, Dockerfile.app, k3s/app/**.

Particularités du Dockerfile.app

  • node ace build plante en CI avec ERR_UNKNOWN_FILE_EXTENSION car @poppinss/ts-exec ne s'enregistre pas à temps avant l'import de bin/console.ts. Solution : on appelle ace via tsx (esbuild), qui gère nativement les .tspnpm exec tsx ace.js build --ignore-ts-errors
  • --ignore-ts-errors car tests/bootstrap.ts référence un type généré dans .adonisjs/client/registry/schema.d.ts qui arrive trop tard. Le typecheck strict est exécuté côté CI séparément (pnpm typecheck).
  • Vite build appelé directement (pnpm exec vite build) au lieu de pnpm build qui fait tsc -b && vite build — le tsc -b plante sans cache .tsbuildinfo à cause de @tanstack/router-core.
  • SPA dist (apps/web/dist) copié dans apps/api/build/public/ pour être servi par le static middleware AdonisJS. Une route wildcard (start/routes.ts) sert index.html pour les chemins non-API → SPA routing TanStack Router.

Voie manuelle (debug, hors CI)

TAG=$(git rev-parse --short HEAD)
# Landing
docker build --platform linux/amd64 -t git.arthurbarre.fr/ordinarthur/rubis:$TAG .
docker push git.arthurbarre.fr/ordinarthur/rubis:$TAG
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
  -n rubis set image deploy/rubis rubis=git.arthurbarre.fr/ordinarthur/rubis:$TAG
# App
docker build --platform linux/amd64 -f Dockerfile.app -t git.arthurbarre.fr/ordinarthur/rubis-app:$TAG .
docker push git.arthurbarre.fr/ordinarthur/rubis-app:$TAG
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
  -n rubis set image deploy/rubis-app app=git.arthurbarre.fr/ordinarthur/rubis-app:$TAG

⚠️ Cross-compile ARM Mac → linux/amd64 plante sur @swc/core au build de l'app. Préférer pousser et laisser le CI Gitea (linux/amd64 natif).

Si changement route/domaine : cd ~/dev/perso/proxmox/ansible && ansible-playbook playbooks/gateway.yml. Penser à sudo systemctl restart traefik sur la gateway si un nouveau domaine ne récupère pas son cert ACME tout seul (le hot-reload de la dynamic config ne déclenche pas toujours le challenge LE).


Quand le domaine définitif sera acheté (rubis-sur-l-ongle.fr)

  1. Records DNS chez OVH : A @, A app, MX/SPF/DKIM pour Resend
  2. Modifier les rules dans rubis.yml.j2 et rubis-app.yml.j2 (Host)
  3. Replay gateway.yml
  4. Maj MAIL_FROM_ADDRESS=relances@rubis-sur-l-ongle.fr dans le secret
  5. (Optionnel) supprimer les A records rubis.arthurbarre.fr et app.rubis.arthurbarre.fr chez OVH

Déjà fait — NE PAS refaire

  • Dockerfile (landing) + Dockerfile.app (app)
  • Manifests k3s/ (landing) + k3s/app/ (app + Redis)
  • Routes Traefik rubis.yml.j2 + rubis-app.yml.j2
  • DNS OVH : A records rubis (id 5413044152) + app.rubis (id 5413305619)
  • Repo Gitea + secrets CI (KUBECONFIG, REGISTRY_PASSWORD)
  • Namespace + secret registry K3s (gitea-registry)
  • Postgres : base rubis_prod + user rubis (10.10.10.3)
  • MinIO : bucket rubis-prod-invoices
  • Secret K3s rubis-app-secrets (APP_KEY, DB pwd, MinIO, Resend, Mistral)

Les prochains /deploy font uniquement build + rollout via push git.