Description
The cap-operator-plugin uses {{ .Release.Revision }} as the spec.version for CAPApplicationVersion resources. Helm release revisions are plain integers (1, 2, 3, ...), which are not valid semantic versions.
The CAP Operator requires valid semver (MAJOR.MINOR.PATCH) for version comparison logic (golang.org/x/mod/semver). When given non-semver versions, the operator silently fails to identify newer versions, and automatic tenant upgrades never trigger.
Related issue: sap/cap-operator#XXX
Location
During cds build, the CapOperatorBuildPlugin (lib/build.js) generates Helm templates by calling helper functions in lib/util.js. The invalid version originates in the writeCAPApplicationVersionCommonCRO function:
// lib/util.js - writeCAPApplicationVersionCommonCRO()
' version: "{{ .Release.Revision }}"',
This is called by both getCAPOpCroYaml() and getConfigurableCapOpCroYaml(), meaning all chart modes (default, with-templates, and with-configurable-templates) are affected.
The resource name helper in getHelperTpl() also uses the revision directly:
// lib/util.js - getHelperTpl()
'{{- define "capApplicationVersionName" -}}',
'{{ printf "%s-%d" (include "appName" $) (.Release.Revision) }}',
'{{- end -}}',
Steps to Reproduce
- Create a CAP project with
cds add cap-operator
- Run
cds build — generates Helm chart templates containing version: "{{ .Release.Revision }}"
- Deploy with
helm install → creates CAV with spec.version: "1"
- Deploy again with
helm upgrade → creates CAV with spec.version: "2"
- Both versions reach Ready state
- Expected: Tenant is automatically upgraded to version
"2"
- Actual: Tenant remains on version
"1" — the operator's semver.Compare("v2", "v1") returns 0 (both invalid) so it never identifies "2" as newer
No error or warning is shown. Everything appears healthy (CAV is Ready, CAPApplication is Consistent).
Suggested Fix
Make the version configurable through values.yaml with a valid semver default that falls back to the Helm release revision.
In lib/util.js, update writeCAPApplicationVersionCommonCRO:
// From:
' version: "{{ .Release.Revision }}"',
// To:
' version: "{{ .Values.app.version | default (printf "%d.0.0" .Release.Revision) }}"',
In the generated values.yaml (via values.yaml.hbs), add an optional version field under app:
app:
# version: "1.2.3" # Optional: override with your own semver (e.g., from CI/CD pipeline)
This way:
- By default (no user override): produces
"1.0.0", "2.0.0", "3.0.0" etc. — valid semver derived from the Helm revision
- With user override: users can set
app.version to their own semver (e.g., from a CI/CD pipeline variable, git tag, or package.json version) via values.yaml or --set app.version=1.2.3
The capApplicationVersionName helper in getHelperTpl() should also use this value for consistency:
// From:
'{{ printf "%s-%d" (include "appName" $) (.Release.Revision) }}',
// To:
'{{ printf "%s-%s" (include "appName" $) (.Values.app.version | default (printf "%d.0.0" .Release.Revision) | replace "." "-") }}',
Impact
- All users deploying with the default cap-operator-plugin templates are affected
- Automatic tenant version upgrades silently do not work
- No error or warning is shown — users have no indication anything is wrong until they notice tenants are not being upgraded
- Version cleanup monitoring (if enabled) is also affected by the same semver comparison issue
Description
The cap-operator-plugin uses
{{ .Release.Revision }}as thespec.versionforCAPApplicationVersionresources. Helm release revisions are plain integers (1,2,3, ...), which are not valid semantic versions.The CAP Operator requires valid semver (
MAJOR.MINOR.PATCH) for version comparison logic (golang.org/x/mod/semver). When given non-semver versions, the operator silently fails to identify newer versions, and automatic tenant upgrades never trigger.Related issue: sap/cap-operator#XXX
Location
During
cds build, theCapOperatorBuildPlugin(lib/build.js) generates Helm templates by calling helper functions inlib/util.js. The invalid version originates in thewriteCAPApplicationVersionCommonCROfunction:This is called by both
getCAPOpCroYaml()andgetConfigurableCapOpCroYaml(), meaning all chart modes (default, with-templates, and with-configurable-templates) are affected.The resource name helper in
getHelperTpl()also uses the revision directly:Steps to Reproduce
cds add cap-operatorcds build— generates Helm chart templates containingversion: "{{ .Release.Revision }}"helm install→ creates CAV withspec.version: "1"helm upgrade→ creates CAV withspec.version: "2""2""1"— the operator'ssemver.Compare("v2", "v1")returns0(both invalid) so it never identifies"2"as newerNo error or warning is shown. Everything appears healthy (CAV is Ready, CAPApplication is Consistent).
Suggested Fix
Make the version configurable through
values.yamlwith a valid semver default that falls back to the Helm release revision.In
lib/util.js, updatewriteCAPApplicationVersionCommonCRO:In the generated
values.yaml(viavalues.yaml.hbs), add an optional version field underapp:This way:
"1.0.0","2.0.0","3.0.0"etc. — valid semver derived from the Helm revisionapp.versionto their own semver (e.g., from a CI/CD pipeline variable, git tag, orpackage.jsonversion) viavalues.yamlor--set app.version=1.2.3The
capApplicationVersionNamehelper ingetHelperTpl()should also use this value for consistency:Impact