From 4f489b085463a2c17722150718f627461b788c20 Mon Sep 17 00:00:00 2001 From: jklein Date: Sun, 23 Aug 2026 19:31:05 +0200 Subject: [PATCH] paperless-ngx: an empty barcodes block falls back to the chart defaults A "barcodes:" key left without children (e.g. after deleting the last remaining sub-key, PAPERLESS_CONSUMER_BARCODE_SCANNER, which paperless-ngx 3.0 removed) is YAML null and replaced the whole default sub-tree on merge. Rendering then failed on the nil pointer, so the release simply stopped updating. An empty or null block now means "the chart defaults apply". The merge runs through a new paperless-ngx.defaultedDict helper: nulls never delete, nested maps merge recursively, and explicitly set values win - including false and 0, which "default" and "mergeOverwrite" would swallow. A scalar where the defaults define a map is rejected with the values path in the message. Major bump: the meaning of a values override changes. Fixes #61 --- ci/paperless-ngx/test-values.yaml | 7 ++ paperless-ngx/Chart.yaml | 2 +- paperless-ngx/templates/_helpers.tpl | 75 +++++++++++++++++++ .../templates/paperless-server.sfs.yaml | 13 +++- paperless-ngx/values.yaml | 7 ++ 5 files changed, 99 insertions(+), 5 deletions(-) diff --git a/ci/paperless-ngx/test-values.yaml b/ci/paperless-ngx/test-values.yaml index 3769628..383ab65 100644 --- a/ci/paperless-ngx/test-values.yaml +++ b/ci/paperless-ngx/test-values.yaml @@ -9,5 +9,12 @@ ingress: # apply - there is no cert-manager in the CI cluster. clusterIssuer: ci +paperless: + files: + # Regression guard for issue #61: a childless "barcodes:" key is YAML null. + # It must fall back to the chart defaults (barcodes enabled, "PATCHT") and + # neither erase them nor break rendering. + barcodes: + # smbConnection and oidc stay disabled (defaults): the SMB CSI driver and a # real OIDC login flow are out of scope for the CI install-test. diff --git a/paperless-ngx/Chart.yaml b/paperless-ngx/Chart.yaml index d87db47..9386978 100644 --- a/paperless-ngx/Chart.yaml +++ b/paperless-ngx/Chart.yaml @@ -5,7 +5,7 @@ icon: https://raw.githubusercontent.com/paperless-ngx/paperless-ngx/dev/src-ui/s type: application -version: 10.0.0+up3.0.5 +version: 11.0.0+up3.0.5 appVersion: 3.0.5 diff --git a/paperless-ngx/templates/_helpers.tpl b/paperless-ngx/templates/_helpers.tpl index b4029b9..48aecea 100644 --- a/paperless-ngx/templates/_helpers.tpl +++ b/paperless-ngx/templates/_helpers.tpl @@ -114,3 +114,78 @@ template-chart is the reference implementation. 1 {{- end -}} {{- end -}} + +{{/* +Merge a user-supplied values sub-tree onto a dict of chart defaults so that a +null / empty override falls back to the defaults instead of erasing them. + +Input: dict "defaults" "given" "path" . Output: YAML of the merged dict (consume +it with fromYaml). + +Why this exists (issue #61): Helm merges values maps recursively, but an +override key written without children — e.g. a leftover + + barcodes: + +after deleting its last remaining sub-key — is YAML null and replaces the +whole default sub-tree. The chart then renders with no barcode settings at +all. This helper defines the opposite semantics: an empty block means "chart +defaults apply". + +Rules: + - a nil "given" is treated as an empty dict (all defaults apply). + - a key whose override value is null keeps the default (nulls never delete). + - a key present in both as a map is merged recursively, so a partially + filled block only overrides the keys it actually sets. + - a scalar override for a key the defaults define as a map is rejected with + a message naming the values path, instead of failing later with a template + field error. + - any other set value wins as given, including an explicit false or 0 — + Helm's "default" and sprig's "mergeOverwrite" would both swallow those + zero values, hence the explicit kindIs "invalid" nil check. + - keys only present in the override are passed through unchanged. +*/}} +{{- define "paperless-ngx.defaultedDict" -}} +{{- $defaults := .defaults | default dict -}} +{{- $given := .given -}} +{{- $path := .path | default "value" -}} +{{- if kindIs "invalid" $given -}} +{{- $given = dict -}} +{{- end -}} +{{- if not (kindIs "map" $given) -}} +{{- fail (printf "%s must be a mapping or null, got %s (%v)" $path (kindOf $given) $given) -}} +{{- end -}} +{{- $out := deepCopy $defaults -}} +{{- range $key, $value := $given -}} +{{- if not (kindIs "invalid" $value) -}} +{{- $default := index $defaults $key -}} +{{- if and (kindIs "map" $value) (kindIs "map" $default) -}} +{{- $_ := set $out $key (fromYaml (include "paperless-ngx.defaultedDict" (dict "defaults" $default "given" $value "path" (printf "%s.%s" $path $key)))) -}} +{{- else if kindIs "map" $default -}} +{{- fail (printf "%s.%s must be a mapping or null, got %s (%v)" $path $key (kindOf $value) $value) -}} +{{- else -}} +{{- $_ := set $out $key $value -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- toYaml $out -}} +{{- end -}} + +{{/* +The effective barcode settings, rendered as YAML (consume with fromYaml). + +Input: .Values.paperless.files.barcodes (may be nil, empty or partial). + +The defaults below MUST stay in sync with the barcodes block in values.yaml, +which stays the documented, user-facing place for them; this copy is only the +fallback for the case where an override has erased the block. +*/}} +{{- define "paperless-ngx.barcodes" -}} +{{- $defaults := dict + "enabled" true + "barcodeString" "PATCHT" + "retainBarcodePage" false + "asn" (dict "enabled" true) -}} +{{- include "paperless-ngx.defaultedDict" (dict "defaults" $defaults "given" . "path" "paperless.files.barcodes") -}} +{{- end -}} diff --git a/paperless-ngx/templates/paperless-server.sfs.yaml b/paperless-ngx/templates/paperless-server.sfs.yaml index 06a6d9f..d100978 100644 --- a/paperless-ngx/templates/paperless-server.sfs.yaml +++ b/paperless-ngx/templates/paperless-server.sfs.yaml @@ -1,3 +1,8 @@ +{{- /* +Barcode settings, defended against a null / empty "barcodes:" override: +an empty block falls back to the chart defaults (issue #61). +*/ -}} +{{- $barcodes := fromYaml (include "paperless-ngx.barcodes" .Values.paperless.files.barcodes) -}} apiVersion: apps/v1 kind: StatefulSet metadata: @@ -137,13 +142,13 @@ spec: - name: PAPERLESS_URL value: "https://{{ .Values.ingress.domain }}" - name: PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE - value: "{{ .Values.paperless.files.barcodes.asn.enabled }}" + value: "{{ $barcodes.asn.enabled }}" - name: PAPERLESS_CONSUMER_ENABLE_BARCODES - value: "{{ .Values.paperless.files.barcodes.enabled }}" + value: "{{ $barcodes.enabled }}" - name: PAPERLESS_CONSUMER_BARCODE_STRING - value: "{{ .Values.paperless.files.barcodes.barcodeString }}" + value: "{{ $barcodes.barcodeString }}" - name: PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES - value: "{{ .Values.paperless.files.barcodes.retainBarcodePage }}" + value: "{{ $barcodes.retainBarcodePage }}" - name: PAPERLESS_CONSUMER_DELETE_DUPLICATES value: "{{ .Values.paperless.files.deleteDuplicates }}" {{- if .Values.paperless.files.tikaGotenberg.enabled }} diff --git a/paperless-ngx/values.yaml b/paperless-ngx/values.yaml index 08ae837..dcc0851 100644 --- a/paperless-ngx/values.yaml +++ b/paperless-ngx/values.yaml @@ -48,6 +48,13 @@ paperless: # The barcode scanner backend is no longer configurable: paperless-ngx 3.0 # removed pyzbar and PAPERLESS_CONSUMER_BARCODE_SCANNER; zxing-cpp is the # only backend now. + # + # An empty or null "barcodes:" block in an override means "the defaults + # below apply" — the chart merges the block onto these defaults instead of + # letting a childless key erase them (issue #61; the fallback copy lives in + # the paperless-ngx.barcodes helper and is kept in sync with this block). + # To switch barcode handling off, set barcodes.enabled: false explicitly; + # deleting the sub-keys does not disable it. barcodes: enabled: true barcodeString: "PATCHT"