@sap/cds-dk 9.4.3 → 9.6.0

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.
Files changed (168) hide show
  1. package/CHANGELOG.md +115 -0
  2. package/bin/compile/index.js +1 -1
  3. package/bin/env.js +3 -3
  4. package/bin/export.js +1 -1
  5. package/bin/help.js +0 -1
  6. package/bin/import.js +8 -3
  7. package/bin/repl.js +7 -11
  8. package/bin/up.js +159 -23
  9. package/bin/util/containerize.js +79 -25
  10. package/bin/version.js +4 -4
  11. package/lib/bind/k8s.js +1 -1
  12. package/lib/build/plugins/helm/files/templates/_deployment_helpers.tpl +1 -1
  13. package/lib/build/plugins/helm/index.js +1 -1
  14. package/lib/build/provider/hana/2migration.js +16 -24
  15. package/lib/build/provider/hana/index.js +1 -1
  16. package/lib/build/provider/hana/template/.hdiconfig-all +7 -1
  17. package/lib/build/provider/hana/template/package.json +1 -1
  18. package/lib/build/provider/internalBuildPlugin.js +15 -1
  19. package/lib/build/provider/java/index.js +12 -1
  20. package/lib/build/provider/mtx-sidecar/index.js +2 -1
  21. package/lib/build/provider/nodejs/index.js +2 -1
  22. package/lib/build/util.js +36 -19
  23. package/lib/compile/to_openapi.js +1 -5
  24. package/lib/import/common.js +9 -2
  25. package/lib/import/odata/ParserContext.js +1 -0
  26. package/lib/import/odata/v4parser.js +57 -25
  27. package/lib/import/openapi/importOpenAPI.js +6 -1
  28. package/lib/init/constants.js +1 -1
  29. package/lib/init/index.js +20 -6
  30. package/lib/init/merge.js +16 -0
  31. package/lib/init/projectReader.js +34 -8
  32. package/lib/init/registries/mta.js +2 -2
  33. package/lib/init/template/ams/index.js +3 -3
  34. package/lib/init/template/app-frontend/files/app-package.json.hbs +1 -7
  35. package/lib/init/template/app-frontend/files/ui5.yaml.hbs +21 -1
  36. package/lib/init/template/app-frontend/index.js +2 -6
  37. package/lib/init/template/application-logging/index.js +11 -16
  38. package/lib/init/template/approuter/files/mta.yaml.hbs +0 -2
  39. package/lib/init/template/approuter/index.js +13 -10
  40. package/lib/init/template/attachments/index.js +4 -2
  41. package/lib/init/template/audit-logging/index.js +4 -2
  42. package/lib/init/template/cloud-logging/index.js +6 -5
  43. package/lib/init/template/connectivity/index.js +2 -2
  44. package/lib/init/template/console/files/package.json +5 -0
  45. package/lib/init/template/console/index.js +25 -0
  46. package/lib/init/template/containerize/index.js +4 -6
  47. package/lib/init/template/data/as-json.js +18 -20
  48. package/lib/init/template/destination/index.js +4 -2
  49. package/lib/init/template/dynatrace/files/package.json.hbs +2 -2
  50. package/lib/init/template/dynatrace/index.js +4 -4
  51. package/lib/init/template/enterprise-messaging/files/mta.yaml.hbs +3 -1
  52. package/lib/init/template/enterprise-messaging/index.js +4 -2
  53. package/lib/init/template/enterprise-messaging-shared/index.js +4 -2
  54. package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +16 -1
  55. package/lib/init/template/github-actions/files/workflows/release.yaml.hbs +2 -2
  56. package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +20 -0
  57. package/lib/init/template/github-actions/index.js +7 -10
  58. package/lib/init/template/hana/files/db/package.json +2 -2
  59. package/lib/init/template/hana/files/mta.yaml.hbs +0 -4
  60. package/lib/init/template/hana/index.js +7 -9
  61. package/lib/init/template/helm/index.js +8 -10
  62. package/lib/init/template/helm-unified-runtime/index.js +1 -4
  63. package/lib/init/template/html5-repo/files/app-package.json.hbs +1 -7
  64. package/lib/init/template/html5-repo/files/ui5.yaml.hbs +21 -1
  65. package/lib/init/template/html5-repo/index.js +14 -18
  66. package/lib/init/template/ias/files/values.yaml.hbs +6 -6
  67. package/lib/init/template/ias/index.js +13 -21
  68. package/lib/init/template/java/index.js +14 -0
  69. package/lib/init/template/kafka/index.js +4 -2
  70. package/lib/init/template/{containerize → kyma}/files/containerize.yaml.hbs +17 -2
  71. package/lib/init/template/kyma/index.js +129 -5
  72. package/lib/init/template/malware-scanner/files/mta.yaml.hbs +3 -1
  73. package/lib/init/template/malware-scanner/files/values.yaml +3 -1
  74. package/lib/init/template/malware-scanner/index.js +4 -2
  75. package/lib/init/template/multitenancy/files/mta.yaml.hbs +1 -3
  76. package/lib/init/template/multitenancy/files/values.yaml.hbs +8 -0
  77. package/lib/init/template/multitenancy/index.js +15 -7
  78. package/lib/init/template/nodejs/files/.vscode/extensions.json +0 -3
  79. package/lib/init/template/nodejs/files/package.json.hbs +1 -1
  80. package/lib/init/template/pipeline/files/app-package.json.hbs +1 -7
  81. package/lib/init/template/pipeline/files/ui5.yaml.hbs +21 -1
  82. package/lib/init/template/pipeline/index.js +1 -2
  83. package/lib/init/template/portal/files/mta.yaml.hbs +5 -3
  84. package/lib/init/template/portal/index.js +8 -8
  85. package/lib/init/template/postgres/index.js +2 -2
  86. package/lib/init/template/redis/index.js +2 -3
  87. package/lib/init/template/redis-messaging/index.js +2 -3
  88. package/lib/init/template/sample/files/_i18n/i18n_de.properties +21 -0
  89. package/lib/init/template/sample/files/_i18n/i18n_en.properties +21 -0
  90. package/lib/init/template/sample/files/_i18n/i18n_fr.properties +21 -0
  91. package/lib/init/template/sample/files/_i18n/messages_de.properties +34 -0
  92. package/lib/init/template/sample/files/_i18n/messages_en.properties +34 -0
  93. package/lib/init/template/sample/files/_i18n/messages_fr.properties +34 -0
  94. package/lib/init/template/sample/files/app/_i18n/i18n.properties +13 -25
  95. package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +13 -25
  96. package/lib/init/template/sample/files/app/admin-authors/fiori-service.cds +52 -0
  97. package/lib/init/template/sample/files/app/admin-authors/webapp/Component.js.hbs +3 -0
  98. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n.properties +3 -0
  99. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n_de.properties +3 -0
  100. package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n_en.properties +3 -0
  101. package/lib/init/template/sample/files/app/admin-authors/webapp/manifest.json +130 -0
  102. package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +81 -88
  103. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js.hbs +3 -0
  104. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +3 -3
  105. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +3 -3
  106. package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +3 -3
  107. package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +24 -48
  108. package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +56 -5
  109. package/lib/init/template/sample/files/app/browse/fiori-service.cds +30 -30
  110. package/lib/init/template/sample/files/app/browse/webapp/Component.js.hbs +3 -0
  111. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +3 -4
  112. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +3 -3
  113. package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +3 -4
  114. package/lib/init/template/sample/files/app/browse/webapp/manifest.json +19 -24
  115. package/lib/init/template/sample/files/app/common.cds +127 -158
  116. package/lib/init/template/sample/files/app/fiori-apps.html +50 -0
  117. package/lib/init/template/sample/files/app/genres/fiori-service.cds +33 -0
  118. package/lib/init/template/sample/files/app/genres/tree-view.cds +42 -0
  119. package/lib/init/template/sample/files/app/genres/value-help.cds +6 -0
  120. package/lib/init/template/sample/files/app/genres/webapp/Component.js.hbs +3 -0
  121. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n.properties +3 -0
  122. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_de.properties +3 -0
  123. package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_en.properties +3 -0
  124. package/lib/init/template/sample/files/app/genres/webapp/manifest.json +126 -0
  125. package/lib/init/template/sample/files/app/services.cds +6 -2
  126. package/lib/init/template/sample/files/db/currencies.cds +13 -0
  127. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +5 -5
  128. package/lib/init/template/sample/files/db/data/{sap.capire.bookshop-Books_texts.csv → sap.capire.bookshop-Books.texts.csv} +4 -4
  129. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +42 -15
  130. package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.texts.csv +43 -0
  131. package/lib/init/template/sample/files/db/data/sap.common-Currencies.csv +12 -0
  132. package/lib/init/template/sample/files/db/data/sap.common-Currencies.texts.csv +15 -0
  133. package/lib/init/template/sample/files/db/hana/index.cds +10 -0
  134. package/lib/init/template/sample/files/db/schema.cds +35 -22
  135. package/lib/init/template/sample/files/db/sqlite/index.cds +10 -0
  136. package/lib/init/template/sample/files/package.json.hbs +14 -1
  137. package/lib/init/template/sample/files/srv/access-control.cds +3 -0
  138. package/lib/init/template/sample/files/srv/admin-service.cds +3 -2
  139. package/lib/init/template/sample/files/srv/cat-service.cds +7 -11
  140. package/lib/init/template/sample/index.js +40 -18
  141. package/lib/init/template/sqlite/files/package.json.hbs +13 -0
  142. package/lib/init/template/sqlite/index.js +1 -1
  143. package/lib/init/template/telemetry/index.js +1 -3
  144. package/lib/init/template/typescript/files/package.json +1 -1
  145. package/lib/init/template/workzone-standard/files/mta.yaml.hbs +0 -5
  146. package/lib/init/template/workzone-standard/index.js +0 -1
  147. package/lib/init/template/xsuaa/files/mta.yaml.hbs +4 -0
  148. package/lib/init/template/xsuaa/index.js +7 -6
  149. package/lib/lint/Linter.js +10 -4
  150. package/lib/lint/eslintHelp.json +1 -1
  151. package/lib/mtx/tenant_action.js +12 -4
  152. package/lib/util/checksum.js +41 -0
  153. package/lib/util/fs.js +8 -1
  154. package/lib/util/glob.js +26 -13
  155. package/lib/util/question.js +60 -7
  156. package/npm-shrinkwrap.json +141 -118
  157. package/package.json +2 -2
  158. package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +0 -25
  159. package/lib/init/template/github-actions/files/cf-setup/action.yaml +0 -53
  160. package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +0 -25
  161. package/lib/init/template/pipeline/files/ui5-deploy.yaml.hbs +0 -25
  162. package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +0 -8
  163. package/lib/init/template/sample/files/app/browse/webapp/Component.js +0 -7
  164. package/lib/init/template/sample/files/app/index.html +0 -32
  165. package/lib/init/template/sqlite/files/package.json +0 -5
  166. /package/lib/init/template/{helm → kyma}/files/Chart.yaml.hbs +0 -0
  167. /package/lib/init/template/{helm → kyma}/files/values.schema.json +0 -0
  168. /package/lib/init/template/{helm → kyma}/files/values.yaml.hbs +0 -0
