diff --git a/CHANGELOG.md b/CHANGELOG.md index 9275fcb..dcce4ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/README.md b/README.md index da4dafe..fe3a3b5 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/lib/add.js b/lib/add.js index 0f09902..a00f704 100644 --- a/lib/add.js +++ b/lib/add.js @@ -89,7 +89,7 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin { // 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) { await this.handleExistingChart(project) return } @@ -189,6 +189,38 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin { 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 + + 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') + console.log("⚠️ Updated values.schema.json") + + return true + } + async handleExistingChart(project) { const isConfigurable = isConfigurableTemplateChart('chart') let loggingDone = false @@ -202,6 +234,8 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin { 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'") @@ -271,7 +305,7 @@ module.exports = class CapOperatorAddPlugin extends cds.add.Plugin { .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) diff --git a/test/add.test.js b/test/add.test.js index fb155ba..8f8fa51 100644 --- a/test/add.test.js +++ b/test/add.test.js @@ -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') + }) })