Skip to content
Merged
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@

All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). The format is based on [Keep a Changelog](http://keepachangelog.com/).

## Version 0.15.0 - 21-April-2026
## Version 0.15.0 - 29-April-2026

### Changed

- Deprecated `globalAccountId` in favor of `providerSubaccountId` in the `values.yaml` file. This change is part of the latest CAP Operator version `v0.28.0`. **Use this version with CAP Operator `v0.28.0` or later to avoid any compatibility issues.**
- Defaulting `ttlSecondsAfterFinished` to 300 seconds for job workloads.
- Added `sme.sap.com/enable-cleanup-monitoring: "true"` as a default annotation on the CAPApplication resource.
- `serviceExposures` restriction removed in from CAPApplicationVersion template to allow users to configure it as per their needs.
- Updated `values.schema.json` to align with the latest CAP Operator version `v0.28.0` and BTP Service Operator version `v0.10.5`.
- Replaced `readline` based prompt with [`enquirer`](https://github.com/enquirer/enquirer) for improved interactive prompt experience during runtime values generation. Required fields are now marked with `*` and validated inline.

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@

The CAP Operator Plugin offers a simple method for generating [CAP Operator](https://sap.github.io/cap-operator/) resources, which are essential for deploying multi-tenant CAP Applications.

> [!WARNING]
> ## Action Required:
> The `globalAccountId` field in the `CAPApplication` spec is [deprecated](https://github.com/SAP/cap-operator/discussions/343) since CAP Operator v0.28.0 and will be removed in a future release.
>
> **To migrate**, upgrade to CAP Operator Plugin v0.15.0 or later and run `cds add cap-operator`. This updates `values.yaml`, `values.schema.json`, and `templates/cap-operator-cros.yaml` to use `providerSubaccountId` instead.
>
> If you have already migrated, you can ignore this message.

## Before You Start

The CAP Operator plugin requires `@sap/cds-dk: ">=8.2.1"`. If you've installed @sap/cds-dk globally, ensure that the installed version is `8.2.1` or higher.
Expand Down
38 changes: 36 additions & 2 deletions lib/add.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
// If chart folder exists, read the chart.yaml to determine if it's a service only chart
const project = this.readProject(isServiceOnly || isServiceOnlyChart('chart'))

if (exists('chart')) {
if (exists('chart') && require('fs').readdirSync(join(cds.root, 'chart')).length > 0) {

Check warning on line 92 in lib/add.js

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `node:fs` over `fs`.

See more on https://sonarcloud.io/project/issues?id=cap-js_cap-operator-plugin&issues=AZ3T_Jv2JlDI95VijJWj&open=AZ3T_Jv2JlDI95VijJWj&pullRequest=98
await this.handleExistingChart(project)
return
}
Expand Down Expand Up @@ -189,6 +189,38 @@
throw new Error(`Option '--with-service-only' cannot be used with '--with-mta' or '--with-mta-extensions'`)
}

async adoptIncompatibleChanges(isConfigurable) {
const valuesPath = join(cds.root, 'chart/values.yaml')
const valuesYaml = yaml.parse(await read(valuesPath))

if (valuesYaml.btp?.globalAccountId === undefined) return false
Comment thread
anirudhprasad-sap marked this conversation as resolved.

console.log("⚠️ 'globalAccountId' is deprecated — replacing with 'providerSubaccountId' in chart files")

delete valuesYaml.btp.globalAccountId
valuesYaml.btp.providerSubaccountId = null
await write(yaml.stringify(valuesYaml)).to(valuesPath)
console.log("⚠️ Updated values.yaml")

const croPath = join(cds.root, 'chart/templates/cap-operator-cros.yaml')
if (exists(croPath)) {
const croContent = await read(croPath)
await write(croContent.replace(
'globalAccountId: {{ .Values.btp.globalAccountId }}',
'providerSubaccountId: {{ .Values.btp.providerSubaccountId }}'
)).to(croPath)
console.log("⚠️ Updated chart/templates/cap-operator-cros.yaml")
}

const valuesSchemaPath = isConfigurable
? '../files/configurableTemplatesChart/values.schema.json'
: '../files/chart/values.schema.json'
await copy(join(__dirname, valuesSchemaPath)).to('chart/values.schema.json')
Comment thread
anirudhprasad-sap marked this conversation as resolved.
console.log("⚠️ Updated values.schema.json")

return true
}

async handleExistingChart(project) {
const isConfigurable = isConfigurableTemplateChart('chart')
let loggingDone = false
Expand All @@ -202,6 +234,8 @@
loggingDone = true
}

loggingDone = loggingDone || await this.adoptIncompatibleChanges(isConfigurable)

if (!isConfigurable && cds.cli.options['with-configurable-templates'])
console.log("CAP Operator chart already present. If you want to convert the existing chart to a configurable template chart, run 'npx cap-op-plugin convert-to-configurable-template-chart'")

Expand Down Expand Up @@ -271,7 +305,7 @@
.filter(binding => {
const serviceInstance = serviceInstances.values().find(instance => instance.name === binding.serviceInstanceName)
return !(serviceInstance?.serviceOfferingName === 'html5-apps-repo' &&
serviceInstance?.servicePlanName === 'app-host')
serviceInstance?.servicePlanName === 'app-host')
})
.map(binding => binding.name)

Expand Down
56 changes: 56 additions & 0 deletions test/add.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,60 @@ describe('cds add cap-operator', () => {
expect(getFileHash(join(__dirname, '../files/commonTemplates/service-binding.yaml'))).to.equal(getFileHash(join(bookshop, 'chart/templates/service-binding.yaml')))
expect(getFileHash(join(__dirname, '../files/commonTemplates/service-instance.yaml'))).to.equal(getFileHash(join(bookshop, 'chart/templates/service-instance.yaml')))
})

it('Migrates globalAccountId to providerSubaccountId in simple chart', async () => {
execSync(`cds add cap-operator`, { cwd: bookshop })

const valuesPath = join(bookshop, 'chart/values.yaml')
fs.writeFileSync(valuesPath, fs.readFileSync(valuesPath, 'utf8').replace(' providerSubaccountId:', ' globalAccountId:'))

const log = execSync(`cds add cap-operator`, { cwd: bookshop }).toString()

expect(log).to.include("'globalAccountId' is deprecated")
expect(log).to.include('Updated values.yaml')
expect(log).to.include('Updated values.schema.json')

const updatedValues = fs.readFileSync(valuesPath, 'utf8')
expect(updatedValues).to.include(' providerSubaccountId:')
expect(updatedValues).to.not.include('globalAccountId:')
})

it('Migrates globalAccountId to providerSubaccountId in configurable template chart', async () => {
fs.writeFileSync(join(bookshop, 'xs-security.json'), orignalXsSecurityJson)
execSync(`cds add cap-operator --with-configurable-templates`, { cwd: bookshop })

const valuesPath = join(bookshop, 'chart/values.yaml')
fs.writeFileSync(valuesPath, fs.readFileSync(valuesPath, 'utf8').replace(' providerSubaccountId:', ' globalAccountId:'))

const croPath = join(bookshop, 'chart/templates/cap-operator-cros.yaml')
fs.writeFileSync(croPath, fs.readFileSync(croPath, 'utf8').replace(
'providerSubaccountId: {{ .Values.btp.providerSubaccountId }}',
'globalAccountId: {{ .Values.btp.globalAccountId }}'
))

const log = execSync(`cds add cap-operator`, { cwd: bookshop }).toString()

expect(log).to.include("'globalAccountId' is deprecated")
expect(log).to.include('Updated values.yaml')
expect(log).to.include('Updated chart/templates/cap-operator-cros.yaml')
expect(log).to.include('Updated values.schema.json')

const updatedValues = fs.readFileSync(valuesPath, 'utf8')
expect(updatedValues).to.include(' providerSubaccountId:')
expect(updatedValues).to.not.include('globalAccountId:')

const updatedCro = fs.readFileSync(croPath, 'utf8')
expect(updatedCro).to.include('providerSubaccountId: {{ .Values.btp.providerSubaccountId }}')
expect(updatedCro).to.not.include('globalAccountId: {{ .Values.btp.globalAccountId }}')
})

it('Skips globalAccountId migration when chart is already up to date', async () => {
execSync(`cds add cap-operator`, { cwd: bookshop })

const log = execSync(`cds add cap-operator`, { cwd: bookshop }).toString()

expect(log).to.not.include("'globalAccountId' is deprecated")
expect(log).to.not.include('Updated values.yaml')
expect(log).to.not.include('Updated values.schema.json')
})
})
Loading