@sap/cds-dk 9.2.0 → 9.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,6 +6,23 @@ This project adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  The format is based on [Keep a Changelog](https://keepachangelog.com/).
8
8
 
9
+ ## Version 9.2.1 - 2025-08-22
10
+
11
+ ### Changed
12
+
13
+ - `cds add github-actions` adds `if: always()` to the scripts retrieving Cloud Foundry logs.
14
+ - `cds up` uses a default timeout of 10 minutes for Helm upgrades.
15
+ - `cds bind` works out-of-the-box for PostgreSQL databases.
16
+ - `cds bind -a` gives warning if no services are bound to the targeted app.
17
+
18
+ ### Fixed
19
+
20
+ - `cds add github-actions` won't try to merge a `cf-info` action if there's an existing `mta.yaml`.
21
+ - `cds add workzone` with missing `sap.app` config in `manifest.json` does not throw a `TypeError`.
22
+ - `cds compile --to xsuaa` generates roles for `@requires` in bound actions.
23
+ - `cds add lint` now adds proper configuration to enable linting of JavaScript and TypeScript files in VS Code.
24
+ - `cds add mta` does no longer adds services in `mta.yaml` for plugins coming from `devDependencies`.
25
+
9
26
  ## Version 9.2.0 - 2025-07-31
10
27
 
11
28
  ### Added
package/bin/bind.js CHANGED
@@ -159,6 +159,8 @@ async function cds_bind(args, options) {
159
159
  const resolvedServices = [], unresolvedServices = {}
160
160
  const services = (await require('../lib/util/cf').services(app)).filter(s => Object.keys(s.credentials).length > 0)
161
161
 
162
+ if (!services) console.warn(`no services bound to app ${app} - nothing to bind`)
163
+
162
164
  services.forEach(service => {
163
165
  const type = service.label || service.type;
164
166
  const requiredServiceEntry = Object.entries(cds.env.requires).find(([, requiredService]) => requiredService.kind === type && requiredService?.vcap?.name === service.name)
package/bin/up.js CHANGED
@@ -116,7 +116,7 @@ async function up() {
116
116
 
117
117
  run('npm i --package-lock')
118
118
  await runDetectingError('ctz containerize.yaml --log --push', /^\[?ERROR\]?.*/m)
119
- let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --wait --wait-for-jobs`
119
+ let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --wait --wait-for-jobs --timeout=10m`
120
120
  if (hasXsuaa) helmUpgrade += ' --set-file xsuaa.jsonParameters=xs-security.json'
121
121
  run(helmUpgrade)
122
122
  run(`kubectl rollout status deployment ${appName}-srv ${nsKubectl} --timeout=8m`)
@@ -23,7 +23,8 @@ const KindToRequiresNameMap = {
23
23
  'enterprise-messaging-shared': 'messaging',
24
24
  'redis-messaging': 'messaging',
25
25
  'multitenancy': 'multitenancy',
26
- 'portal': 'portal'
26
+ 'portal': 'portal',
27
+ 'postgres': 'db'
27
28
  }
28
29
 
29
30
  const PreferredKinds = {
@@ -77,7 +78,8 @@ async function bind(options = { on: 'cf' }) {
77
78
  resolved.name = options.serviceArg
78
79
  } else if (kind) {
79
80
  const requiresName = KindToRequiresNameMap[kind]
80
- if (!requiresName) throw new Error(`Unknown CDS service name for service ${bold(instance)}. Please specify as argument for ${bold('cds bind')}.`)
81
+
82
+ if (!requiresName) throw `unknown CDS service name for service ${bold(instance)} of kind ${bold(kind)} – please specify as argument for ${bold('cds bind')}`
81
83
  resolved.name = requiresName
82
84
  }
83
85
  resolved.name ??= `custom-service:${instance || secret}`
@@ -60,8 +60,10 @@ module.exports = function compile_to_xsuaa (model, options) {
60
60
  return findUserAttrInExpression(expr)
61
61
  }
62
62
 
63
- cds.reflect(model).foreach(def => {
64
- var reqAnnotations = def['@requires']
63
+ const authAnnotations = def => {
64
+ if (!def) return
65
+
66
+ let reqAnnotations = def['@requires']
65
67
  if(!Array.isArray(reqAnnotations)) reqAnnotations=[reqAnnotations]
66
68
 
67
69
  reqAnnotations.forEach(scope => {
@@ -84,7 +86,14 @@ module.exports = function compile_to_xsuaa (model, options) {
84
86
  lattributes.forEach(attr => attributes[attr] = true)
85
87
  })
86
88
  }
89
+ }
90
+
91
+ cds.reflect(model).foreach(def => {
92
+ authAnnotations(def)
93
+ if (def.actions) Object.values(def.actions).forEach(authAnnotations)
94
+ if (def.functions) Object.values(def.functions).forEach(authAnnotations)
87
95
  })
96
+
88
97
  const roleTemplates = Object.keys(scopes).map((s) => {
89
98
  return {
90
99
  name: s,
@@ -250,7 +250,7 @@ spec:
250
250
  if (/[^\w-_]+/g.exec(cfServiceInstanceName)) {
251
251
  throw `Service name ${cfServiceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`;
252
252
  }
253
- console.log(`Using container ${bold(cfServiceInstanceName)}`);
253
+ console.log(`using container ${bold(cfServiceInstanceName)}`);
254
254
 
255
255
  const cfConfig = cfServiceInstanceMta?.parameters?.config;
256
256
  const serviceInstance = await cfUtil.getOrCreateService('hana', 'hdi-shared', cfServiceInstanceName, cfConfig);
@@ -6,7 +6,7 @@ const HDI_DEPLOY_LIB = '@sap/hdi-deploy';
6
6
  module.exports = new class HdiDeployUtil {
7
7
 
8
8
  async deploy(dbDir, vcapEnv = {}, { autoUndeploy, parameter={} } = {}) {
9
- console.log(`Deploying to HANA from ${dbDir}`);
9
+ console.log(`deploying to HANA from ${dbDir}`);
10
10
 
11
11
  const { clean_env, deploy } = this._loadHdiDeployLib(dbDir);
12
12
 
@@ -47,12 +47,14 @@ module.exports = new class ProjectReader {
47
47
  * Returns cds.env using 'production' profile by default as mta deployment is executed with having production profile set.
48
48
  */
49
49
  env4(profile = 'production') {
50
+ const nodeEnv = process.env.NODE_ENV
50
51
  const cdsEnv = process.env.CDS_ENV
51
- process.env.CDS_ENV = profile
52
+ process.env.CDS_ENV = process.env.NODE_ENV = profile
52
53
  try {
53
54
  return cds.env.for('cds')
54
55
  } finally {
55
56
  cdsEnv ? process.env.CDS_ENV = cdsEnv : delete process.env.CDS_ENV
57
+ nodeEnv ? process.env.NODE_ENV = nodeEnv : delete process.env.NODE_ENV
56
58
  }
57
59
  }
58
60
 
@@ -13,7 +13,7 @@ module.exports = class AttachmentsTemplate extends require('../../plugin') {
13
13
  }
14
14
 
15
15
  static hasInProduction(env) {
16
- return !!env.requires?.attachments
16
+ return !!env.requires?.attachments || readProject().dependencies?.['@cap-js/attachments']
17
17
  }
18
18
 
19
19
  async canRun() {
@@ -12,7 +12,7 @@ module.exports = class AuditLoggingTemplate extends require('../../plugin') {
12
12
  }
13
13
 
14
14
  static hasInProduction(env) {
15
- return env.requires?.['audit-log']
15
+ return env.requires?.['audit-log'] || readProject().dependencies?.['@cap-js/audit-logging']
16
16
  }
17
17
 
18
18
  options() {
@@ -38,11 +38,15 @@ jobs:
38
38
 
39
39
  {{#hasApprouter}}
40
40
  - run: cf logs "${{ env.APP_NAME }}" --recent
41
+ if: always()
41
42
  {{/hasApprouter}}
42
43
  - run: cf logs "${{ env.APP_NAME }}-srv" --recent
44
+ if: always()
43
45
  {{^hasMultitenancy}}
44
46
  - run: cf logs "${{ env.APP_NAME }}-db-deployer" --recent
47
+ if: always()
45
48
  {{/hasMultitenancy}}
46
49
  {{#hasMultitenancy}}
47
50
  - run: cf logs "${{ env.APP_NAME }}-mtx" --recent
51
+ if: always()
48
52
  {{/hasMultitenancy}}
@@ -49,10 +49,6 @@ module.exports = class GitHubActionTemplate extends require('../../plugin') {
49
49
  if (!exists(cfGithub) || force) {
50
50
  await copy(path.join(__dirname, 'files/cf-github-deploy'), cfGithub)
51
51
  }
52
- const cfInfo = '.github/actions/cf-info'
53
- if (!exists(cfInfo) || force) {
54
- await copy(path.join(__dirname, 'files/cf-info'), cfInfo)
55
- }
56
52
  }
57
53
  if (hasHelm) {
58
54
  const kyma = '.github/workflows/kyma.yaml'
@@ -5,8 +5,11 @@ const cds = require('../../../cds')
5
5
  const { merge } = require('../../merge')
6
6
  const { dim, bold } = require('../../../util/term')
7
7
 
8
- const VSCODE_FILE_ASSOCIATIONS = {
9
- csv: ['csv', 'csv (semicolon)', 'tsv', 'tab']
8
+ const ESLINT_LANGS_FOR_FILE_TYPE = {
9
+ cds: ['cds', 'csn'],
10
+ csv: ['csv', 'csv (semicolon)', 'tsv', 'tab'],
11
+ js: ['javascript'],
12
+ ts: ['typescript']
10
13
  }
11
14
 
12
15
  /**
@@ -54,15 +57,7 @@ module.exports = class LintTemplate extends require('../../plugin') {
54
57
  await this.#addEslintPluginConfig()
55
58
 
56
59
  // Generate VS Code extension settings
57
- const ALLOWED_FILE_EXTENSIONS = ['*.cds', '*.csn', '*.csv']
58
- const extensions = new Set(ALLOWED_FILE_EXTENSIONS.map((ext) => path.extname(ext).replace('.', '')))
59
- for (const ext of extensions) {
60
- if (ext in VSCODE_FILE_ASSOCIATIONS) {
61
- for (const assoc of VSCODE_FILE_ASSOCIATIONS[ext]) {
62
- extensions.add(assoc)
63
- }
64
- }
65
- }
60
+ const extensions = new Set(Object.values(ESLINT_LANGS_FOR_FILE_TYPE).flat())
66
61
  await io.sanitizeVscodeSettings(
67
62
  path.join(cds.root, '.vscode/settings.json'),
68
63
  Array.from(extensions),
@@ -1,8 +1,8 @@
1
1
  {
2
2
  // uncomment entries once all libraries have been installed via 'npm install'
3
3
  "eslint.validate": [
4
- // "cds",
5
- // "csn",
4
+ // "javascript",
5
+ // "typescript",
6
6
  // "csv",
7
7
  // "csv (semicolon)",
8
8
  // "tab",
@@ -15,7 +15,7 @@ module.exports = class NotificationsTemplate extends require('../../plugin') {
15
15
  }
16
16
 
17
17
  static hasInProduction(env) {
18
- return !!env.requires?.notifications
18
+ return !!env.requires?.notifications || readProject().dependencies?.['@cap-js/notifications']
19
19
  }
20
20
 
21
21
  async canRun() {
@@ -18,7 +18,7 @@ module.exports = class PostgresTemplate extends require('../../plugin') {
18
18
  }
19
19
 
20
20
  static hasInProduction(env) {
21
- return env.requires?.db?.kind === 'postgres'
21
+ return env.requires?.db?.kind === 'postgres' || readProject().dependencies?.['@cap-js/postgres']
22
22
  }
23
23
 
24
24
  async run() {
@@ -46,7 +46,7 @@ module.exports = class extends require('../../plugin') {
46
46
  project.uiApp = p
47
47
  const manifestPath = join(appPath, p, 'webapp/manifest.json')
48
48
  const manifest = exists(manifestPath) ? await read(manifestPath) : {}
49
- if (!manifest['sap.app'].crossNavigation.inbounds) {
49
+ if (!manifest['sap.app']?.crossNavigation?.inbounds) {
50
50
  return merge(__dirname, 'files/manifest.json.hbs').into(join(appPath, p, 'webapp/manifest.json'), { project })
51
51
  } else {
52
52
  return merge({ 'sap.cloud': {
package/lib/util/cf.js CHANGED
@@ -223,7 +223,7 @@ module.exports = new class CfUtil {
223
223
  async env(app) {
224
224
  const { spaceGuid: space_guids, orgGuid: organization_guids } = await this.target();
225
225
  const apps = await this.curl('/v3/apps', { space_guids, organization_guids, names: app });
226
- if (apps.resources.length < 1) throw `Cannot find app with name '${app}'`
226
+ if (apps.resources.length < 1) throw `cannot find app with name '${app}'`
227
227
  const guid = apps.resources[0].guid;
228
228
  return await this.curl('/v3/apps/' + guid + '/env', { space_guids, organization_guids, names: app });
229
229
  }
@@ -257,15 +257,15 @@ module.exports = new class CfUtil {
257
257
  if (!LOG._debug && serviceInstance?.last_operation?.description) {
258
258
  throw serviceInstance.last_operation.description;
259
259
  }
260
- throw `The returned service reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(serviceInstance, null, 4)}`;
260
+ throw `the returned service reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(serviceInstance, null, 4)}`;
261
261
 
262
262
  default:
263
- console.error(`Unsupported server response state '${serviceInstance?.last_operation?.state}'. Waiting for next response.`);
263
+ console.error(`unsupported server response state '${serviceInstance?.last_operation?.state}' – waiting for next response`);
264
264
  break;
265
265
  }
266
266
  }
267
267
 
268
- throw `Timeout occurred while getting service ${bold(serviceName)}`;
268
+ throw `timeout occurred while getting service ${bold(serviceName)}`;
269
269
  }
270
270
 
271
271
  async getOrCreateService(serviceOfferingName, planName, serviceName, options) {
@@ -275,7 +275,7 @@ module.exports = new class CfUtil {
275
275
  return probeService;
276
276
  }
277
277
 
278
- console.log(`Creating service ${bold(serviceName)} - please be patient...`);
278
+ console.log(`creating service ${bold(serviceName)} - please be patient...`);
279
279
 
280
280
  const target = await this.target();
281
281
 
@@ -287,7 +287,7 @@ module.exports = new class CfUtil {
287
287
  });
288
288
 
289
289
  if (!servicePlan?.resources?.length) {
290
- throw `No service plans found for ${planName} in service offering ${serviceOfferingName}.`;
290
+ throw `no service plans found for ${planName} in service offering ${serviceOfferingName}`;
291
291
  }
292
292
 
293
293
  const body = {
@@ -353,7 +353,7 @@ module.exports = new class CfUtil {
353
353
  throw `The returned binding reported state '${OPERATION_STATE_FAILED}'.\n${JSON.stringify(binding, null, 4)}`;
354
354
 
355
355
  default:
356
- console.error(`Unsupported server response state '${binding?.last_operation?.state}'. Waiting for next response.`);
356
+ console.error(`unsupported server response state '${binding?.last_operation?.state}'. Waiting for next response.`);
357
357
  break;
358
358
  }
359
359
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "9.2.0",
3
+ "version": "9.2.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@sap/cds-dk",
9
- "version": "9.2.0",
9
+ "version": "9.2.1",
10
10
  "license": "SEE LICENSE IN LICENSE",
11
11
  "dependencies": {
12
12
  "@cap-js/asyncapi": "^1.0.0",
@@ -78,8 +78,8 @@
78
78
  }
79
79
  },
80
80
  "node_modules/@eslint/js": {
81
- "version": "9.32.0",
82
- "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==",
81
+ "version": "9.33.0",
82
+ "integrity": "sha512-5K1/mKhWaMfreBGJTwval43JJmkip0RmM+3+IuqupeSKNC/Th2Kc7ucaq5ovTSra/OOKB9c58CGSz3QMVbWt0A==",
83
83
  "license": "MIT",
84
84
  "peer": true,
85
85
  "engines": {
@@ -90,8 +90,8 @@
90
90
  }
91
91
  },
92
92
  "node_modules/@sap/cds": {
93
- "version": "9.2.0",
94
- "integrity": "sha512-u/R+Bzz8RsiL4JINwwmbPFmSD8Y10PGpXetGHYvUyefUG/V/S8OBAWy/elu4FmTr1X8J4+kuynCgoETaWRveKw==",
93
+ "version": "9.2.1",
94
+ "integrity": "sha512-0zjjZGlBvcJMyVj48hTs6Q22/w9hNnJR6O88rGoEj4R87uaP4uM5gFLf3PDnPDUwkAeveVHgJRnWPqDGqIMgvA==",
95
95
  "license": "SEE LICENSE IN LICENSE",
96
96
  "dependencies": {
97
97
  "@sap/cds-compiler": "^6",
@@ -789,8 +789,8 @@
789
789
  "license": "MIT"
790
790
  },
791
791
  "node_modules/follow-redirects": {
792
- "version": "1.15.10",
793
- "integrity": "sha512-V7O/fFKM539IC2bweloFWuoiJ9OtI3W2uIqJPWM8IT5xxNyt73QtvVqmSpcDmk07ivmmlKB+rRY0vpQjIYNtKw==",
792
+ "version": "1.15.11",
793
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
794
794
  "funding": [
795
795
  {
796
796
  "type": "individual",
@@ -1833,8 +1833,8 @@
1833
1833
  }
1834
1834
  },
1835
1835
  "node_modules/yaml": {
1836
- "version": "2.8.0",
1837
- "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==",
1836
+ "version": "2.8.1",
1837
+ "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==",
1838
1838
  "license": "ISC",
1839
1839
  "bin": {
1840
1840
  "yaml": "bin.mjs"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap/cds-dk",
3
- "version": "9.2.0",
3
+ "version": "9.2.1",
4
4
  "description": "Command line client and development toolkit for the SAP Cloud Application Programming Model",
5
5
  "homepage": "https://cap.cloud.sap/",
6
6
  "keywords": [