package/CHANGELOG.md CHANGED
@@ -6,17 +6,87 @@ 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.6.0 - 2025-12-16
10
+
11
+ ### Added
12
+
13
+ - `cds add console` adds dependency to the CAP console plugin.
14
+
15
+ ### Changed
16
+
17
+ - `cds import` no longer generates a first parameter typed as `$self` to bound actions, as this parameter is implicit.
18
+ - `cds add` sets `relativePaths: true` for generated UI5 apps to avoid deployment issues if UI5 data sources start with a "/".
19
+ - `cds up` automatically detects the preferred platform.
20
+ - Added deprecation notices for the following commands in favor of using `cds add kyma`:
21
+ - `cds add containerize`
22
+ - `cds add helm`
23
+ - `cds add helm-unified-runtime`
24
+
25
+ ### Fixed
26
+
27
+ - `cds build --ws-pack` now checks folder depth of up to 5.
28
+ - `cds import --config param1=true,param2=0` now writes boolean and number values correctly to `package.json`.
29
+ - `cds add sample` sets the correct UI component in _\_appconfig/fioriSandboxConfig.json_.
30
+ - `cds add xsuaa` correctly adds the authentication binding to the application deployer if executed afterwards.
31
+ - `cds env` prints coloured output by default again.
32
+ - `cds add java` will preserve existing _package.json_ settings.
33
+ - `cds add multitenancy` now correctly adds a `devDependencies` section when missing.
34
+ - `cds add mta` adds fixes an issue where a faulty XSUAA instance was added with `messaging: true` configured.
35
+ - `cds add sqlite --for production` correctly activates SQLite for production, not just development.
36
+ - `cds up -2 k8s` correctly runs `before_all` before the checksum calculation to calculate against the correct artifact.
37
+
38
+ ## Version 9.5.0 - 2025-12-01
39
+
40
+ ### Added
41
+
42
+ - `cds import` now accepts `--into-namespace <string>` to override the namespace entities are imported to, when importing OpenAPI definitions.
43
+
44
+ ### Changed
45
+
46
+ - `cds add portal` adds the correct service key configuration to the _mta.yaml_.
47
+ - `cds add sample` creates sample files aligned with recent versions of bookshop and bookstore in https://github.com/capire.
48
+ - `cds add sample` uses a matching ID in generated `manifest.json` and UI5 components.
49
+ - `cds build` uses `hdblogicalschema` and `hdblogicalschemaconfig` plugins in its default `.hdiconfig`.
50
+ - `cds add cloud-logging` adds required dependencies to _pom.xml_ for Java projects.
51
+ - `cds add kyma` is now the only suggested Kyma facet in `cds add help`.
52
+ - `cds add helm-unified-runtime` option is now deprecated; use `cds add kyma --unified-runtime` instead.
53
+ - `cds up --to k8s` now saves the checksum of buildpack images to determine if a rebuild is necessary.
54
+ - `cds up --to k8s` will interactively ask for registry credentials if they don't exist on the remote cluster.
55
+ - `cds build` automatically adds `contentLocalizedEdmx=true` to the java build task configuration if OData V2 is set.
56
+ - `cds add github-actions` has out-of-the-box support for Java projects.
57
+ - `cds add html5-repo` and `cds add app-frontend` do not create a separate _ui5-deploy.yaml_ but use the default _ui5.yaml_.
58
+ - `cds add` plugins allow merging into non-existing files with `cds.add.merge(<object>).into(<yaml-path>)`.
59
+ - `cds build` tasks that produce `csn.json` now emit the `compile.for.runtime` event, in alignment with other runtime CSN producers like `cds serve`. Plugins can use such events to hook into CSN creation.
60
+ - `cds add xsuaa` sets `credential-types: [ "binding-secret", "x509" ]` for XSUAA instances.
61
+ - `cds add hana` does not create an explicit `.hdiconfig` file any more as `cds build` provides defaults.
62
+ - `cds import` generates the on condition for a composition when the corresponding OData navigation property targets a collection, is contained (`$ContainsTarget` in OData EDM) and has a backlink (`$Partner` in OData EDM).
63
+ - `cds import` now considers OData contained navigation properties as compositions instead of associations.
64
+
65
+ ### Fixed
66
+
67
+ - `cds add ias` will always add a binding to the MTX sidecar or approuter if required.
68
+ - `cds add ias` has improved support for multitenancy scenarios on Kyma.
69
+ - `cds lint` no longer fails with error code 1 and no message when loading the config fails.
70
+ - `cds version` no longer prints an `undefined` value for `@sap/cds-dk`.
71
+ - `cds compile --flavor files` no longer crashes with a `TypeError`.
72
+ - `cds add workzone` does not add superfluous `destinations` configuration to the `srv-api` import.
73
+
9
74
  ## Version 9.4.3 - 2025-10-29
