chart/scripts/update_ownership.sql (a Dolibarr before-start step, embedded into a ConfigMap by chart/templates/scripts-config.yaml) hardcoded the Postgres owner role erp_role. After install it reassigns ownership of allpublic-schema objects to that role.
For any non-prod environment the owner role differs. By the multi-env elision rule (ADR-0002 / ADR-0003) it is snake-case <app>_role for prod and <app>_<env>_role for non-prod — so the sandbox owner role is erp_sandbox_role.
With the literal erp_role, once Dolibarr installs in erp-sandbox the script would reassign sandbox tables to prod'serp_role, which:
(a) breaks the sandbox runtime — its dynamic DB creds are a member of erp_sandbox_role, not erp_role; and
(b) breaks the ADR-0003 reset, which does DROP OWNED BY erp_sandbox_role.
The fix — make the owner role env-aware via a chart value
A new chart value db.ownerRole carries the per-env role:
chart/values.yaml — default ownerRole: erp_role (prod) in the existing db: block.
chart/scripts/update_ownership.sql — all 'erp_role' literals → '{{ .Values.db.ownerRole }}' (single quotes preserved so the rendered output is still a quoted SQL string literal). All four occurrences replaced, so the comment + RAISE NOTICE lines render the real role too.
chart/templates/scripts-config.yaml — render that one SQL file through tpl so the value is substituted: {{- tpl (.Files.Get "scripts/update_ownership.sql") . | nindent 4 }}. The other script (update_conf_db_credentials.sh, no template vars) stays on plain .Files.Get. The SQL's $$, %I, format(...), RAISE NOTICE are not Go-template syntax, so tpl only substitutes the added {{ .Values.db.ownerRole }}.
Proof: prod renders byte-identical
ConfigMap rendered from origin/main vs this branch, both with only chart/values.yaml:
$ helm template erp ./chart --show-only templates/scripts-config.yaml # origin/main → A
$ helm template erp ./chart --show-only templates/scripts-config.yaml # this branch → B
$ diff A B
<<< EMPTY DIFF — PROD IS BYTE-IDENTICAL >>>
Both still contain erp_role. helm lint ./chart passes, no worse than origin/main (same single [INFO] icon is recommended).
Proof: sandbox renders the right role
$ helm template erp ./chart --show-only templates/scripts-config.yaml \
-f chart/values.yaml -f chart/values-sandbox.yaml
...
update_table_ownership.sql: |
...
-- Si le propriétaire actuel est différent de erp_sandbox_role
IF current_schema_owner <> 'erp_sandbox_role' THEN
EXECUTE format('REASSIGN OWNED BY %I TO %I', current_schema_owner, 'erp_sandbox_role');
RAISE NOTICE 'Ownership of all objects in schema "public" has been reassigned from % to %', current_schema_owner, 'erp_sandbox_role';
ELSE
RAISE NOTICE 'No change needed; the owner of schema "public" is already %', 'erp_sandbox_role';
END IF;
END $$;
erp_sandbox_role appears 5× in the rendered SQL; bare 'erp_role' appears 0×.
Scope / follow-up
Full end-to-end validation — that sandbox tables actually end up owned by erp_sandbox_role after a real Dolibarr install — is deferred to Phase E1, when Dolibarr is installed into erp-sandbox. This PR is chart text only (no secrets introduced).
Cross-links: [factory vibe/ADR/0003-sandbox-state-lifecycle.md] — the sandbox state-lifecycle ADR that defines the DROP OWNED BY <app>_<env>_role reset this fix unblocks (also relevant: ADR-0002 per-application-environments / the elision rule).
## The bug
`chart/scripts/update_ownership.sql` (a Dolibarr before-start step, embedded into a ConfigMap by `chart/templates/scripts-config.yaml`) hardcoded the Postgres owner role `erp_role`. After install it reassigns ownership of **all** `public`-schema objects to that role.
For any non-prod environment the owner role differs. By the multi-env elision rule (ADR-0002 / ADR-0003) it is snake-case `<app>_role` for prod and `<app>_<env>_role` for non-prod — so the sandbox owner role is **`erp_sandbox_role`**.
With the literal `erp_role`, once Dolibarr installs in `erp-sandbox` the script would reassign sandbox tables to **prod's** `erp_role`, which:
- **(a)** breaks the sandbox runtime — its dynamic DB creds are a member of `erp_sandbox_role`, not `erp_role`; and
- **(b)** breaks the ADR-0003 reset, which does `DROP OWNED BY erp_sandbox_role`.
## The fix — make the owner role env-aware via a chart value
A new chart value `db.ownerRole` carries the per-env role:
- **`chart/values.yaml`** — default `ownerRole: erp_role` (prod) in the existing `db:` block.
- **`chart/values-sandbox.yaml`** — override `ownerRole: erp_sandbox_role`.
- **`chart/scripts/update_ownership.sql`** — all `'erp_role'` literals → `'{{ .Values.db.ownerRole }}'` (single quotes preserved so the rendered output is still a quoted SQL string literal). All four occurrences replaced, so the comment + `RAISE NOTICE` lines render the real role too.
- **`chart/templates/scripts-config.yaml`** — render that one SQL file through `tpl` so the value is substituted: `{{- tpl (.Files.Get "scripts/update_ownership.sql") . | nindent 4 }}`. The other script (`update_conf_db_credentials.sh`, no template vars) stays on plain `.Files.Get`. The SQL's `$$`, `%I`, `format(...)`, `RAISE NOTICE` are not Go-template syntax, so `tpl` only substitutes the added `{{ .Values.db.ownerRole }}`.
## Proof: prod renders byte-identical
ConfigMap rendered from `origin/main` vs this branch, both with only `chart/values.yaml`:
```
$ helm template erp ./chart --show-only templates/scripts-config.yaml # origin/main → A
$ helm template erp ./chart --show-only templates/scripts-config.yaml # this branch → B
$ diff A B
<<< EMPTY DIFF — PROD IS BYTE-IDENTICAL >>>
```
Both still contain `erp_role`. `helm lint ./chart` passes, no worse than origin/main (same single `[INFO] icon is recommended`).
## Proof: sandbox renders the right role
```
$ helm template erp ./chart --show-only templates/scripts-config.yaml \
-f chart/values.yaml -f chart/values-sandbox.yaml
...
update_table_ownership.sql: |
...
-- Si le propriétaire actuel est différent de erp_sandbox_role
IF current_schema_owner <> 'erp_sandbox_role' THEN
EXECUTE format('REASSIGN OWNED BY %I TO %I', current_schema_owner, 'erp_sandbox_role');
RAISE NOTICE 'Ownership of all objects in schema "public" has been reassigned from % to %', current_schema_owner, 'erp_sandbox_role';
ELSE
RAISE NOTICE 'No change needed; the owner of schema "public" is already %', 'erp_sandbox_role';
END IF;
END $$;
```
`erp_sandbox_role` appears 5× in the rendered SQL; bare `'erp_role'` appears 0×.
## Scope / follow-up
Full end-to-end validation — that sandbox tables actually end up owned by `erp_sandbox_role` after a real Dolibarr install — is **deferred to Phase E1**, when Dolibarr is installed into `erp-sandbox`. This PR is chart text only (no secrets introduced).
Cross-links: [`factory vibe/ADR/0003-sandbox-state-lifecycle.md`] — the sandbox state-lifecycle ADR that defines the `DROP OWNED BY <app>_<env>_role` reset this fix unblocks (also relevant: ADR-0002 per-application-environments / the elision rule).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
The Dolibarr before-start step `chart/scripts/update_ownership.sql` (embedded
into a ConfigMap by `chart/templates/scripts-config.yaml`) hardcoded the
Postgres owner role `erp_role`. It reassigns ownership of all public-schema
objects to that role after install. For any non-prod environment the owner
role differs — by the multi-env elision rule (ADR-0002/0003) it is snake-case
`<app>_role` for prod and `<app>_<env>_role` for non-prod, so the sandbox owner
role is `erp_sandbox_role`. With the literal `erp_role`, installing Dolibarr in
`erp-sandbox` would reassign sandbox tables to prod's `erp_role`, which (a)
breaks the sandbox runtime (its dynamic DB creds are a member of
`erp_sandbox_role`, not `erp_role`) and (b) breaks the ADR-0003 reset
(`DROP OWNED BY erp_sandbox_role`).
Fix: make the owner role env-aware via a new chart value `db.ownerRole`.
- values.yaml: default `ownerRole: erp_role` (prod).
- values-sandbox.yaml: override `ownerRole: erp_sandbox_role`.
- update_ownership.sql: all `'erp_role'` literals → `'{{ .Values.db.ownerRole }}'`.
- scripts-config.yaml: render that one SQL file through `tpl` so the value is
substituted (the other script has no template vars and stays on `.Files.Get`).
The SQL's `$$`, `%I`, `format(...)`, `RAISE NOTICE` are not Go-template syntax,
so `tpl` only substitutes the added `{{ .Values.db.ownerRole }}`.
Verified: the prod ConfigMap render (values.yaml only) is byte-identical to
origin/main (empty diff, still `erp_role`); the sandbox render
(-f values.yaml -f values-sandbox.yaml) now contains `erp_sandbox_role` and no
bare `erp_role`; `helm lint` passes (no worse than origin/main).
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The bug
chart/scripts/update_ownership.sql(a Dolibarr before-start step, embedded into a ConfigMap bychart/templates/scripts-config.yaml) hardcoded the Postgres owner roleerp_role. After install it reassigns ownership of allpublic-schema objects to that role.For any non-prod environment the owner role differs. By the multi-env elision rule (ADR-0002 / ADR-0003) it is snake-case
<app>_rolefor prod and<app>_<env>_rolefor non-prod — so the sandbox owner role iserp_sandbox_role.With the literal
erp_role, once Dolibarr installs inerp-sandboxthe script would reassign sandbox tables to prod'serp_role, which:erp_sandbox_role, noterp_role; andDROP OWNED BY erp_sandbox_role.The fix — make the owner role env-aware via a chart value
A new chart value
db.ownerRolecarries the per-env role:chart/values.yaml— defaultownerRole: erp_role(prod) in the existingdb:block.chart/values-sandbox.yaml— overrideownerRole: erp_sandbox_role.chart/scripts/update_ownership.sql— all'erp_role'literals →'{{ .Values.db.ownerRole }}'(single quotes preserved so the rendered output is still a quoted SQL string literal). All four occurrences replaced, so the comment +RAISE NOTICElines render the real role too.chart/templates/scripts-config.yaml— render that one SQL file throughtplso the value is substituted:{{- tpl (.Files.Get "scripts/update_ownership.sql") . | nindent 4 }}. The other script (update_conf_db_credentials.sh, no template vars) stays on plain.Files.Get. The SQL's$$,%I,format(...),RAISE NOTICEare not Go-template syntax, sotplonly substitutes the added{{ .Values.db.ownerRole }}.Proof: prod renders byte-identical
ConfigMap rendered from
origin/mainvs this branch, both with onlychart/values.yaml:Both still contain
erp_role.helm lint ./chartpasses, no worse than origin/main (same single[INFO] icon is recommended).Proof: sandbox renders the right role
erp_sandbox_roleappears 5× in the rendered SQL; bare'erp_role'appears 0×.Scope / follow-up
Full end-to-end validation — that sandbox tables actually end up owned by
erp_sandbox_roleafter a real Dolibarr install — is deferred to Phase E1, when Dolibarr is installed intoerp-sandbox. This PR is chart text only (no secrets introduced).Cross-links: [
factory vibe/ADR/0003-sandbox-state-lifecycle.md] — the sandbox state-lifecycle ADR that defines theDROP OWNED BY <app>_<env>_rolereset this fix unblocks (also relevant: ADR-0002 per-application-environments / the elision rule).🤖 Generated with Claude Code
The Dolibarr before-start step `chart/scripts/update_ownership.sql` (embedded into a ConfigMap by `chart/templates/scripts-config.yaml`) hardcoded the Postgres owner role `erp_role`. It reassigns ownership of all public-schema objects to that role after install. For any non-prod environment the owner role differs — by the multi-env elision rule (ADR-0002/0003) it is snake-case `<app>_role` for prod and `<app>_<env>_role` for non-prod, so the sandbox owner role is `erp_sandbox_role`. With the literal `erp_role`, installing Dolibarr in `erp-sandbox` would reassign sandbox tables to prod's `erp_role`, which (a) breaks the sandbox runtime (its dynamic DB creds are a member of `erp_sandbox_role`, not `erp_role`) and (b) breaks the ADR-0003 reset (`DROP OWNED BY erp_sandbox_role`). Fix: make the owner role env-aware via a new chart value `db.ownerRole`. - values.yaml: default `ownerRole: erp_role` (prod). - values-sandbox.yaml: override `ownerRole: erp_sandbox_role`. - update_ownership.sql: all `'erp_role'` literals → `'{{ .Values.db.ownerRole }}'`. - scripts-config.yaml: render that one SQL file through `tpl` so the value is substituted (the other script has no template vars and stays on `.Files.Get`). The SQL's `$$`, `%I`, `format(...)`, `RAISE NOTICE` are not Go-template syntax, so `tpl` only substitutes the added `{{ .Values.db.ownerRole }}`. Verified: the prod ConfigMap render (values.yaml only) is byte-identical to origin/main (empty diff, still `erp_role`); the sandbox render (-f values.yaml -f values-sandbox.yaml) now contains `erp_sandbox_role` and no bare `erp_role`; `helm lint` passes (no worse than origin/main). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>