# 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 : ```bash 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 `.ts` → `pnpm 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) ```bash 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.