10
75
 
76
+ ### Added
77
+
11
78
  ### Changed
12
79
 
80
+ - `cds add github-actions` will use the `cap-js/cf-setup` action from GitHub marketplace instead of creating a local copy.
13
81
  - `cds add containerize` omits the Unix-based `before_all` scripts in `containerize.yaml` in favor of a cross-platform compatible `cds up --to k8s` integration.
14
82
  - `cds up --to k8s` now uses built-in containerization, eliminating the hard dependency on the `ctz` library.
15
83
  - `cds up --to k8s` will interactively ask for a domain if it can't be determined from the current Kubernetes configuration.
16
84
  - `cds build --for hana` no longer requires `.hdbgrants` and `.hdbrevokes` to be specified as a plugin in the `.hdiconfig` file.
85
+ - `cds import` no longer emits usage info when invoked with the dry option.
17
86
 
18
87
  ### Fixed
19
88
 
89
+ - `cds add data` fixes an issue where randomly generated `Decimal` and `Integer` values could exceed their defined precision or scale.
20
90
  - `cds add pipeline` does not add UI5 template files in some cases.
21
91
  - `cds add ias` does not write to the `mta.yaml` for non-MTA projects.
22
92
  - `cds add ias` automatically adds the `authenticationType` to `xs-app.json` if required.
