fix(deploy): init container migrate utilise build/ace.js (compilé)
All checks were successful
Build & Deploy App / build-and-deploy (push) Successful in 30s

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>
This commit is contained in:
ordinarthur 2026-05-07 02:50:26 +02:00
parent f6776dd81c
commit cc047013c6
2 changed files with 129 additions and 39 deletions

View File

@ -1,52 +1,139 @@
# Deploy memory — rubis
## Infra
- Namespace : `rubis`
- Deployment : `rubis`
- Container : `rubis`
- NodePort : `30109`
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 pas encore acheté)
- Manifests : `k3s/` (namespace.yml, deployment.yml, service.yml)
- 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`
- Repo Gitea : https://git.arthurbarre.fr/ordinarthur/rubis (remote `gitea`)
- Workflow CI : `.gitea/workflows/deploy.yml`
- Source : `landing/` (servi par nginx)
## Mise à jour (à suivre pour les prochains /deploy)
### Mise à jour
Push git, le CI build + rollout auto (filter sur `landing/**`, `Dockerfile`,
`k3s/{namespace,deployment,service}.yml`).
1. **Voie normale — push git, le CI build + rollout auto** :
```bash
git push gitea main
```
---
2. **Voie manuelle** (si Docker Desktop tourne sur le Mac) :
```bash
TAG=$(git rev-parse --short HEAD)
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
kubectl --kubeconfig ~/dev/perso/proxmox/k3s/kubeconfig.yaml \
-n rubis rollout status deploy/rubis
```
## 2. App SaaS (`app.rubis.arthurbarre.fr`)
3. Si changement route/domaine : `cd ~/dev/perso/proxmox/ansible && ansible-playbook playbooks/gateway.yml`
### 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)
4. Vérif : `curl -I https://rubis.arthurbarre.fr`
### 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
## Quand le domaine définitif sera acheté
### 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 -
```
1. Créer le record A chez OVH (ou autre registrar) : `<nouveau-domaine>``51.38.62.199`
2. Modifier la rule du fichier `~/dev/perso/proxmox/ansible/roles/traefik/templates/rubis.yml.j2` :
`Host(\`<nouveau-domaine>\`)`
3. Relancer le playbook `gateway.yml`
4. (Optionnel) supprimer le record DNS `rubis.arthurbarre.fr` si plus utilisé
### 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/**`.
## Stack
Landing statique, servie par **nginx:1.27-alpine** (Dockerfile à la racine, sert `public/`).
Le rebuild est rapide (pas de bundler).
### 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, manifests K3s, route Traefik (`rubis.yml.j2` + tâche dans `tasks/main.yml`),
DNS OVH (A record `rubis.arthurbarre.fr` id 5413044152), repo Gitea + secrets CI
(`KUBECONFIG`, `REGISTRY_PASSWORD`), namespace + secret registry K3s.
- 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.

View File

@ -30,8 +30,11 @@ spec:
initContainers:
- name: migrate
image: git.arthurbarre.fr/ordinarthur/rubis-app:latest
workingDir: /app/apps/api
command: ['node', 'ace', 'migration:run', '--force']
# On exécute ace depuis build/ (compilé JS) — le shim ace.js de
# /app/apps/api/ charge bin/console.ts (TS) qui n'a pas de loader
# disponible en runtime sans devDeps.
workingDir: /app/apps/api/build
command: ['node', 'ace.js', 'migration:run', '--force']
envFrom:
- secretRef: { name: rubis-app-secrets }
- configMapRef: { name: rubis-app-config }