@sap/cds-dk 9.2.1 → 9.3.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.
- package/CHANGELOG.md +19 -0
- package/bin/bind.js +5 -8
- package/bin/up.js +12 -8
- package/lib/bind/cf.js +4 -0
- package/lib/bind/k8s.js +21 -0
- package/lib/bind/plugin.js +9 -0
- package/lib/build/buildTaskProviderFactory.js +8 -14
- package/lib/build/constants.js +1 -0
- package/lib/build/index.js +17 -2
- package/lib/build/plugin.js +5 -7
- package/lib/build/provider/hana/index.js +4 -4
- package/lib/env/schemas/cds-rc.js +2 -2
- package/lib/init/constants.js +3 -2
- package/lib/init/projectReader.js +2 -4
- package/lib/init/template/ams/index.js +1 -2
- package/lib/init/template/approuter/files/containerize.yaml.hbs +1 -1
- package/lib/init/template/approuter/index.js +1 -1
- package/lib/init/template/extension/files/.base/index.cds +6 -6
- package/lib/init/template/extension/index.js +5 -5
- package/lib/init/template/github-actions/files/{cf-github-deploy → github-deployment}/action.yaml +7 -5
- package/lib/init/template/github-actions/files/kyma-info/action.yaml +1 -1
- package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +10 -7
- package/lib/init/template/github-actions/files/workflows/kyma.yaml.hbs +6 -3
- package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +5 -2
- package/lib/init/template/github-actions/index.js +8 -9
- package/lib/init/template/hana/index.js +2 -1
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +5 -1
- package/lib/init/template/html5-repo/files/package.json.hbs +1 -1
- package/lib/init/template/html5-repo/index.js +24 -19
- package/lib/init/template/ias/files/mta.yaml.hbs +4 -3
- package/lib/init/template/ias/files/values.yaml.hbs +41 -40
- package/lib/init/template/mta/files/package.json +1 -1
- package/lib/init/template/nodejs/files/.vscode/extensions.json +0 -1
- package/lib/init/template/nodejs/files/package.json.hbs +3 -9
- package/lib/init/template/portal/files/mta.yaml.hbs +5 -1
- package/lib/init/template/portal/index.js +0 -1
- package/lib/init/template/sample/files/app/_i18n/i18n.properties +15 -0
- package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +15 -0
- package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +14 -15
- package/lib/init/template/sample/files/app/browse/fiori-service.cds +44 -44
- package/lib/init/template/sample/files/app/common.cds +99 -84
- package/lib/init/template/sample/files/db/schema.cds +22 -17
- package/lib/init/template/sample/files/package.json.hbs +1 -3
- package/lib/init/template/sample/files/srv/admin-service.cds +4 -3
- package/lib/init/template/sample/files/srv/cat-service.cds +28 -11
- package/lib/init/template/tiny-sample/files/nodejs/db/schema.cds +3 -3
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/ui5/index.js +22 -0
- package/lib/lint/eslintHelp.json +5 -3
- package/npm-shrinkwrap.json +13 -13
- package/package.json +1 -1
- package/lib/init/template/sample/files/javascript/admin-service.js.hbs +0 -24
- package/lib/init/template/sample/files/javascript/cat-service.js.hbs +0 -43
- package/lib/init/template/sample/files/typescript/admin-service.ts.hbs +0 -16
- package/lib/init/template/sample/files/typescript/cat-service.ts.hbs +0 -38
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,25 @@ 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.3.0 - 2025-09-01
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `cds bind -a` is now also supported for Kyma, where the app prefix can be passed for `-a`, e.g. `bookshop-srv`.
|
|
14
|
+
- `cds build --for hana` now trims leading and trailing whitespaces in csv-Files if build option `trimCsvWhitespaces` is set.
|
|
15
|
+
- `cds add github-actions` sets required `permissions`.
|
|
16
|
+
- `cds up` supports a deployment layout where approuter or portal service configuration is in a top-level `.deploy` folder.
|
|
17
|
+
- `cds add ias` sets the `access-token-format` to `jwt` by default.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- `cds add sample` provides more i18n translations.
|
|
22
|
+
- `cds add ias` sets the `xsuaa-cross-consumption` field to `true` by default.
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- `cds add ias` adds a subdomain-less application URL in `redirect_uris`.
|
|
27
|
+
|
|
9
28
|
## Version 9.2.1 - 2025-08-22
|
|
10
29
|
|
|
11
30
|
### Changed
|
package/bin/bind.js
CHANGED
|
@@ -46,7 +46,7 @@ module.exports = Object.assign(cds_bind, {
|
|
|
46
46
|
|
|
47
47
|
*-a | --to-app-services* <app>
|
|
48
48
|
|
|
49
|
-
Bind services of a given application
|
|
49
|
+
Bind services of a given application.
|
|
50
50
|
|
|
51
51
|
*--no-create-service-key*
|
|
52
52
|
|
|
@@ -83,7 +83,7 @@ const { bind, storeBindings } = require('../lib/bind/shared');
|
|
|
83
83
|
|
|
84
84
|
|
|
85
85
|
async function cds_bind(args, options) {
|
|
86
|
-
const { on, 'to-app-services': app, to, out, 'output-file': outputFile, exec, credentials } = options
|
|
86
|
+
const { on = 'cf', 'to-app-services': app, to, out, 'output-file': outputFile, exec, credentials } = options
|
|
87
87
|
|
|
88
88
|
if (exec) {
|
|
89
89
|
process.env.CDS_ENV ??= 'hybrid'
|
|
@@ -103,10 +103,6 @@ async function cds_bind(args, options) {
|
|
|
103
103
|
options.out ??= outputFile
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
if (on === 'k8s' && app) {
|
|
107
|
-
throw `Option ${term.bold('--to-app-services')} is only supported for Cloud Foundry.`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
106
|
if (credentials === true) {
|
|
111
107
|
throw `Option ${term.bold('--credentials')} requires a JSON object or a file path.`;
|
|
112
108
|
}
|
|
@@ -155,9 +151,10 @@ async function cds_bind(args, options) {
|
|
|
155
151
|
options.for ??= 'hybrid';
|
|
156
152
|
|
|
157
153
|
if (app) {
|
|
158
|
-
const cds = require('../lib/cds')
|
|
154
|
+
const cds = require('../lib/cds')
|
|
159
155
|
const resolvedServices = [], unresolvedServices = {}
|
|
160
|
-
const
|
|
156
|
+
const plugin = require(`../lib/bind/${on}`)
|
|
157
|
+
const services = await plugin.services4(app)
|
|
161
158
|
|
|
162
159
|
if (!services) console.warn(`no services bound to app ${app} - nothing to bind`)
|
|
163
160
|
|
package/bin/up.js
CHANGED
|
@@ -30,7 +30,7 @@ module.exports = Object.assign(up, {
|
|
|
30
30
|
const { execSync, spawn } = require('node:child_process')
|
|
31
31
|
const cds = require('../lib/cds')
|
|
32
32
|
const { highlight, bold } = require('../lib/util/term')
|
|
33
|
-
const { exists, fs, path } = cds.utils
|
|
33
|
+
const { exists, fs, path, path: { join } } = cds.utils
|
|
34
34
|
const { readProject } = require('../lib/init/projectReader')
|
|
35
35
|
|
|
36
36
|
const run = cmd => {
|
|
@@ -79,14 +79,18 @@ async function up() {
|
|
|
79
79
|
if (exists('mtx/sidecar') && !exists('mtx/sidecar/package-lock.json')) {
|
|
80
80
|
run('npm i --package-lock-only --prefix mtx/sidecar')
|
|
81
81
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
|
|
83
|
+
const app = cds.env.folders.app
|
|
84
|
+
const legacyToPreferred = {
|
|
85
|
+
[join(app, 'router')]: '.deploy/app-router',
|
|
86
|
+
[join(app, 'portal')]: '.deploy/portal',
|
|
87
|
+
[join(app, 'html5-deployer')]: '.deploy/html5-deployer'
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
-
|
|
89
|
+
for (const [legacy, preferred] of Object.entries(legacyToPreferred)) {
|
|
90
|
+
const root = exists(preferred) ? preferred : legacy
|
|
91
|
+
if (exists(join(root, 'package.json')) && !exists(join(root, 'package-lock.json'))) {
|
|
92
|
+
run(`npm i --package-lock-only --prefix ${root}`)
|
|
93
|
+
}
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
if (cds.cli.options.to === 'k8s') {
|
package/lib/bind/cf.js
CHANGED
|
@@ -33,6 +33,10 @@ module.exports = new class CFBind extends require('./plugin') {
|
|
|
33
33
|
return service
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
async services4(app) {
|
|
37
|
+
return (await cf.services(app)).filter(s => Object.keys(s.credentials).length > 0)
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
async resolve(name, binding) {
|
|
37
41
|
this.target ??= await cf.target()
|
|
38
42
|
this.#checkApiEndpoint(name, binding)
|
package/lib/bind/k8s.js
CHANGED
|
@@ -30,6 +30,27 @@ module.exports = new class K8SBind extends require('./plugin') {
|
|
|
30
30
|
return service
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
services4(app) {
|
|
34
|
+
const { execSync } = require('node:child_process')
|
|
35
|
+
const json = cmd => JSON.parse(execSync(cmd, { encoding: 'utf8' }))
|
|
36
|
+
|
|
37
|
+
const bindings = json('kubectl get servicebindings -o json').items
|
|
38
|
+
.filter(b => b.metadata.name.startsWith(app))
|
|
39
|
+
if (!bindings.length) throw `cannot find services with metadata name starting with '${app}'`
|
|
40
|
+
|
|
41
|
+
const offerings = Object.fromEntries(
|
|
42
|
+
json('kubectl get serviceinstances -o json').items
|
|
43
|
+
.map(i => [i.metadata.name, i.spec.serviceOfferingName])
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
for (const b of bindings) {
|
|
47
|
+
const offering = offerings[b.spec.serviceInstanceName]
|
|
48
|
+
b.label = b.type = offering
|
|
49
|
+
b.name = b.metadata.name
|
|
50
|
+
}
|
|
51
|
+
return bindings
|
|
52
|
+
}
|
|
53
|
+
|
|
33
54
|
async resolve(name, binding) {
|
|
34
55
|
const { context, namespace, cluster } = await this.#context4(binding)
|
|
35
56
|
|
package/lib/bind/plugin.js
CHANGED
|
@@ -36,6 +36,15 @@ module.exports = class BindPlugin {
|
|
|
36
36
|
throw new Error('credentials4 not implemented')
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Returns the services for the given app
|
|
41
|
+
* @param {string} app - The name of the app
|
|
42
|
+
* @returns {Promise<Array>} A promise that resolves to an array of bound service names
|
|
43
|
+
*/
|
|
44
|
+
services4(_app) {
|
|
45
|
+
throw new Error('services4 not implemented')
|
|
46
|
+
}
|
|
47
|
+
|
|
39
48
|
/**
|
|
40
49
|
* Resolves the binding configuration for the given service name
|
|
41
50
|
* @param {string} name - The name of the service
|
|
@@ -8,24 +8,13 @@ const BuildTaskProvider = require('./buildTaskProvider')
|
|
|
8
8
|
const { BuildError, normalizePath } = require('./util')
|
|
9
9
|
const DEBUG = cds.debug('cli|build')
|
|
10
10
|
|
|
11
|
-
/**
|
|
12
|
-
* @typedef {{
|
|
13
|
-
* clean?: boolean,
|
|
14
|
-
* outputMode?: number,
|
|
15
|
-
* for?: string,
|
|
16
|
-
* src?: string,
|
|
17
|
-
* dest?: string,
|
|
18
|
-
* } & {[key:string]:unknown}} BuildTaskProviderFactoryOptions
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
11
|
/**
|
|
22
|
-
*
|
|
23
|
-
* @typedef {object} Task
|
|
12
|
+
* @typedef {import('./plugin').Task} Task
|
|
24
13
|
*/
|
|
25
14
|
|
|
26
15
|
class BuildTaskProviderFactory {
|
|
27
16
|
/**
|
|
28
|
-
* @param {
|
|
17
|
+
* @param {import('./index').BuildOptions} options - build options
|
|
29
18
|
*/
|
|
30
19
|
constructor(options = {}) {
|
|
31
20
|
options.outputMode = options.outputMode || OUTPUT_MODE_FILESYSTEM
|
|
@@ -43,7 +32,7 @@ class BuildTaskProviderFactory {
|
|
|
43
32
|
}
|
|
44
33
|
}
|
|
45
34
|
})
|
|
46
|
-
/** @type {{ options:
|
|
35
|
+
/** @type {{ options: import('./index').BuildOptions, tasks: Task[]}} */
|
|
47
36
|
this._context = { options, tasks: [] } // resolved tasks
|
|
48
37
|
}
|
|
49
38
|
get options() {
|
|
@@ -112,6 +101,7 @@ class BuildTaskProviderFactory {
|
|
|
112
101
|
* 'for' defines an alias for built-in plugins like 'hana', 'java', 'node', 'fiori' or 'mtx'.
|
|
113
102
|
* 'use' defines the fully qualified module name of custom build plugins implementations.
|
|
114
103
|
* @param {Task} task
|
|
104
|
+
* @returns {typeof import('./plugin')}
|
|
115
105
|
*/
|
|
116
106
|
getPlugin(task) {
|
|
117
107
|
const provider = this._getProvider(task)
|
|
@@ -227,6 +217,10 @@ class PluginBuildTaskProvider extends BuildTaskProvider {
|
|
|
227
217
|
})
|
|
228
218
|
}
|
|
229
219
|
|
|
220
|
+
/**
|
|
221
|
+
* @param {{for: string}} key
|
|
222
|
+
* @returns {Task}
|
|
223
|
+
*/
|
|
230
224
|
getTask(key) {
|
|
231
225
|
// plugin entry is already validated, a taskDefaults default member exists
|
|
232
226
|
const task = JSON.parse(JSON.stringify(this.plugins.get(key.for)?.taskDefaults ?? {}))
|
package/lib/build/constants.js
CHANGED
|
@@ -38,6 +38,7 @@ exports.OPTION_WS = "ws" // run a workspa
|
|
|
38
38
|
exports.OPTION_WS_PACK = "ws-pack" // enable tarball based packaging of workspace dependencies
|
|
39
39
|
exports.CSV_FILE_TARGET = "csvFileTarget" // target folder when copying CSV files to the deployment target folder ./db/src/gen/*, default is 'data'
|
|
40
40
|
exports.CSV_FILE_DETECTION = "csvFileDetection" // detect CSV files based on CDS model location - ENABLED by default
|
|
41
|
+
exports.CSV_TRIM_WHITESPACES = "trimCsvWhitespaces" // trim whitespaces from CSV column names and values - DISABLED by default
|
|
41
42
|
exports.EXTENSION_POINT_VALIDATION = "extensionPointValidation" // validate extension point restrictions defined by the extended SaaS app - ENABLED by default
|
|
42
43
|
exports.MTX_SIDECAR_DB_VALIDATION = "mtxSidecarDbValidation" // validate consistency of app and sidecar db configuration - ENABLED by default
|
|
43
44
|
exports.CONTINUE_UNRESOLVED_SCHEMA_CHANGES = "continueUnresolvedSchemaChanges" // build doesn't fail if unresolved HANA schema changes are detected
|
package/lib/build/index.js
CHANGED
|
@@ -6,10 +6,25 @@ const { BuildError } = require('./util')
|
|
|
6
6
|
|
|
7
7
|
module.exports = { build, register, Plugin, BuildError }
|
|
8
8
|
|
|
9
|
+
// reuse schema definition from cds-rc.js used for package.json code completion
|
|
10
|
+
/** @typedef{import('../env/schemas/cds-rc')['$defs']['buildTaskType']['enum'][number]} BuildFor */
|
|
11
|
+
|
|
12
|
+
// using (string & {}) prevents widening of `'x' | string` to just `string`
|
|
13
|
+
/**
|
|
14
|
+
* @typedef {{
|
|
15
|
+
* clean?: boolean,
|
|
16
|
+
* outputMode?: number,
|
|
17
|
+
* for?: BuildFor | (string & {}),
|
|
18
|
+
* src?: string,
|
|
19
|
+
* dest?: string,
|
|
20
|
+
* model?: string[]
|
|
21
|
+
* } & {[key:string]:unknown}} BuildOptions
|
|
22
|
+
*/
|
|
23
|
+
|
|
9
24
|
/**
|
|
10
25
|
* Executes cds build in the directory defined by cds.root.
|
|
11
26
|
*
|
|
12
|
-
* @param {
|
|
27
|
+
* @param {BuildOptions} options - command options as defined by build command.
|
|
13
28
|
*/
|
|
14
29
|
async function build(options = {}) {
|
|
15
30
|
if (!fs.existsSync(cds.root) || !cds.utils.isdir(cds.root)) {
|
|
@@ -31,4 +46,4 @@ async function build(options = {}) {
|
|
|
31
46
|
|
|
32
47
|
function register(id, plugin) {
|
|
33
48
|
return require('./plugins').register(id, plugin)
|
|
34
|
-
}
|
|
49
|
+
}
|
package/lib/build/plugin.js
CHANGED
|
@@ -21,7 +21,7 @@ const DEBUG = cds.debug('cli|build')
|
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* The build plugin creates the build output for a dedicated build task. It is uniquely identified
|
|
24
|
-
* by the build task's `for<` or `use` property. The `use` property represents
|
|
24
|
+
* by the build task's `for<` or `use` property. The `use` property represents
|
|
25
25
|
* the fully qualified node module path of the build plugin implementation.
|
|
26
26
|
* The build task engine defines the following protocol. The methods are invoked in descending order:
|
|
27
27
|
* - `init()` - optional
|
|
@@ -40,13 +40,11 @@ class Plugin {
|
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* @type {Task | undefined}
|
|
43
|
-
* @private
|
|
44
43
|
*/
|
|
45
44
|
_task
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
47
|
* @type {Context | undefined}
|
|
49
|
-
* @private
|
|
50
48
|
*/
|
|
51
49
|
_context
|
|
52
50
|
|
|
@@ -69,7 +67,7 @@ class Plugin {
|
|
|
69
67
|
*/
|
|
70
68
|
_messages = []
|
|
71
69
|
/**
|
|
72
|
-
* Returns a list of build and compiler messages created by this build plugin.
|
|
70
|
+
* Returns a list of build and compiler messages created by this build plugin.
|
|
73
71
|
* Supported message severities are 'Info', 'Warning', and 'Error'.
|
|
74
72
|
*/
|
|
75
73
|
get messages() {
|
|
@@ -176,9 +174,9 @@ class Plugin {
|
|
|
176
174
|
|
|
177
175
|
/**
|
|
178
176
|
* Asynchronously copies a single file or the entire directory structure from 'src' to 'dest', including subdirectories and files.
|
|
179
|
-
*
|
|
177
|
+
*
|
|
180
178
|
* Note: The file names are stored in the list of files written by this build plugin.
|
|
181
|
-
*
|
|
179
|
+
*
|
|
182
180
|
* @param {string} src - The absolute or relative source path of the file or directory to copy.
|
|
183
181
|
* Relative paths will be resolved to this task's source path.
|
|
184
182
|
* @returns {FileWriter}
|
|
@@ -216,7 +214,7 @@ class Plugin {
|
|
|
216
214
|
|
|
217
215
|
/**
|
|
218
216
|
* Adds the given user message and severity to the list of messages issued by this build task.
|
|
219
|
-
*
|
|
217
|
+
*
|
|
220
218
|
* User messages will be logged after CDS build has been finished based on the log-level that has been set.
|
|
221
219
|
* By default messages with severity <em>warning</em> and <em>error</em> will be logged.
|
|
222
220
|
* @param {string} message - the message text
|
|
@@ -74,7 +74,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
74
74
|
await this._writePackageJson()
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// copy native hana artifacts from other workspaces including .hdbmigrationtable files defined for
|
|
77
|
+
// copy native hana artifacts from other workspaces including .hdbmigrationtable files defined for
|
|
78
78
|
// common model artifacts that are not part of the current project
|
|
79
79
|
if (wsPaths.length || cds.cli.options?.[OPTION_WS] || hasOptionValue(this.task.options[OPTION_WS], true)) {
|
|
80
80
|
// first is winning, e.g. a .hdiconfig file might exist in multiple workspaces
|
|
@@ -272,7 +272,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
272
272
|
|
|
273
273
|
for (const file of csvFiles) {
|
|
274
274
|
if (this.isStagingBuild() || file.startsWith(this.task.options.compileDest)) {
|
|
275
|
-
await stripComments(file)
|
|
275
|
+
await stripComments(file, null, this.task.options.trimCsvWhitespaces)
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
}
|
|
@@ -394,7 +394,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
396
|
// The last-dev CSN shall only be updated, if all .hdbmigrationtable files could be successfully updated.
|
|
397
|
-
// Of course, .hdbmigrationtable files already written would need to be manually reverted before the
|
|
397
|
+
// Of course, .hdbmigrationtable files already written would need to be manually reverted before the
|
|
398
398
|
// cds build command is again executed.
|
|
399
399
|
await Promise.all(promises)
|
|
400
400
|
|
|
@@ -578,7 +578,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
578
578
|
/**
|
|
579
579
|
* Returns all hana native artifacts located in db/src/** workspace folders.
|
|
580
580
|
* @param {Array} workspacePaths Array of workspace paths, 'cds.root' is excluded from the list
|
|
581
|
-
* @returns {Array} Array of workspace files, each entry contains the db src directory,
|
|
581
|
+
* @returns {Array} Array of workspace files, each entry contains the db src directory,
|
|
582
582
|
* the relative file path and the file name.
|
|
583
583
|
*/
|
|
584
584
|
async _getWorkspaceFiles(workspacePaths) {
|
|
@@ -278,7 +278,7 @@ module.exports = {
|
|
|
278
278
|
},
|
|
279
279
|
|
|
280
280
|
buildTaskType: {
|
|
281
|
-
enum: [
|
|
281
|
+
enum: /**@type { const } */ ([
|
|
282
282
|
"nodejs",
|
|
283
283
|
"java",
|
|
284
284
|
"hana",
|
|
@@ -288,7 +288,7 @@ module.exports = {
|
|
|
288
288
|
"node-cf",
|
|
289
289
|
"java-cf",
|
|
290
290
|
"helm"
|
|
291
|
-
],
|
|
291
|
+
]),
|
|
292
292
|
enumDescriptions: [
|
|
293
293
|
"Node.js build plugin",
|
|
294
294
|
"Java build plugin",
|
package/lib/init/constants.js
CHANGED
|
@@ -5,7 +5,7 @@ module.exports = {
|
|
|
5
5
|
COMMAND_ADD: 'add',
|
|
6
6
|
|
|
7
7
|
JAVA_LTS_VERSIONS: [17, 21],
|
|
8
|
-
MAVEN_ARCHETYPE_VERSION: '4.
|
|
8
|
+
MAVEN_ARCHETYPE_VERSION: '4.2.0',
|
|
9
9
|
|
|
10
10
|
OPTIONS: Object.freeze({
|
|
11
11
|
COMPLETION: 'completion',
|
|
@@ -60,6 +60,7 @@ module.exports = {
|
|
|
60
60
|
NOTIFICATIONS: 'notifications',
|
|
61
61
|
WORKZONE: 'workzone',
|
|
62
62
|
WORKZONE_STANDARD: 'workzone-standard',
|
|
63
|
+
UI5: 'ui5',
|
|
63
64
|
DATA: 'data',
|
|
64
65
|
HTTP: 'http',
|
|
65
66
|
LINT: 'lint',
|
|
@@ -74,7 +75,7 @@ module.exports = {
|
|
|
74
75
|
|
|
75
76
|
URLS: Object.freeze({
|
|
76
77
|
PIPELINE_HELP: 'https://www.project-piper.io/',
|
|
77
|
-
SAMPLES: 'https://github.com/
|
|
78
|
+
SAMPLES: 'https://github.com/capire/samples',
|
|
78
79
|
CAPIRE: 'https://cap.cloud.sap',
|
|
79
80
|
MAVEN_ARCHETYPE_HELP: `https://cap.cloud.sap/docs/java/developing-applications/building#the-maven-archetype`,
|
|
80
81
|
MAVEN_INSTALL_HELP: `https://maven.apache.org/install.html`,
|
|
@@ -124,7 +124,6 @@ module.exports = new class ProjectReader {
|
|
|
124
124
|
const _isTypescript = () => !!pkgJson().dependencies?.typescript || !!pkgJson().devDependencies?.typescript
|
|
125
125
|
const _hasTyper = () => !!pkgJson().dependencies?.['@cap-js/cds-typer'] || !!pkgJson().devDependencies?.['@cap-js/cds-typer']
|
|
126
126
|
const _inProd = plugin => require(`./template/${plugin}`).hasInProduction(env) || cds.cli.argv.includes(plugin) || cds.cli.options.add?.has(plugin)
|
|
127
|
-
const _ui5 = () => pkgJson().sapux
|
|
128
127
|
const _uiEmbedded = () => exists(join(env.folders.app, 'xs-app.json'))
|
|
129
128
|
const _uiModule = () => !_uiEmbedded() && isdir(env.folders.app) && fs.readdirSync(resolve(cds.root, env.folders.app)).length > 0
|
|
130
129
|
const _isJava = () => this.isJava
|
|
@@ -134,6 +133,7 @@ module.exports = new class ProjectReader {
|
|
|
134
133
|
}
|
|
135
134
|
const reserved = {
|
|
136
135
|
dependencies: () => pkgJson().dependencies,
|
|
136
|
+
devDependencies: () => pkgJson().devDependencies,
|
|
137
137
|
has: x => _inProd(x),
|
|
138
138
|
profile: () => cds.cli.options?.for,
|
|
139
139
|
language: () => _isJava() ? 'java' : 'nodejs',
|
|
@@ -183,7 +183,7 @@ module.exports = new class ProjectReader {
|
|
|
183
183
|
appPath: () => env.folders.app,
|
|
184
184
|
appVersion: () => appVersion,
|
|
185
185
|
jdkVersion: () => jdkVersion,
|
|
186
|
-
approuterPath: () => (_uiEmbedded() ? join(env.folders.app) : join(env.folders.app, 'router')).replace(/\\/g, '/'),
|
|
186
|
+
approuterPath: () => (_uiEmbedded() ? join(env.folders.app) : exists('.deploy/app-router') ? join('.deploy', 'app-router') : join(env.folders.app, 'router')).replace(/\\/g, '/'),
|
|
187
187
|
appName: () => appName,
|
|
188
188
|
cleanedAppName: () => appName.replaceAll('_', '-'),
|
|
189
189
|
strippedAppName: () => appName.replace(/-/g, ''),
|
|
@@ -192,8 +192,6 @@ module.exports = new class ProjectReader {
|
|
|
192
192
|
hasUIEmbedded: _uiEmbedded,
|
|
193
193
|
hasUIModule: _uiModule,
|
|
194
194
|
hasUI: () => _uiEmbedded() || _uiModule(),
|
|
195
|
-
isUI5: _ui5,
|
|
196
|
-
hasUI5: _ui5,
|
|
197
195
|
literal: () => () => text => text,
|
|
198
196
|
hasMTXRoute: () => _inProd('extensibility') || (_inProd('multitenancy') && !_isJava()),
|
|
199
197
|
hasMTXRouteJava: () => (_inProd('helm-unified-runtime') || _inProd('helm')) && _isJava() && _inProd('multitenancy'),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const cds = require('../../../cds')
|
|
2
1
|
const { readProject } = require('../../projectReader')
|
|
3
2
|
const { merge, sort } = require('../../merge')
|
|
4
3
|
const { srv4 } = require('../../registries/mta')
|
|
@@ -15,7 +14,7 @@ module.exports = class AmsTemplate extends require('../../plugin') {
|
|
|
15
14
|
}
|
|
16
15
|
|
|
17
16
|
static hasInProduction(env) {
|
|
18
|
-
return env.requires?.auth?.ams ||
|
|
17
|
+
return env.requires?.auth?.ams || readProject().devDependencies?.['@sap/ams']
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
async run() {
|
|
@@ -19,7 +19,7 @@ module.exports = class ApprouterTemplate extends require('../../plugin') {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
static hasInProduction(env) {
|
|
22
|
-
return exists(join(env.folders.app, 'xs-app.json')) || exists(join(env.folders.app, 'router', 'xs-app.json'))
|
|
22
|
+
return exists(join('.deploy', 'app-router')) || exists(join(env.folders.app, 'xs-app.json')) || exists(join(env.folders.app, 'router', 'xs-app.json'))
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async run() {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
entity my.bookshop.Books {
|
|
2
|
-
key ID
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
key ID : Integer;
|
|
3
|
+
title : String @title: 'Title';
|
|
4
|
+
stock : Integer @title: 'Stock';
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
service CatalogService {
|
|
@@ -9,7 +9,7 @@ service CatalogService {
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
annotate CatalogService.Books with @UI.LineItem: [
|
|
12
|
-
{
|
|
13
|
-
{
|
|
14
|
-
{
|
|
12
|
+
{Value: ID},
|
|
13
|
+
{Value: title},
|
|
14
|
+
{Value: stock},
|
|
15
15
|
];
|
|
@@ -28,7 +28,7 @@ module.exports = class ExtensionProjectTemplate extends require('../../plugin')
|
|
|
28
28
|
if (typeof base !== 'string' || !base?.length) base = '@capire/bookshop'
|
|
29
29
|
|
|
30
30
|
const pkgJson = exists('package.json') ? await read('package.json') : { name: 'my-extension-project' }
|
|
31
|
-
if (!pkgJson.extends
|
|
31
|
+
if (force || !pkgJson.extends) {
|
|
32
32
|
pkgJson.extends = base
|
|
33
33
|
}
|
|
34
34
|
|
|
@@ -38,7 +38,7 @@ module.exports = class ExtensionProjectTemplate extends require('../../plugin')
|
|
|
38
38
|
}
|
|
39
39
|
await write('package.json', pkgJson, { spaces: 2 })
|
|
40
40
|
|
|
41
|
-
if (!exists('.base/package.json')
|
|
41
|
+
if (force || !exists('.base/package.json')) {
|
|
42
42
|
await write('.base/package.json', {name: base})
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -56,17 +56,17 @@ module.exports = class ExtensionProjectTemplate extends require('../../plugin')
|
|
|
56
56
|
const extensionNamespace = `${namespacify(proj.appName)}.${namespacify(projectSimple)}`
|
|
57
57
|
const { force } = cds.cli.options
|
|
58
58
|
|
|
59
|
-
if (!exists('.base/index.cds')
|
|
59
|
+
if (force || !exists('.base/index.cds')) {
|
|
60
60
|
await copy(join(__dirname, 'files/.base/index.cds'), '.base/index.cds')
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
if (!exists('app/extension.cds')
|
|
63
|
+
if (force || !exists('app/extension.cds')) {
|
|
64
64
|
await renderAndCopy(join(__dirname, 'files/app'), 'app', {
|
|
65
65
|
basePackage: base,
|
|
66
66
|
extensionNamespace
|
|
67
67
|
})
|
|
68
68
|
}
|
|
69
|
-
if (!exists('test/data')
|
|
69
|
+
if (force || !exists('test/data')) {
|
|
70
70
|
await copy(join(__dirname, 'files/test/data/my.bookshop-Books.csv'), 'test/data/my.bookshop-Books.csv')
|
|
71
71
|
await copy(join(__dirname, 'files/test/data/Authors.csv'), `test/data/${extensionNamespace}-x_Authors.csv`)
|
|
72
72
|
}
|
package/lib/init/template/github-actions/files/{cf-github-deploy → github-deployment}/action.yaml
RENAMED
|
@@ -5,6 +5,9 @@ inputs:
|
|
|
5
5
|
app-name:
|
|
6
6
|
description: "Base name of the deployed CAP app"
|
|
7
7
|
required: true
|
|
8
|
+
environment:
|
|
9
|
+
description: "Deployment environment name"
|
|
10
|
+
required: true
|
|
8
11
|
|
|
9
12
|
runs:
|
|
10
13
|
using: "composite"
|
|
@@ -21,20 +24,20 @@ runs:
|
|
|
21
24
|
uses: actions/github-script@v7
|
|
22
25
|
env:
|
|
23
26
|
URL: ${{ steps.get-url.outputs.url }}
|
|
27
|
+
ENVIRONMENT: ${{ inputs.environment }}
|
|
24
28
|
with:
|
|
25
29
|
script: |
|
|
26
30
|
const { owner, repo } = context.repo
|
|
27
31
|
const ref = context.ref
|
|
28
32
|
const url = process.env.URL
|
|
33
|
+
const environment = process.env.ENVIRONMENT
|
|
29
34
|
|
|
30
35
|
const deployment = await github.rest.repos.createDeployment({
|
|
31
36
|
owner,
|
|
32
37
|
repo,
|
|
33
38
|
ref,
|
|
34
39
|
required_contexts: [],
|
|
35
|
-
environment
|
|
36
|
-
auto_merge: false,
|
|
37
|
-
description: 'Deployed to Cloud Foundry',
|
|
40
|
+
environment
|
|
38
41
|
})
|
|
39
42
|
|
|
40
43
|
await github.rest.repos.createDeploymentStatus({
|
|
@@ -43,6 +46,5 @@ runs:
|
|
|
43
46
|
deployment_id: deployment.data.id,
|
|
44
47
|
state: 'success',
|
|
45
48
|
environment_url: url,
|
|
46
|
-
log_url: `https://github.com/${owner}/${repo}/actions/runs/${context.runId}
|
|
47
|
-
description: 'CAP app is live',
|
|
49
|
+
log_url: `https://github.com/${owner}/${repo}/actions/runs/${context.runId}`
|
|
48
50
|
})
|
|
@@ -3,11 +3,14 @@ name: Cloud Foundry
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_call:
|
|
5
5
|
workflow_dispatch:
|
|
6
|
-
merge_group:
|
|
7
6
|
push:
|
|
8
7
|
branches:
|
|
9
8
|
- main
|
|
10
9
|
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
deployments: write
|
|
13
|
+
|
|
11
14
|
concurrency:
|
|
12
15
|
group: {{#literal}}cf-${{ github.workflow }}-${{ github.head_ref || github.run_id }}{{/literal}}
|
|
13
16
|
cancel-in-progress: true
|
|
@@ -18,7 +21,7 @@ jobs:
|
|
|
18
21
|
deploy:
|
|
19
22
|
runs-on: {{#sap}}[self-hosted, solinas]{{/sap}}{{^sap}}ubuntu-latest{{/sap}}
|
|
20
23
|
steps:
|
|
21
|
-
- uses: actions/checkout@
|
|
24
|
+
- uses: actions/checkout@v5
|
|
22
25
|
- uses: actions/setup-node@v4
|
|
23
26
|
with:
|
|
24
27
|
node-version: 22
|
|
@@ -32,21 +35,21 @@ jobs:
|
|
|
32
35
|
- run: npm install
|
|
33
36
|
- run: npx cds up
|
|
34
37
|
|
|
35
|
-
- uses: ./.github/actions/
|
|
38
|
+
- uses: ./.github/actions/github-deployment
|
|
36
39
|
with:
|
|
37
40
|
app-name: {{#literal}}${{ env.APP_NAME }}{{/literal}}
|
|
38
41
|
|
|
39
42
|
{{#hasApprouter}}
|
|
40
|
-
- run: cf logs "${{ env.APP_NAME }}" --recent
|
|
43
|
+
- run: cf logs {{#literal}}"${{ env.APP_NAME }}"{{/literal}} --recent
|
|
41
44
|
if: always()
|
|
42
45
|
{{/hasApprouter}}
|
|
43
|
-
- run: cf logs "${{ env.APP_NAME }}-srv" --recent
|
|
46
|
+
- run: cf logs {{#literal}}"${{ env.APP_NAME }}-srv"{{/literal}} --recent
|
|
44
47
|
if: always()
|
|
45
48
|
{{^hasMultitenancy}}
|
|
46
|
-
- run: cf logs "${{ env.APP_NAME }}-db-deployer" --recent
|
|
49
|
+
- run: cf logs {{#literal}}"${{ env.APP_NAME }}-db-deployer"{{/literal}} --recent
|
|
47
50
|
if: always()
|
|
48
51
|
{{/hasMultitenancy}}
|
|
49
52
|
{{#hasMultitenancy}}
|
|
50
|
-
- run: cf logs "${{ env.APP_NAME }}-mtx" --recent
|
|
53
|
+
- run: cf logs {{#literal}}"${{ env.APP_NAME }}-mtx"{{/literal}} --recent
|
|
51
54
|
if: always()
|
|
52
55
|
{{/hasMultitenancy}}
|
|
@@ -3,11 +3,14 @@ name: Kyma
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_call:
|
|
5
5
|
workflow_dispatch:
|
|
6
|
-
merge_group:
|
|
7
6
|
push:
|
|
8
7
|
branches:
|
|
9
8
|
- main
|
|
10
9
|
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
deployments: write
|
|
13
|
+
|
|
11
14
|
concurrency:
|
|
12
15
|
group: {{#literal}}deploy-${{ github.workflow }}-${{ github.head_ref || github.run_id }}{{/literal}}
|
|
13
16
|
cancel-in-progress: true
|
|
@@ -16,11 +19,11 @@ env:
|
|
|
16
19
|
APP_NAME: {{ appName }}
|
|
17
20
|
jobs:
|
|
18
21
|
deploy:
|
|
19
|
-
runs-on: [self-hosted,
|
|
22
|
+
runs-on: {{#sap}}[self-hosted, solinas]{{/sap}}{{^sap}}ubuntu-latest{{/sap}}
|
|
20
23
|
container:
|
|
21
24
|
image: public.int.repositories.cloud.sap/ubuntu:24.04
|
|
22
25
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
26
|
+
- uses: actions/checkout@v5
|
|
24
27
|
- uses: actions/setup-node@v4
|
|
25
28
|
with:
|
|
26
29
|
node-version: 22
|