@@ -40,6 +110,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
40
110
  - `cds add test` creates correct OData URLs with unqualified entity names in service paths.
41
111
  - `cds add http` skips contained composition targets.
42
112
  - `cds add sample` no longer comes with an error when creating a Book through Fiori.
113
+ - `cds build` now places the `i18n` folder in the generated output root directory instead of nested locations.
43
114
 
44
115
  ## Version 9.4.1 - 2025-10-09
45
116
 
@@ -331,6 +402,50 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
331
402
  - `cds add multitenancy` requires the `srv-api` instead of the `mtx-api` for Java projects.
332
403
 
333
404
 
405
+ ## Version 8.9.12 - 2025-12-16
406
+
407
+ ### Fixed
408
+
409
+ - `cds import` generates the on condition for a composition when the corresponding OData navigation property targets a collection, is contained (`$ContainsTarget` in OData EDM) and has a backlink (`$Partner` in OData EDM).
410
+ - `cds import` now considers OData contained navigation properties as compositions instead of associations.
411
+ - Support for `hdb` v2.
412
+
413
+ ## Version 8.9.11 - 2025-11-21
414
+
415
+ ### Fixed
416
+
417
+ - Bump dependencies in shrinkwrap.
418
+
419
+ ## Version 8.9.10 - 2025-09-19
420
+
421
+ ### Fixed
422
+
423
+ - Updated shrinkwrap to include `@sap/cds-compiler` version 5.9.12.
424
+
425
+ ## Version 8.9.9 - 2025-09-16
426
+
427
+ ### Fixed
428
+
429
+ - CVE-2025-58754: vulnerability with `axios` versions <1.12.0.
430
+
431
+ ## Version 8.9.8 - 2025-07-24
432
+
433
+ ### Fixed
434
+
435
+ - CVE-2025-7783: vulnerability with `form-data` versions <4.0.4.
436
+
437
+ ## Version 8.9.7 - 2025-07-17
438
+
439
+ ### Fixed
440
+
441
+ - Bump dependencies in shrinkwrap.
442
+
443
+ ## Version 8.9.6 - 2025-06-05
444
+
445
+ ### Fixed
446
+
447
+ - Bump dependencies in shrinkwrap.
448
+
334
449
  ## Version 8.9.5 - 2025-06-05
