feat(multi-env): Phase B — make factory machinery env-capable (no activation)

ADR-0002 Phase B. Makes postgres/iac, argocd, and the conventions docs
multi-environment-capable WITHOUT activating any sandbox yet — every app
stays prod-only, so this change is behaviour-neutral:
  - postgres/iac `tofu plan` is a no-op (proven: the elision flatten keys
    are bare app names, db=<app>, role=<app>_role — identical addresses)
  - the argocd apps.yaml render is byte-identical (181→181 lines, diff
    empty) since no app declares `envs`

postgres/iac:
- variables.tf: `applications` becomes set(object({name, envs=optional(["prod"])}))
- main.tf: a `local.app_instances` flatten of applications × envs keyed by the
  elided instance id (env=prod → "<app>"); per-app resources iterate it and
  reference each.key / each.value.{database,role}. For prod-only apps every
  resource address + attribute is unchanged. (main.tf also got a full
  `tofu fmt` pass — the pgbouncer function block reindents 4→2 spaces, which
  is cosmetic; the correctness gate is the CI tofu plan, not the text diff.)
- terraform.tfvars: string entries → { name = "..." } objects.

argocd/templates/apps.yaml:
- after the prod Application, a `range $app_attr.envs` loop renders one extra
  Application per non-prod env: name/namespace `<app>-<env>`, shared repoURL,
  helm.valueFiles [values.yaml, values-<env>.yaml], per-env syncPolicy override.
  Renders nothing while no app sets `envs` → prod render unchanged.

docs:
- doc/runbooks/new-web-app/conventions.md (FR, authoritative): new section
  "Plusieurs environnements pour une même app" — elision rule, suffix rule,
  snake-case owner-role exception, erp/erp-sandbox table, ADR-0002 link.
- vibe/guidebooks/lab-ecosystem/naming-conventions.md (EN mirror): the env
  coordinate section + a "Two sandbox models" section reconciling the
  separate-cluster (ADR-0001, names repeat) vs in-cluster sibling (ADR-0002,
  <env> suffix) strategies; Last Updated bumped; ADR-0002 cross-links.

Activation (erp gets envs=["prod","sandbox"] in postgres tfvars + argocd
values + erp/iac) is Phase D, gated by its own plan review.

Refs ADR-0002 (factory#15). Phase A = tools#2 (merged). Phase C = erp#11 (merged).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-28 16:28:28 +02:00
parent 8a1a63ee10
commit c00c4cdd5c
6 changed files with 178 additions and 59 deletions

View File

@@ -44,6 +44,29 @@ Les briques se « branchent » entre elles **par convention de nom**, pas par co
**Utilise un nom court, stable, kebab-case** dès le départ.
**N'introduis pas** de variantes (`my_app` vs `my-app`, `MyApp`, pluriels) : rien ne te préviendra, l'app échouera silencieusement à se connecter ou à se déployer.
## Plusieurs environnements pour une même app
Une application peut être déployée plusieurs fois (prod, sandbox, …) **sans devenir une app distincte** : même dépôt, même chart, même version. On ajoute une seconde coordonnée `<env>` au nom, régie par une **règle d'élision** ([ADR-0002](../../../vibe/ADR/0002-per-application-environments.md)) :
- **`env` vaut `prod` par défaut, et `prod` s'élide.** Quand `env == prod`, **aucun suffixe** n'est ajouté : tous les noms dérivés sont identiques au cas mono-environnement décrit plus haut. Une app existante ne change donc pas (`plan` à vide).
- **Les environnements non-prod prennent le suffixe `<app>-<env>`** en kebab-case partout — base, namespace, chemins/rôles/policies Vault, Application ArgoCD, hôte DNS, sous-préfixe d'état GCS — **à une exception** : le rôle propriétaire PostgreSQL reste en snake-case `<app>_<env>_role`, pour rester cohérent avec le suffixe `_role`.
- **Un seul dépôt et un seul chart** servent tous les environnements ; les différences sont superposées via `values-<env>.yaml`. **Un seul rôle JWT de CI** (`gitea_cicd_<app>`) par dépôt couvre tous ses environnements.
Exemple — `erp` (prod, élidé) vs `erp-sandbox` :
| Système | `erp` (env = prod) | `erp-sandbox` (env = sandbox) |
|---|---|---|
| Base PostgreSQL | `erp` | `erp-sandbox` |
| Rôle propriétaire PG | `erp_role` | `erp_sandbox_role` |
| Namespace + ServiceAccount | `erp` | `erp-sandbox` |
| Creds DB dynamiques Vault | `postgres/creds/erp` | `postgres/creds/erp-sandbox` |
| Secret KV de config | `kvv2/erp/config` | `kvv2/erp-sandbox/config` |
| Application ArgoCD | `erp` | `erp-sandbox` |
| Domaine interne | `erp.arcodange.lab` | `erp-sandbox.arcodange.lab` |
| Dépôt Gitea / chart / JWT CI | `arcodange-org/erp` · chart · `gitea_cicd_erp` | partagés (mêmes valeurs) |
Déclaration : `postgres/iac/terraform.tfvars` et la liste `applications` côté `tools` acceptent `envs = ["prod", "sandbox"]` ; l'omettre revient à `["prod"]`. L'`Application` ArgoCD non-prod se déclare via une clé `envs` sous l'app dans [argocd/values.yaml](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/argocd/values.yaml).
## Références croisées
- [01 · Dépôt Gitea](01-gitea-repo.md) — fixe `<app>` comme nom de dépôt sous `arcodange-org`.