From 44a6972b44c80d24254ea4412889cb6848b3cfb9 Mon Sep 17 00:00:00 2001 From: OdooSky v3 Date: Fri, 8 May 2026 23:26:52 +0200 Subject: [PATCH] fix(hoot-dom): platform shim addon to inject hoot-dom into web.assets_backend --- Chart.yaml | 2 +- templates/odoo-deployment.yaml | 104 +++++++++++++++++++++++++++++---- 2 files changed, 94 insertions(+), 12 deletions(-) diff --git a/Chart.yaml b/Chart.yaml index 5c82102..38cd350 100644 --- a/Chart.yaml +++ b/Chart.yaml @@ -5,7 +5,7 @@ description: | Variation between instances is expressed via values.yaml only. No chart variants. No string-templating in Tower. type: application -version: 0.1.1 +version: 0.1.2 appVersion: "1.0" keywords: - odoo diff --git a/templates/odoo-deployment.yaml b/templates/odoo-deployment.yaml index a38c40e..07147e4 100644 --- a/templates/odoo-deployment.yaml +++ b/templates/odoo-deployment.yaml @@ -91,6 +91,56 @@ spec: volumeMounts: - name: filestore mountPath: /var/lib/odoo + # platform-shim: workaround for upstream Odoo manifest bug. + # Materializes a tiny addon (`odoosky_hoot_dom_shim`) into the + # shared addons volume; the addon injects + # `web/static/lib/hoot-dom/**/*` into the `web.assets_backend` + # bundle. + # + # Why: Odoo 18.0 nightlies (verified through 2026-05-08) + # include production source files + # (`barcodes/static/src/barcode_handlers.js`, + # `web_tour/.../tour_step_automatic.js`, + # `sale/.../tour_utils.js`) that `import '@odoo/hoot-dom'`, + # but the upstream `web/__manifest__.py` only places the + # hoot-dom library in `web.assets_unit_tests_setup` (the test + # bundle). Production bundle ends up with dangling references + # and the browser fails to bootstrap. erp18 incident + # 2026-05-08 — surfaced after a heavy addon install pulled + # barcode_handlers.js into the active backend bundle. + # + # Remove this initContainer (and the shim install step in + # db-init) once upstream patches the manifest. + - name: platform-shim + image: {{ include "instance.odooImage" . | quote }} + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "-c"] + args: + - | + set -eu + mkdir -p /target/odoosky_hoot_dom_shim + : > /target/odoosky_hoot_dom_shim/__init__.py + cat > /target/odoosky_hoot_dom_shim/__manifest__.py <<'PYEOF' + { + 'name': 'OdooSky hoot-dom backend shim', + 'version': '18.0.1.0.0', + 'category': 'Hidden', + 'summary': 'Inject web/static/lib/hoot-dom/**/* into web.assets_backend (workaround for upstream manifest bug)', + 'depends': ['web'], + 'assets': { + 'web.assets_backend': [ + 'web/static/lib/hoot-dom/**/*', + ], + }, + 'installable': True, + 'auto_install': True, + 'license': 'LGPL-3', + } + PYEOF + echo "── platform shim materialized at /target/odoosky_hoot_dom_shim ──" + volumeMounts: + - name: addons + mountPath: /target # db-init: idempotent on every pod boot. # 1. createdb if missing (PG-level) # 2. odoo -i base --stop-after-init to install base module @@ -182,6 +232,30 @@ spec: --db_host="$HOST" --db_port="$PORT" --db_user="$USER" --db_password="$PASSWORD" \ --workers=0 fi + # Install the platform-shim addon if not already + # installed. The shim's files were materialized into + # the addons volume by the platform-shim initContainer + # above; here we just register them in this DB. Once + # registered, Odoo's asset machinery emits an ir.asset + # row that adds web/static/lib/hoot-dom/**/* to the + # web.assets_backend bundle on next regeneration. See + # platform-shim initContainer for the why. + SHIM_INSTALLED="" + IS_INIT_AFTER=$(PGPASSWORD="$PASSWORD" psql -h "$HOST" -p "$PORT" -U "$USER" -d "$DBNAME" -tAc \ + "SELECT 1 FROM information_schema.tables WHERE table_schema='public' AND table_name='ir_module_module'" 2>/dev/null || true) + if [ "$IS_INIT_AFTER" = "1" ]; then + SHIM_INSTALLED=$(PGPASSWORD="$PASSWORD" psql -h "$HOST" -p "$PORT" -U "$USER" -d "$DBNAME" -tAc \ + "SELECT 1 FROM ir_module_module WHERE name='odoosky_hoot_dom_shim' AND state='installed'" 2>/dev/null || true) + fi + if [ "$SHIM_INSTALLED" = "1" ]; then + echo "── odoosky_hoot_dom_shim already installed — skipping ──" + else + echo "── installing odoosky_hoot_dom_shim (hoot-dom workaround) ──" + odoo -i odoosky_hoot_dom_shim -d "$DBNAME" --stop-after-init --no-http \ + --db_host="$HOST" --db_port="$PORT" --db_user="$USER" --db_password="$PASSWORD" \ + --workers=0 \ + --addons-path="/usr/lib/python3/dist-packages/odoo/addons,{{ .Values.addonsMountPath }}" + fi echo "── db-init done ──" env: - name: HOST @@ -198,6 +272,14 @@ spec: secretKeyRef: name: {{ include "instance.fullname" . }}-pg key: POSTGRES_PASSWORD + volumeMounts: + # db-init reads the platform-shim addon from this mount + # to install it via `odoo -i odoosky_hoot_dom_shim`. The + # mount is unconditional because the shim is unconditional; + # any tenant addon images write here too if .Values.addons + # is non-empty. + - name: addons + mountPath: {{ .Values.addonsMountPath }} {{- if .Values.addons }} # One initContainer per selected addon. Each addon image's # ENTRYPOINT/CMD copies its bundled content into /target/, @@ -243,9 +325,11 @@ spec: - "-d" - "{{ .Values.instance.code }}" - "--db-filter=^{{ .Values.instance.code }}$" - {{- if .Values.addons }} + # addons-path always includes the shared addons mount — + # the platform-shim addon lives there even when the + # tenant has no extra addons. See platform-shim + # initContainer. - "--addons-path=/usr/lib/python3/dist-packages/odoo/addons,{{ .Values.addonsMountPath }}" - {{- end }} ports: - name: http containerPort: 8069 @@ -278,11 +362,11 @@ spec: volumeMounts: - name: filestore mountPath: /var/lib/odoo - {{- if .Values.addons }} + # addons mount is always present — at minimum it carries + # the platform-shim addon (see platform-shim initContainer). - name: addons mountPath: {{ .Values.addonsMountPath }} readOnly: true - {{- end }} resources: {{- include "instance.resources" (dict "Values" .Values "role" "odoo") | nindent 12 }} # /web/login is the most stable health endpoint across Odoo @@ -314,12 +398,10 @@ spec: - name: filestore persistentVolumeClaim: claimName: {{ include "instance.fullname" . }}-odoo - {{- if .Values.addons }} - # Shared scratch volume that init containers populate with - # addon content. emptyDir is fine — the source of truth is - # the registry's images; if the volume is wiped (pod - # recreate) the init containers re-materialize from the - # cached images. + # Shared scratch volume that init containers populate. Always + # created — at minimum the platform-shim initContainer drops + # `odoosky_hoot_dom_shim` here. emptyDir is fine — source of + # truth is the registry's images / hardcoded shim; if the + # volume is wiped (pod recreate) initContainers re-materialize. - name: addons emptyDir: {} - {{- end }}