feat(multi-env): Phase B — factory machinery env-capable (no activation) #16
Reference in New Issue
Block a user
Delete Branch "claude/multi-env-phaseb"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Phase B of ADR-0002. Makes
postgres/iac,argocd, and the conventions docs multi-environment-capable without activating any sandbox. Every app stays prod-only, so this PR is behaviour-neutral — the machinery is in place but inert until Phase D flips a switch.Why it's a no-op (proven, not asserted)
postgres/iac — the
applicationsvariable becomesset(object({name, envs=optional([\"prod\"])})), and alocal.app_instancesflatten mapsapplications × envs→ instances keyed by the elided id. For a prod-only app the key is the bare name,database=<app>,role=<app>_role— character-for-character identical to the oldset(string)for_each. I verified the flatten output with a standalonetofu apply:So
postgresql_database.app_db[\"erp\"],postgresql_role.app_role[\"erp\"](nameerp_role), etc. keep their exact Terraform addresses and attributes → no resource is dropped or recreated.argocd —
apps.yamlgains arange $app_attr.envsloop after the prod Application. Since no app declaresenvs, it renders nothing extra:helm templatebefore vs after diffs to empty (verified). When an app does declareenvs(Phase D), it additionally renders<app>-<env>Applications (shared repoURL,values-<env>.yamloverlay, own namespace) — I previewederp.envs.sandboxand it produced a correcterp-sandboxApplication alongside the unchangederpone.Files
postgres/iac/variables.tfapplications→set(object({name, envs}))postgres/iac/main.tflocal.app_instanceselision flatten; per-app resources iterate it viaeach.key/each.value.{database,role}. Also a fulltofu fmtpass (cosmetic — the pgbouncer function block reindents 4→2 spaces).postgres/iac/terraform.tfvars{ name = "..." }argocd/templates/apps.yamlenvs)doc/runbooks/new-web-app/conventions.mdvibe/guidebooks/lab-ecosystem/naming-conventions.md<env>suffix in-cluster);Last Updatedbumped; ADR-0002 cross-linksNote on the guidebook reconciliation
The existing naming-conventions guidebook argued names repeat across environments (and disparaged "erp-staging-style aliases"). That holds for the separate-cluster sandbox (ADR-0001). ADR-0002's in-cluster sibling has no cluster fence, so the
<env>suffix is the disambiguator. I reframed that section to present both models cleanly rather than leave the contradiction.Verification done locally
tofu validate(postgres/iac) passes;tofu fmt -checkclean on my 3 tf filestofu applyhelm templatediff empty (prod render unchanged); Phase-D preview renderserp-sandboxcorrectlyLast Updatedstamps bumpedtofu planno-op on postgres/iac ← the merge gate (please confirm)Not in this PR
erp-sandbox(addenvs=[\"prod\",\"sandbox\"]to erp in postgres tfvars + argocd values + erpiac/main.tffor_each). Real resources appear there, gated by its own plan review.erp-sandbox.arcodange.lab+ write-scopedai_agent_sandboxDolibarr user.🤖 Generated with Claude Code
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>