335
450
 
336
451
  ### Fixed
@@ -297,7 +297,7 @@ async function compile (models, options={}) {
297
297
  throw `You must specify a model to compile.\nRun 'cds c -?' to learn more.`
298
298
  }
299
299
 
300
- let chain = model.then (m => model=m)
300
+ let chain = model.then?. (m => model=m) ?? Promise.resolve(model) // model could be a sync. value
301
301
 
302
302
  // add processors for compiling
303
303
  if (options.for) for (let each of options.for.split(',')) {
package/bin/env.js CHANGED
@@ -67,12 +67,12 @@ async function env ([cmd, key, value], options={}) {
67
67
  env['_home_cds-dk'] = path.resolve(__dirname, '..') // to help tools find the DK
68
68
 
69
69
  if (!options.for && options.mocked) await cds.service.bindings
70
+ options.colors = cds.utils.colors.enabled === true
70
71
  return fn (env, key, value, options)
71
72
  }
72
73
 
73
74
 
74
75
  const { inspect, format } = require('node:util')
75
- const {colors} = require('../lib/util/term')
76
76
  const commands = {
77
77
 
78
78
  json: (env,k,v,o) => commands.get (env,k,v,{...o,json:true}),
@@ -88,7 +88,7 @@ const commands = {
88
88
  },
89
89
 
90
90
  get (conf, key, val, options) {
91
- let fmt = x => inspect (x, {depth:22, colors})
91
+ let fmt = x => inspect (x, { depth:22, colors: true })
92
92
 
93
93
  let obj = (
94
94
  !key ? conf
@@ -97,7 +97,7 @@ const commands = {
97
97
  : conf.get(key.replace(/^cds\./,''))
98
98
  )
99
99
 
100
- if (!colors) {
100
+ if (options.colors === false) {
101
101
  fmt = x => typeof x !== 'object' ? x : JSON.stringify(x,0,2)
102
102
  }
103
103
  if (options.json) {
package/bin/export.js CHANGED
@@ -111,7 +111,7 @@ class Export extends Minify {
111
111
  async export (minified,o) {
112
112
  if (this.options.to === '-') return super.print (minified)
113
113
  if (this.options.to.endsWith('/*')) this.options.to = this.export_to()
114
- console.log(`\n${this.name}ing APIs to`, this.options.to, '...\n')
114
+ console.log(`\n${this.name}ing APIs to`, cds.utils.local(this.options.to), '...\n')
115
115
  const exports = []; exports.push(...this.export_models (minified))
116
116
  if (o.texts) exports.push (...this.export_texts (minified))
117
117
  if (o.data) exports.push (this.export_data (minified))
package/bin/help.js CHANGED
@@ -12,7 +12,6 @@ module.exports = exports = Object.assign (help, {formatHelp, handleCompletion, h
12
12
 
13
13
  *i* | *init* jump-start cds-based projects
14
14
  *a* | *add* add a feature to an existing project
15
- * * | *gen* generate models/data using a descriptive prompt [beta]
16
15
  *m* | *import* add models from external sources
17
16
  *c* | *compile* compile cds models to different outputs
18
17
  *p* | *parse* parses given cds models
package/bin/import.js CHANGED
@@ -1,7 +1,7 @@
1
1
  module.exports = Object.assign(_import, {
2
2
  _name: 'import',
3
- options: [ '--services', '--for', '--out', '--as', '--name', '--include-namespaces', '--dependencies', '--from', '--config', '--destination', '--group', '--data-product' ],
4
- shortcuts: [ '-s', '-4', '-o', '-as', '-n', '-ns', '-d', undefined, '-c', undefined, undefined, '-', '-f'],
3
+ options: [ '--services', '--for', '--out', '--as', '--name', '--include-namespaces', '--dependencies', '--from', '--config', '--destination', '--group', '--into-namespace', '--data-product' ],
4
+ shortcuts: [ '-s', '-4', '-o', '-as', '-n', '-ns', '-d',undefined, '-c', undefined, undefined, '-2', '-', '-f'],
5
5
  flags: [ '--dry', '--force', '--no-copy', '--no-save', '--keep-namespace', '--data-product', '--edmx', '--rfc', '--odata', '--openapi', '--asyncapi' ],
6
6
  help: `
7
7
  # SYNOPSIS
@@ -56,10 +56,15 @@ module.exports = Object.assign(_import, {
56
56
  Not supported by import types "edmx", "openapi" or "asyncapi".
57
57
 
58
58
  *--data-product*
59
- Tells cds import to import a data product. The source must be a valid
59
+ Tells cds import to import a data product. The source must be a valid
60
60
  data product CSN file, e.g. as found on http://api.sap.com/dataproducts.
61
61
  (Not released with general availability yet)
62
62
 
63
+ *-2 | --into-namespace* <string>
64
+ Specifies a custom namespace to be used for the imported service.
65
+ This will override the namespace that was specified via the the
66
+ @title annotation of the original service.
67
+
63
68
  *Below options are valid for OData only*
64
69
 
65
70
  *--keep-namespace*
package/bin/repl.js CHANGED
@@ -35,7 +35,7 @@ module.exports = Object.assign (repl, {
35
35
  *cds eval* to evaluate and execute JavaScript.
36
36
  `})
37
37
 
38
- async function repl (_, options={}) {
38
+ async function repl ([project], options={}) {
39
39
 
40
40
  const GREEN = '\x1b[32m', RESET = '\x1b[0m'
41
41
  const cds = require('../lib/cds')
@@ -46,9 +46,9 @@ async function repl (_, options={}) {
46
46
  inspect.defaultOptions.depth = 11
47
47
 
48
48
  Object.defineProperty (cds,'repl',{value:true})
49
- await cds.service.bindings
49
+ await cds.service.bindings // REVISIT: this pre-populates many of the cds facade's lazy properties
50
50
  let context={}, repl = { context, displayPrompt(){} }
51
- if (options.run) await _run (options.run)
51
+ if ((options.run ??= project)) await _run (options.run)
52
52
  if (options.use) _export (...options.use.split(','))
53
53
 
54
54
  repl = require('repl').start ({
@@ -94,14 +94,10 @@ async function repl (_, options={}) {
94
94
  process.on = (event, listener) => event !== 'uncaughtException' && event !== 'unhandledRejection' && process.addListener(event, listener)
95
95
 
96
96
  async function _run (project) {
97
- try {
98
- if (require.resolve('@cap-js/cds-test', {paths: [cds.root, cds.home]})) {
99
- await cds.test (project)
100
- }
101
- } catch (e) {
102
- if (e.code !== 'MODULE_NOT_FOUND') throw e
103
- await cds.exec ('--project', project, '--port', '0', '--in-memory?')
104
- }
97
+ await require('../bin/serve') ([project], { project, port: 0,
98
+ 'in-memory?': true,
99
+ 'with-mocks': true
100
+ })
105
101
  _export ('entities', 'services')
106
102
  }
107
103
 
package/bin/up.js CHANGED
@@ -13,7 +13,9 @@ module.exports = Object.assign(up, {
13
13
 
14
14
  *-2* | *--to* cf|k8s
15
15
 
16
- The platform to deploy to. Default is 'cf'.
16
+ The platform to deploy to.
17
+
18
+ Defaults to *k8s* when Helm charts are present and no *mta.yaml* exists, otherwise *cf*.
17
19
 
18
20
  *-n* | *--namespace* <name>
19
21
 
@@ -28,28 +30,78 @@ module.exports = Object.assign(up, {
28
30
  `})
29
31
 
30
32
  const { execSync } = require('node:child_process')
33
+ const { tmpdir } = require('node:os')
34
+ const crypto = require('node:crypto')
31
35
  const cds = require('../lib/cds')
32
- const { highlight } = require('../lib/util/term')
33
- const { exists, fs, path, path: { join } } = cds.utils
36
+ const { highlight, warn, dim, info } = require('../lib/util/term')
37
+ const { exists, fs, path, path: { join }, read, write } = cds.utils
38
+ const { ask4 } = require('../lib/util/question')
34
39
  const { readProject } = require('../lib/init/projectReader')
35
40
  const { mkdirp, copy } = cds.utils
36
41
  const { containerize } = require('../bin/util/containerize')
42
+ const { calcDirChecksum } = require('../lib/util/checksum')
43
+
44
+ const DEBUG = /\b(y|all|cli)\b/.test(process.env.DEBUG) ? console.debug : undefined
37
45
 
38
- const run = cmd => {
39
- console.log(highlight(cmd))
46
+ const run = (cmd, { continueOnFail = false, silent = false } = {}) => {
47
+ const redactedCmd = cmd.replace(/(--docker-password=)(['"]?)[^'"\s]+(['"]?)/, '$1[REDACTED]$3')
48
+ console.log(highlight(redactedCmd))
40
49
  try {
41
50
  return execSync(cmd, {
42
- stdio: 'inherit',
51
+ stdio: silent ? 'pipe' : 'inherit',
43
52
  env: { ...process.env, FORCE_COLOR: cds.utils.colors.enabled }
44
53
  })
45
54
  } catch (error) {
55
+ if (continueOnFail) {
56
+ if (!silent)
57
+ console.error(error.message)
58
+ return
59
+ }
46
60
  throw error.message
47
61
  }
48
62
  }
49
63
 
64
+ /**
65
+ * Get or create checksum file path in temp directory for UI5 apps
66
+ * @param {string} appName - The name of the app
67
+ * @returns {string} - Path to checksum file in temp directory
68
+ */
69
+ function getChecksumFilePath(appName) {
70
+ const projectHash = crypto.createHash('md5').update(cds.root).digest('hex').substring(0, 8)
71
+ return join(tmpdir(), `cds-ui5-checksum-${projectHash}-${appName}.txt`)
72
+ }
73
+
74
+ /**
75
+ * Read stored checksum for an app from temp directory
76
+ * @param {string} appName - The name of the app
77
+ * @returns {string|null} - The stored checksum or null if not found
78
+ */
79
+ function readStoredChecksum(appName) {
80
+ const checksumFile = getChecksumFilePath(appName)
81
+ if (exists(checksumFile)) {
82
+ try {
83
+ return fs.readFileSync(checksumFile, 'utf-8').trim()
84
+ } catch (e) {
85
+ // Ignore read errors and assume no checksum is stored
86
+ }
87
+ }
88
+ return null
89
+ }
90
+
91
+ /**
92
+ * Write checksum for an app to temp directory
93
+ * @param {string} appName - The name of the app
94
+ * @param {string} checksum - The checksum to store
95
+ */
96
+ function writeStoredChecksum(appName, checksum) {
97
+ const checksumFile = getChecksumFilePath(appName)
98
+ fs.writeFileSync(checksumFile, checksum, 'utf-8')
99
+ }
100
+
50
101
  async function up() {
51
102
  const project = readProject()
52
- const { apps, appName, hasApprouter, hasXsuaa, isMonorepoMicroservice } = project
103
+ const { apps, appName, hasApprouter, hasXsuaa, isMonorepoMicroservice, hasKyma, hasMta, k8s } = project
104
+ const toKyma = cds.cli.options.to === 'k8s' || hasKyma && !hasMta
53
105
 
54
106
  const html5DeployerPath = join(cds.root, cds.env.folders.app, 'html5-deployer', 'resources')
55
107
 
@@ -81,19 +133,39 @@ async function up() {
81
133
  }
82
134
  }
83
135
 
84
- if (cds.cli.options.to === 'k8s') {
85
- if (!exists('chart')) run('cds add helm')
86
- if (apps.length > 0) {
87
- await mkdirp(html5DeployerPath)
88
- }
136
+ if (toKyma) {
137
+ if (!exists('chart') || !exists('containerize.yaml')) run('cds add kyma')
89
138
 
90
- for (const { app } of apps) {
91
- const appPath = path.join(cds.root, cds.env.folders.app, app)
92
- run(`npm run build --prefix ${appPath}`)
93
- await copy(path.join(appPath, `dist/${app}.zip`)).to(html5DeployerPath, `${app}.zip`)
94
- }
139
+ if (project.hasHtml5Repo) {
140
+ if (apps.length > 0) {
141
+ await mkdirp(html5DeployerPath)
142
+ }
143
+
144
+ for (const { app, archiveName } of apps) {
145
+ if (!archiveName) continue
146
+
147
+ const appPath = path.join(cds.root, cds.env.folders.app, app)
148
+ const zipPath = path.join(appPath, `dist/${archiveName}`)
149
+
150
+ // Calculate checksum for the app source folder (excluding node_modules and dist)
151
+ const currentChecksum = await calcDirChecksum(
152
+ appPath,
153
+ (dir) => !dir.includes('node_modules') && !dir.includes('dist')
154
+ )
155
+
156
+ const storedChecksum = readStoredChecksum(app)
157
+ const needsBuild = currentChecksum !== storedChecksum || !exists(zipPath)
95
158
 
96
- if (!exists('containerize.yaml')) run('cds add containerize')
159
+ if (needsBuild || process.env.SKIP_CHECK == 'true') {
160
+ DEBUG?.(dim(`build required for '${app}' – checksums`, { old: storedChecksum, new: currentChecksum }))
161
+ run(`npm run build --prefix ${appPath}`)
162
+ await copy(zipPath).to(html5DeployerPath, `${app}.zip`)
163
+ writeStoredChecksum(app, currentChecksum)
164
+ } else {
165
+ console.log(info(`Skipping build for UI5 app '${app}' - no changes detected (checksum: ${currentChecksum.substring(0, 8)}...)`))
166
+ }
167
+ }
168
+ }
97
169
  } else {
98
170
  if (!exists('mta.yaml')) {
99
171
  run('cds add mta')
@@ -112,18 +184,82 @@ async function up() {
112
184
  if (!exists(target)) fs.symlinkSync(relative, target, 'junction')
113
185
  }
114
186
 
115
- if (cds.cli.options.to === 'k8s') {
187
+ if (toKyma) {
116
188
  const ns = cds.cli.options.namespace
117
- const nsHelm = ns ? `--namespace ${ns} --create-namespace` : ''
189
+ const nsHelm = ns ? `--namespace ${ns}` : ''
118
190
  const nsKubectl = ns ? `-n ${ns}` : ''
119
191
 
120
192
  run('npm i --package-lock')
121
193
  run('cds build --production')
122
- await containerize('containerize.yaml', { push: true, clearCache: !!process.env.CLEAR_CACHE })
123
- console.log('Containerization completed')
194
+
195
+ if (ns) {
196
+ const k8sNamespaceResult = run(`kubectl get namespace ${ns} -o jsonpath="{.metadata.name}"`, { continueOnFail: true, silent: true })
197
+ if (!k8sNamespaceResult) {
198
+ run(`kubectl create namespace ${ns}`)
199
+ }
200
+ }
201
+
202
+ const isValidDomain = str => /^[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/.test(str)
203
+ const { image, imagePullSecret } = k8s.values?.global ?? {}
204
+ let registry = image?.registry
205
+ if (!process.env.CI) {
206
+ if (!registry || !isValidDomain(registry)) {
207
+ const valuesPath = 'chart/values.yaml'
208
+ const [yn] = await ask4([warn(`invalid registry server '${registry}' detected, override in ${valuesPath}? (Y/n)`)])
209
+ if (yn?.toLowerCase() !== 'y' || process.env.CI) {
210
+ throw `registry parameter is invalid, please specify it in ${valuesPath}`
211
+ } else {
212
+ registry = await ask4(['registry server (e.g. index.docker.io): '])
213
+ if (!k8s.values.global.image) k8s.values.global.image = {}
214
+ k8s.values.global.image.registry = registry
215
+ const values = (await read(valuesPath)).replace(/(registry:\s*).*/, `$1${registry}`)
216
+ await write(values).to(valuesPath)
217
+
218
+ const containerize = (await read('containerize.yaml')).replace(/(repository:\s*).*/, `$1${registry}`)
219
+ await write(containerize).to('containerize.yaml')
220
+ }
221
+ }
222
+ if (imagePullSecret?.name) {
223
+ const imagePullSecretResult = run(`kubectl get secret ${imagePullSecret.name} ${nsKubectl} -o jsonpath="{.metadata.name}"`, { continueOnFail: true, silent: true })
224
+ if (!imagePullSecretResult) {
225
+ const yn = await ask4([`image pull secret '${imagePullSecret.name}' not found in namespace '${ns || 'default'}' – do you want to create it? (Y/n) `])
226
+ if (yn?.toLowerCase() !== 'y' && yn !== '') {
227
+ throw `image pull secret '${imagePullSecret.name}' is required to pull images from the registry`
228
+ } else {
229
+ const [username, email] = await ask4([
230
+ 'registry username: ',
231
+ 'registry email: '
232
+ ])
233
+ const password = await ask4(['registry password: '], { redacted: true })
234
+ run(`kubectl create secret docker-registry ${imagePullSecret.name} ${nsKubectl} --docker-server=${registry} --docker-username=${username} --docker-password='${password}' --docker-email='${email}'`)
235
+ }
236
+ }
237
+ }
238
+ }
239
+
240
+ const modules = await containerize()
241
+ const helmArgs = []
242
+ modules?.forEach(m => {
243
+ if (m.path === 'gen/srv') {
244
+ helmArgs.push(`--set srv.annotations.deployment.deployment-checksum=${m.checksum}`)
245
+ }
246
+ if (m.path === 'app/router') {
247
+ helmArgs.push(`--set approuter.annotations.deployment.deployment-checksum=${m.checksum}`)
248
+ }
249
+ if (m.path === 'app/html5-deployer') {
250
+ helmArgs.push(`--set html5-apps-deployer.annotations.job.job-checksum=${m.checksum}`)
251
+ }
252
+ if (m.path === 'gen/db') {
253
+ helmArgs.push(`--set hana-deployer.annotations.job.job-checksum=${m.checksum}`)
254
+ }
255
+ if (m.path === 'gen/mtx/sidecar') {
256
+ helmArgs.push(`--set sidecar.annotations.deployment.deployment-checksum=${m.checksum}`)
257
+ }
258
+ })
124
259
 
125
260
  let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --wait --wait-for-jobs --timeout=10m`
126
- if (hasXsuaa) helmUpgrade += ' --set-file xsuaa.jsonParameters=xs-security.json'
261
+ if (hasXsuaa) helmArgs.push(' --set-file xsuaa.jsonParameters=xs-security.json')
262
+ if (helmArgs.length > 0) helmUpgrade += ` ${helmArgs.join(' ')}`
127
263
  run(helmUpgrade)
128
264
  run(`kubectl rollout status deployment ${appName}-srv ${nsKubectl} --timeout=8m`)
129
265
  if (hasApprouter) run(`kubectl rollout status deployment ${appName}-approuter ${nsKubectl} --timeout=8m`)