@sap/cds-dk 9.4.2 → 9.5.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 +94 -0
- package/bin/compile/index.js +1 -1
- package/bin/data-products/index.js +10 -2
- package/bin/data-products/prepare-csn.js +63 -26
- package/bin/export.js +1 -1
- package/bin/help.js +2 -3
- package/bin/import.js +13 -3
- package/bin/repl.js +7 -11
- package/bin/up.js +164 -13
- package/bin/util/containerize.js +183 -0
- package/bin/version.js +4 -4
- package/lib/bind/k8s.js +1 -1
- package/lib/build/buildTaskEngine.js +5 -2
- package/lib/build/plugin.js +1 -1
- package/lib/build/plugins/helm/files/templates/_deployment_helpers.tpl +1 -1
- package/lib/build/plugins/helm/index.js +7 -7
- package/lib/build/provider/hana/2migration.js +16 -24
- package/lib/build/provider/hana/index.js +11 -3
- package/lib/build/provider/hana/template/.hdiconfig-all +7 -1
- package/lib/build/provider/hana/template/package.json +1 -1
- package/lib/build/provider/internalBuildPlugin.js +15 -1
- package/lib/build/provider/java/index.js +12 -1
- package/lib/build/provider/mtx-sidecar/index.js +2 -1
- package/lib/build/provider/nodejs/index.js +2 -1
- package/lib/build/util.js +3 -3
- package/lib/compile/to_openapi.js +1 -5
- package/lib/import/common.js +5 -4
- package/lib/import/message.js +1 -1
- package/lib/import/odata/ParserContext.js +1 -0
- package/lib/import/odata/v4parser.js +48 -3
- package/lib/import/openapi/importOpenAPI.js +6 -1
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +15 -6
- package/lib/init/merge.js +16 -0
- package/lib/init/projectReader.js +54 -24
- package/lib/init/registries/mta.js +5 -2
- package/lib/init/registries/xs-app.js +7 -1
- package/lib/init/template/ams/index.js +1 -1
- package/lib/init/template/app-frontend/files/app-package.json.hbs +1 -7
- package/lib/init/template/app-frontend/files/ui5.yaml.hbs +20 -1
- package/lib/init/template/app-frontend/index.js +0 -4
- package/lib/init/template/application-logging/index.js +9 -14
- package/lib/init/template/approuter/files/mta.yaml.hbs +7 -2
- package/lib/init/template/approuter/index.js +21 -16
- package/lib/init/template/audit-logging/index.js +2 -2
- package/lib/init/template/cloud-logging/index.js +2 -3
- package/lib/init/template/containerize/files/containerize.yaml.hbs +17 -3
- package/lib/init/template/containerize/index.js +0 -4
- package/lib/init/template/data/as-json.js +18 -20
- package/lib/init/template/destination/index.js +0 -4
- package/lib/init/template/dynatrace/index.js +0 -2
- package/lib/init/template/github-actions/files/kyma-setup/action.yaml +0 -2
- package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +16 -1
- package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +20 -0
- package/lib/init/template/github-actions/index.js +5 -8
- package/lib/init/template/hana/files/db/package.json +2 -2
- package/lib/init/template/hana/files/mta.yaml.hbs +0 -4
- package/lib/init/template/hana/index.js +5 -7
- package/lib/init/template/helm/index.js +16 -33
- package/lib/init/template/helm-unified-runtime/index.js +0 -4
- package/lib/init/template/html5-repo/files/app-package.json.hbs +1 -7
- package/lib/init/template/html5-repo/files/containerize.yaml.hbs +0 -9
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +0 -7
- package/lib/init/template/html5-repo/files/ui5.yaml.hbs +20 -1
- package/lib/init/template/html5-repo/files/values.yaml.hbs +6 -0
- package/lib/init/template/html5-repo/files/xs-app.json.hbs +5 -3
- package/lib/init/template/html5-repo/index.js +8 -14
- package/lib/init/template/ias/files/values.yaml.hbs +6 -6
- package/lib/init/template/ias/index.js +14 -23
- package/lib/init/template/kyma/index.js +14 -1
- package/lib/init/template/malware-scanner/files/mta.yaml.hbs +3 -1
- package/lib/init/template/malware-scanner/files/values.yaml +3 -1
- package/lib/init/template/multitenancy/files/mta.yaml.hbs +1 -3
- package/lib/init/template/multitenancy/files/values.yaml.hbs +8 -0
- package/lib/init/template/multitenancy/index.js +10 -5
- package/lib/init/template/nodejs/files/package.json.hbs +1 -1
- package/lib/init/template/pipeline/files/app-package.json.hbs +1 -7
- package/lib/init/template/pipeline/files/ui5.yaml.hbs +20 -1
- package/lib/init/template/pipeline/index.js +6 -3
- package/lib/init/template/portal/files/mta.yaml.hbs +5 -3
- package/lib/init/template/portal/index.js +3 -6
- package/lib/init/template/sample/files/_i18n/i18n_de.properties +12 -0
- package/lib/init/template/sample/files/_i18n/i18n_en.properties +21 -0
- package/lib/init/template/sample/files/_i18n/i18n_fr.properties +20 -0
- package/lib/init/template/sample/files/_i18n/messages_de.properties +34 -0
- package/lib/init/template/sample/files/_i18n/messages_en.properties +34 -0
- package/lib/init/template/sample/files/_i18n/messages_fr.properties +34 -0
- package/lib/init/template/sample/files/app/_i18n/i18n_de.properties +0 -26
- package/lib/init/template/sample/files/app/_i18n/i18n_en.properties +8 -0
- package/lib/init/template/sample/files/app/admin-authors/fiori-service.cds +52 -0
- package/lib/init/template/sample/files/app/admin-authors/webapp/Component.js.hbs +3 -0
- package/lib/init/template/sample/files/app/admin-authors/webapp/i18n/i18n.properties +11 -0
- package/lib/init/template/sample/files/app/admin-authors/webapp/manifest.json +130 -0
- package/lib/init/template/sample/files/app/admin-books/fiori-service.cds +81 -88
- package/lib/init/template/sample/files/app/admin-books/webapp/Component.js.hbs +3 -0
- package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n.properties +10 -2
- package/lib/init/template/sample/files/app/admin-books/webapp/manifest.json +24 -48
- package/lib/init/template/sample/files/app/appconfig/fioriSandboxConfig.json +54 -3
- package/lib/init/template/sample/files/app/browse/fiori-service.cds +30 -30
- package/lib/init/template/sample/files/app/browse/webapp/Component.js.hbs +3 -0
- package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n.properties +10 -3
- package/lib/init/template/sample/files/app/browse/webapp/manifest.json +18 -23
- package/lib/init/template/sample/files/app/common.cds +126 -158
- package/lib/init/template/sample/files/app/fiori-apps.html +30 -0
- package/lib/init/template/sample/files/app/genres/fiori-service.cds +33 -0
- package/lib/init/template/sample/files/app/genres/tree-view.cds +42 -0
- package/lib/init/template/sample/files/app/genres/value-help.cds +6 -0
- package/lib/init/template/sample/files/app/genres/webapp/Component.js.hbs +3 -0
- package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n.properties +4 -0
- package/lib/init/template/sample/files/app/genres/webapp/i18n/i18n_de.properties +2 -0
- package/lib/init/template/sample/files/app/genres/webapp/manifest.json +126 -0
- package/lib/init/template/sample/files/app/services.cds +6 -2
- package/lib/init/template/sample/files/db/currencies.cds +13 -0
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Books.csv +5 -5
- package/lib/init/template/sample/files/db/data/{sap.capire.bookshop-Books_texts.csv → sap.capire.bookshop-Books.texts.csv} +4 -4
- package/lib/init/template/sample/files/db/data/sap.capire.bookshop-Genres.csv +42 -15
- package/lib/init/template/sample/files/db/data/sap.common-Currencies.csv +12 -0
- package/lib/init/template/sample/files/db/data/sap.common-Currencies.texts.csv +15 -0
- package/lib/init/template/sample/files/db/hana/index.cds +10 -0
- package/lib/init/template/sample/files/db/schema.cds +35 -22
- package/lib/init/template/sample/files/db/sqlite/index.cds +10 -0
- package/lib/init/template/sample/files/package.json.hbs +14 -1
- package/lib/init/template/sample/files/srv/access-control.cds +3 -0
- package/lib/init/template/sample/files/srv/admin-service.cds +3 -2
- package/lib/init/template/sample/files/srv/cat-service.cds +7 -11
- package/lib/init/template/sample/index.js +26 -17
- package/lib/init/template/telemetry/index.js +1 -3
- package/lib/init/template/typescript/files/package.json +1 -1
- package/lib/init/template/workzone-standard/files/mta.yaml.hbs +0 -5
- package/lib/init/template/workzone-standard/index.js +0 -1
- package/lib/init/template/xsuaa/files/mta.yaml.hbs +4 -0
- package/lib/init/template/xsuaa/index.js +5 -7
- package/lib/lint/Linter.js +10 -4
- package/lib/lint/eslintHelp.json +1 -1
- package/lib/mtx/tenant_action.js +12 -4
- package/lib/util/checksum.js +32 -0
- package/lib/util/question.js +60 -7
- package/npm-shrinkwrap.json +61 -37
- package/package.json +2 -2
- package/lib/init/template/app-frontend/files/ui5-deploy.yaml.hbs +0 -25
- package/lib/init/template/github-actions/files/cf-setup/action.yaml +0 -53
- package/lib/init/template/html5-repo/files/ui5-deploy.yaml.hbs +0 -25
- package/lib/init/template/pipeline/files/ui5-deploy.yaml.hbs +0 -25
- package/lib/init/template/sample/files/app/_i18n/i18n.properties +0 -29
- package/lib/init/template/sample/files/app/admin-books/webapp/Component.js +0 -8
- package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_de.properties +0 -3
- package/lib/init/template/sample/files/app/admin-books/webapp/i18n/i18n_en.properties +0 -3
- package/lib/init/template/sample/files/app/browse/webapp/Component.js +0 -7
- package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_de.properties +0 -3
- package/lib/init/template/sample/files/app/browse/webapp/i18n/i18n_en.properties +0 -4
- package/lib/init/template/sample/files/app/index.html +0 -32
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
const cds = require('../../lib/cds')
|
|
2
|
+
const { path } = cds.utils
|
|
3
|
+
const { join } = path
|
|
4
|
+
const { execSync } = require('node:child_process')
|
|
5
|
+
const { highlight, info } = require('../../lib/util/term')
|
|
6
|
+
const { calcDirChecksum } = require('../../lib/util/checksum')
|
|
7
|
+
|
|
8
|
+
const buildpacks = new Set([
|
|
9
|
+
'java', 'nodejs', 'sap-machine', 'executable-jar', 'spring-boot', 'syft'
|
|
10
|
+
])
|
|
11
|
+
const builders = new Set([
|
|
12
|
+
'builder-jammy-base', 'builder-jammy-buildpackless-base', 'builder-jammy-full', 'builder-jammy-buildpackless-full'
|
|
13
|
+
])
|
|
14
|
+
|
|
15
|
+
const keyProcessorMap = {
|
|
16
|
+
dockerfile: processDockerfile,
|
|
17
|
+
commands: processCommands,
|
|
18
|
+
buildpack: processBuildpack
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const run = (cmd, { continueOnFail = false, silent = false } = {}) => {
|
|
22
|
+
cmd = Array.isArray(cmd) ? cmd.join(' ') : cmd
|
|
23
|
+
console.log(highlight(cmd))
|
|
24
|
+
try {
|
|
25
|
+
return execSync(cmd, {
|
|
26
|
+
stdio: silent ? 'pipe' : 'inherit',
|
|
27
|
+
env: { ...process.env, FORCE_COLOR: cds.utils.colors.enabled }
|
|
28
|
+
})
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (continueOnFail) {
|
|
31
|
+
if (!silent)
|
|
32
|
+
console.error(error.message)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
throw error.message
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function appendSubcommand(param, subcommand) {
|
|
40
|
+
if (!param) return []
|
|
41
|
+
if (subcommand === '--env') {
|
|
42
|
+
return Object.entries(param).flatMap(([k, v]) =>
|
|
43
|
+
String(v).split(',').map(env => [subcommand, `${k}=${env.trim() || '""'}`])
|
|
44
|
+
).flat()
|
|
45
|
+
}
|
|
46
|
+
return param.split(',').map(e => [subcommand, e.trim()]).flat()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function processDockerfile(bp, name, tag) {
|
|
50
|
+
if (!bp.dockerfile) return []
|
|
51
|
+
return [['docker', 'build',
|
|
52
|
+
...appendSubcommand(`${name}:${tag ?? 'latest'}`, '-t'),
|
|
53
|
+
...appendSubcommand(bp.dockerfile, '-f'),
|
|
54
|
+
'.']]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function processCommands(bp) {
|
|
58
|
+
return bp.commands ? bp.commands.map(c => c.trim().split(' ')) : []
|
|
59
|
+
}
|
|
60
|
+
function processBuildpack(bp, name, tag, clearCache = false) {
|
|
61
|
+
let { type, path, env, builder } = bp.buildpack
|
|
62
|
+
type = type ? type.split(',').map(t => buildpacks.has(t.trim()) ? `paketo-buildpacks/${t.trim()}` : t.trim()) : []
|
|
63
|
+
if (builders.has(builder)) builder = `paketobuildpacks/${builder}`
|
|
64
|
+
const cacheArgs = clearCache ? ['--clear-cache'] : []
|
|
65
|
+
return [['pack',
|
|
66
|
+
...appendSubcommand(`${name}:${tag}`, 'build'),
|
|
67
|
+
...appendSubcommand(path, '--path'),
|
|
68
|
+
...type.flatMap(t => appendSubcommand(t, '--buildpack')),
|
|
69
|
+
...appendSubcommand(builder, '--builder'),
|
|
70
|
+
...appendSubcommand(env, '--env'),
|
|
71
|
+
...cacheArgs
|
|
72
|
+
]]
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parses a YAML configuration file to extract Docker build and push commands for modules.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} filename - The path to the YAML file to parse.
|
|
79
|
+
* @param {string} [repoOpt=''] - Optional repository override. If not provided, uses the repository defined in the YAML file.
|
|
80
|
+
* @param {boolean} [clearCache=false] - Whether to clear cache for buildpack builds.
|
|
81
|
+
* @returns {Promise<{
|
|
82
|
+
* commands: Array<{
|
|
83
|
+
* name: string,
|
|
84
|
+
* tag: string,
|
|
85
|
+
* buildCmd: Array<string>,
|
|
86
|
+
* tagCmd: Array<string>,
|
|
87
|
+
* pushCmd: Array<string>,
|
|
88
|
+
* image: string,
|
|
89
|
+
* checksum: string|null
|
|
90
|
+
* path: string
|
|
91
|
+
* }>,
|
|
92
|
+
* before_all: Array<Array<string>>,
|
|
93
|
+
* repository: string
|
|
94
|
+
* }>} An object containing the commands for each module, any global pre-commands, and the repository name.
|
|
95
|
+
* @throws {Error} If modules are not defined in the YAML file or if build-parameters are invalid.
|
|
96
|
+
*/
|
|
97
|
+
async function parseYAML(filename, repoOpt = '', options) {
|
|
98
|
+
const file = cds.load.yaml(join(cds.root, filename))
|
|
99
|
+
const { repository: repoFile, modules, tag: globalTag = 'latest' } = file
|
|
100
|
+
const repository = repoOpt || repoFile
|
|
101
|
+
const before_all = file['before-all'] ? file['before-all'].map(c => c.trim().split(' ')) : []
|
|
102
|
+
if (!modules) throw `no modules defined in ${filename}`
|
|
103
|
+
const commands = await Promise.all(modules.map(async (m) => {
|
|
104
|
+
const name = m.name, tag = m.tag || globalTag, bp = m['build-parameters']
|
|
105
|
+
const rawImg = `${repository}/${name}`, source = `${name}:${tag}`, repoImg = `${repository}/${source}`
|
|
106
|
+
const key = Object.keys(bp)[0]
|
|
107
|
+
let checksum = null
|
|
108
|
+
if (bp.buildpack?.path)
|
|
109
|
+
checksum = await calcDirChecksum(
|
|
110
|
+
join(cds.root, bp.buildpack.path),
|
|
111
|
+
(dir) => !dir.includes('node_modules'),
|
|
112
|
+
'sha256',
|
|
113
|
+
[
|
|
114
|
+
join(cds.root, filename),
|
|
115
|
+
join(cds.root, 'package.json')
|
|
116
|
+
]
|
|
117
|
+
)
|
|
118
|
+
const buildType = keyProcessorMap[key]
|
|
119
|
+
|
|
120
|
+
if (!buildType) throw `'build-parameters' ${Object.keys(bp)} in ${filename} is invalid`
|
|
121
|
+
return {
|
|
122
|
+
name,
|
|
123
|
+
tag,
|
|
124
|
+
buildCmd: buildType(bp, name, tag, key === 'buildpack' && options.clearCache),
|
|
125
|
+
tagCmd: [['docker', 'tag', source, repoImg]].concat(checksum ? [['docker', 'tag', source, `${rawImg}:${checksum}`]] : []),
|
|
126
|
+
pushCmd: ['docker', 'push', repoImg],
|
|
127
|
+
image: rawImg,
|
|
128
|
+
checksum,
|
|
129
|
+
path: bp.buildpack?.path
|
|
130
|
+
}
|
|
131
|
+
}))
|
|
132
|
+
return { commands, before_all, repository }
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Executes the containerization commands for each module.
|
|
137
|
+
* @param {*} modules - The list of modules with their respective build, tag, and push commands.
|
|
138
|
+
* @param {*} options - Options object, e.g., { push: true } to push images after building.
|
|
139
|
+
*/
|
|
140
|
+
async function executeModules(modules, options) {
|
|
141
|
+
for (const m of modules) {
|
|
142
|
+
if (m.checksum) {
|
|
143
|
+
try {
|
|
144
|
+
run(`docker inspect --format='{{index .RepoTags 0}}' ${m.image}:${m.checksum}`, { silent: true })
|
|
145
|
+
console.log(info(`image ${m.image}:${m.checksum} already exists – skipping build and push`))
|
|
146
|
+
continue
|
|
147
|
+
} catch (error) {
|
|
148
|
+
// ignore
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
for (const cmd of m.buildCmd) run(cmd)
|
|
153
|
+
if (options.push) {
|
|
154
|
+
for (const cmd of m.tagCmd) run(cmd)
|
|
155
|
+
run(m.pushCmd)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Main function to containerize modules based on a YAML configuration file.
|
|
162
|
+
* @param {string} filename - The path to the YAML configuration file.
|
|
163
|
+
* @param {{ push: boolean, clearCache: boolean }} options - Options object, e.g., { push: true } to push images after building.
|
|
164
|
+
* @returns {Promise<Array<{
|
|
165
|
+
* name: string,
|
|
166
|
+
* tag: string,
|
|
167
|
+
* buildCmd: Array<string>,
|
|
168
|
+
* tagCmd: Array<string>,
|
|
169
|
+
* pushCmd: Array<string>,
|
|
170
|
+
* image: string,
|
|
171
|
+
* checksum: string|null
|
|
172
|
+
* path: string
|
|
173
|
+
* }>} - The list of modules that were containerized.
|
|
174
|
+
*/
|
|
175
|
+
async function containerize(filename = 'containerize.yaml', options = { clearCache: !!process.env.CLEAR_CACHE, push: true }) {
|
|
176
|
+
const { repository, before_all, commands: modules } = await parseYAML(filename, '', options)
|
|
177
|
+
if (!repository || !modules) return
|
|
178
|
+
for (const cmd of before_all) run(cmd)
|
|
179
|
+
await executeModules(modules, options)
|
|
180
|
+
return modules
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
module.exports = { containerize }
|
package/bin/version.js
CHANGED
|
@@ -52,7 +52,7 @@ function list_versions (args, options) {
|
|
|
52
52
|
|
|
53
53
|
process.stdout.isTTY && console.log()
|
|
54
54
|
if (options.markdown || options.info) {
|
|
55
|
-
_markdown (versions)
|
|
55
|
+
_markdown (versions, options)
|
|
56
56
|
} else {
|
|
57
57
|
const mark = !colors ? s => s : require('../lib/util/term').info
|
|
58
58
|
for (let each of Object.keys(versions)) console.log(`${mark(each)}: ${versions[each]}`)
|
|
@@ -81,7 +81,7 @@ function info({ root: cwd = process.cwd() }) {
|
|
|
81
81
|
|
|
82
82
|
// Sort entries, with @sap/cds packages on top, followed by @cap-js packages
|
|
83
83
|
const keys = Object.keys(versions).sort(), sorted={}
|
|
84
|
-
for (let k of ['@sap/cds'
|
|
84
|
+
for (let k of ['@sap/cds']) if (!(k in sorted)) sorted[k] = versions[k]
|
|
85
85
|
for (let k of keys) if (!(k in sorted) && k.startsWith('@sap/cds')) sorted[k] = versions[k]
|
|
86
86
|
for (let k of keys) if (!(k in sorted) && k.startsWith('@cap-js')) sorted[k] = versions[k]
|
|
87
87
|
for (let k of keys) if (!(k in sorted)) sorted[k] = versions[k]
|
|
@@ -115,9 +115,9 @@ function _versions_from(pkg_path, pkg_label) {
|
|
|
115
115
|
return versions
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
function _markdown (versions) {
|
|
118
|
+
function _markdown (versions, { root: cwd = process.cwd() }) {
|
|
119
119
|
const pkg = { name:'', repository:'', version:'' }; try {
|
|
120
|
-
Object.assign (pkg, require (resolve('package.json')))
|
|
120
|
+
Object.assign (pkg, require (require.resolve('./package.json', { paths:[cwd] })))
|
|
121
121
|
} catch (e) {/* ignored */}
|
|
122
122
|
const repo = pkg.repository.url || pkg.repository || 'https://github.com/<your/repo>'
|
|
123
123
|
const valLength = Math.max(repo.length, versions.home.length)
|
package/lib/bind/k8s.js
CHANGED
|
@@ -32,7 +32,7 @@ module.exports = new class K8SBind extends require('./plugin') {
|
|
|
32
32
|
|
|
33
33
|
services4(app) {
|
|
34
34
|
const { execSync } = require('node:child_process')
|
|
35
|
-
const json = cmd => JSON.parse(execSync(cmd, { encoding: 'utf8' }))
|
|
35
|
+
const json = cmd => JSON.parse(execSync(cmd, { encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }))
|
|
36
36
|
|
|
37
37
|
const bindings = json('kubectl get servicebindings -o json').items
|
|
38
38
|
.filter(b => b.metadata.name.startsWith(app))
|
|
@@ -179,7 +179,7 @@ class BuildTaskEngine {
|
|
|
179
179
|
|
|
180
180
|
/** @private */
|
|
181
181
|
_resolveHandlerResponse(results, pluginMessages = []) {
|
|
182
|
-
/** @type {BuildError[]}*/
|
|
182
|
+
/** @type {(BuildError | Error)[]}*/
|
|
183
183
|
const errors = []
|
|
184
184
|
const resolvedResults = results.reduce((acc, r) => {
|
|
185
185
|
if (r.status === 'fulfilled') {
|
|
@@ -195,7 +195,10 @@ class BuildTaskEngine {
|
|
|
195
195
|
const error = errors.find(e => e.constructor.name !== COMPILATION_ERROR)
|
|
196
196
|
if (error) {
|
|
197
197
|
// throw original BuildError including existing BuildMessages
|
|
198
|
-
|
|
198
|
+
if (!(error instanceof BuildError)) {
|
|
199
|
+
throw error
|
|
200
|
+
}
|
|
201
|
+
error.messages = error.messages?.map(m => typeof m === 'string' ? new BuildMessage(m, SEVERITY_ERROR) : m)
|
|
199
202
|
error.messages = this._sortMessagesUnique(pluginMessages.filter(m => m instanceof BuildMessage), error.messages)
|
|
200
203
|
throw error
|
|
201
204
|
}
|
package/lib/build/plugin.js
CHANGED
|
@@ -126,7 +126,7 @@ class Plugin {
|
|
|
126
126
|
* @abstract
|
|
127
127
|
* Called by the framework to create the artifacts of this build plugin.
|
|
128
128
|
*/
|
|
129
|
-
async build() { /* abstract */}
|
|
129
|
+
async build() { /* abstract */ }
|
|
130
130
|
|
|
131
131
|
/**
|
|
132
132
|
* Called by the framework immediately before 'build' to delete any output created by this build plugin.
|
|
@@ -23,7 +23,7 @@ copied from web-application: web-application.fullname
|
|
|
23
23
|
*/}}
|
|
24
24
|
{{- define "cap.deploymentHost" -}}
|
|
25
25
|
{{- if .deployment.expose.host }}
|
|
26
|
-
{{- .deployment.expose.host }}
|
|
26
|
+
{{- tpl .deployment.expose.host . }}
|
|
27
27
|
{{- else }}
|
|
28
28
|
{{- $name := (include "cap.web-application.fullname" .) }}
|
|
29
29
|
{{- if hasPrefix $name .Release.Namespace }}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const cds = require('../../../cds')
|
|
2
|
-
const yaml =
|
|
3
|
-
const { exists, path, readFileSync } = cds.utils
|
|
2
|
+
const { exists, path, readFileSync, yaml } = cds.utils
|
|
4
3
|
const { Plugin, BuildError } = require('../..')
|
|
5
4
|
const { join } = path
|
|
6
5
|
const { readProject } = require('../../../init/projectReader')
|
|
@@ -11,7 +10,7 @@ module.exports = class HelmBuildPlugin extends Plugin {
|
|
|
11
10
|
static hasTask() {
|
|
12
11
|
if (exists('chart/Chart.yaml')) {
|
|
13
12
|
const chartFile = readFileSync(join(cds.root, 'chart', 'Chart.yaml'), 'utf8')
|
|
14
|
-
const chartYaml = yaml.
|
|
13
|
+
const chartYaml = yaml.parse(chartFile)
|
|
15
14
|
return chartYaml.annotations?.["app.kubernetes.io/managed-by"] === 'cds-dk/helm'
|
|
16
15
|
}
|
|
17
16
|
return false
|
|
@@ -27,15 +26,16 @@ module.exports = class HelmBuildPlugin extends Plugin {
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
async build() {
|
|
30
|
-
if(!HelmBuildPlugin.hasTask()) {
|
|
31
|
-
this.pushMessage("Helm Chart not found. Execute `cds add
|
|
29
|
+
if (!HelmBuildPlugin.hasTask()) {
|
|
30
|
+
this.pushMessage("Helm Chart not found. Execute `cds add kyma` to add the helm chart.", Plugin.WARNING)
|
|
32
31
|
return
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
const
|
|
34
|
+
const project = readProject()
|
|
35
|
+
const { hasApprouter, hasConnectivity, hasHtml5Repo } = project
|
|
36
36
|
|
|
37
37
|
const chartFile = readFileSync(join(cds.root, 'chart', 'Chart.yaml'), 'utf8')
|
|
38
|
-
const chartYaml = yaml.
|
|
38
|
+
const chartYaml = yaml.parse(chartFile)
|
|
39
39
|
const isInternal = chartYaml.dependencies?.find(dep => dep.name === 'web-application')?.repository !== undefined
|
|
40
40
|
|
|
41
41
|
if (!isInternal) {
|
|
@@ -48,31 +48,23 @@ function _compileToHana(model, options, beforeImage) {
|
|
|
48
48
|
let migrations = []
|
|
49
49
|
let afterImage
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
definitions.push({ name, suffix, sql: content })
|
|
67
|
-
}
|
|
51
|
+
const result = cds.compile.to.hana(model, options, beforeImage);
|
|
52
|
+
|
|
53
|
+
for (const [content, { file }] of result) {
|
|
54
|
+
switch (file) {
|
|
55
|
+
case 'deletions.json':
|
|
56
|
+
break
|
|
57
|
+
case 'migrations.json':
|
|
58
|
+
migrations = content
|
|
59
|
+
break
|
|
60
|
+
case 'afterImage.json':
|
|
61
|
+
afterImage = content
|
|
62
|
+
break
|
|
63
|
+
default: {
|
|
64
|
+
const { name, ext: suffix } = path.parse(file)
|
|
65
|
+
definitions.push({ name, suffix, sql: content })
|
|
68
66
|
}
|
|
69
67
|
}
|
|
70
|
-
} else {
|
|
71
|
-
// compatibility with cds < 8.0.0
|
|
72
|
-
const result = cdsc.to.hdi.migration(cds.minify(model), options, beforeImage)
|
|
73
|
-
definitions = result.definitions
|
|
74
|
-
migrations = result.migrations
|
|
75
|
-
afterImage = result.afterImage
|
|
76
68
|
}
|
|
77
69
|
let migrationTableNames = new Set()
|
|
78
70
|
if (afterImage) {
|
|
@@ -97,7 +89,7 @@ function _compileToHana(model, options, beforeImage) {
|
|
|
97
89
|
* @param {String} srcPath Fully qualified path of the directory containing .hdbmigrationtable files
|
|
98
90
|
* @param {String} migration the migration descriptor
|
|
99
91
|
* @param {String} tableSql SQL TABLE definition
|
|
100
|
-
* @returns {Object} Providing access to 'content' and 'fileName'.
|
|
92
|
+
* @returns {Promise<Object>} Providing access to 'content' and 'fileName'.
|
|
101
93
|
*/
|
|
102
94
|
async function _2migrationtable(srcPath, migration, tableSql) {
|
|
103
95
|
let migrationTableModel = null
|
|
@@ -21,6 +21,10 @@ const FILE_NAME_UNDEPLOY_JSON = "undeploy.json"
|
|
|
21
21
|
const DEPLOY_FORMAT = "deploy-format"
|
|
22
22
|
const DEBUG = cds.debug('cli|build')
|
|
23
23
|
|
|
24
|
+
// Some HDI artifacts do not require explicit plugin specification in _.hdiconfig_.
|
|
25
|
+
// This list contains exceptions which will be ignored during the consistency check.
|
|
26
|
+
const PLUGINLESS_HDB_FILES = new Set([".hdbgrants", ".hdbrevokes"])
|
|
27
|
+
|
|
24
28
|
class HanaBuildPlugin extends InternalBuildPlugin {
|
|
25
29
|
init() {
|
|
26
30
|
if (this.hasBuildOption(OUTPUT_MODE, OUTPUT_MODE_RESULT)) {
|
|
@@ -473,8 +477,12 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
473
477
|
}
|
|
474
478
|
}
|
|
475
479
|
if (Object.keys(content['file_suffixes']).length < hdiPlugins.size) {
|
|
476
|
-
const missingPlugins = Array.from(hdiPlugins)
|
|
477
|
-
|
|
480
|
+
const missingPlugins = Array.from(hdiPlugins)
|
|
481
|
+
.filter(key => !content['file_suffixes'][key.slice(1)])
|
|
482
|
+
.filter(key => !PLUGINLESS_HDB_FILES.has(key))
|
|
483
|
+
if (missingPlugins.length) {
|
|
484
|
+
this.pushMessage(`'HANA database plugin not found for following file suffix [${missingPlugins.join(', ')}]`)
|
|
485
|
+
}
|
|
478
486
|
}
|
|
479
487
|
await this.write(content).to(hdiConfigPath)
|
|
480
488
|
}
|
|
@@ -578,7 +586,7 @@ class HanaBuildPlugin extends InternalBuildPlugin {
|
|
|
578
586
|
/**
|
|
579
587
|
* Returns all hana native artifacts located in db/src/** workspace folders.
|
|
580
588
|
* @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,
|
|
589
|
+
* @returns {Promise<Array>} Array of workspace files, each entry contains the db src directory,
|
|
582
590
|
* the relative file path and the file name.
|
|
583
591
|
*/
|
|
584
592
|
async _getWorkspaceFiles(workspacePaths) {
|
|
@@ -167,6 +167,12 @@
|
|
|
167
167
|
},
|
|
168
168
|
"hdbschedulerjob" : {
|
|
169
169
|
"plugin_name" : "com.sap.hana.di.schedulerjob"
|
|
170
|
+
},
|
|
171
|
+
"hdblogicalschema" : {
|
|
172
|
+
"plugin_name" : "com.sap.hana.di.logicalschema"
|
|
173
|
+
},
|
|
174
|
+
"hdblogicalschemaconfig" : {
|
|
175
|
+
"plugin_name" : "com.sap.hana.di.logicalschema.config"
|
|
170
176
|
}
|
|
171
177
|
}
|
|
172
|
-
}
|
|
178
|
+
}
|
|
@@ -4,6 +4,7 @@ const cds = require('../../cds')
|
|
|
4
4
|
const Plugin = require('../plugin')
|
|
5
5
|
const { hasOptionValue, getFiles } = require('../util')
|
|
6
6
|
const { FOLDER_GEN, DEFAULT_BUILTIN_PRIORITY_MAX } = require('../constants')
|
|
7
|
+
const TRACE = cds.debug('trace')
|
|
7
8
|
|
|
8
9
|
class InternalBuildPlugin extends Plugin {
|
|
9
10
|
/**
|
|
@@ -116,7 +117,7 @@ class InternalBuildPlugin extends Plugin {
|
|
|
116
117
|
cwd: cds.root,
|
|
117
118
|
src: this.hasBuildOption('flatDeployLayout', true) || this.task.src === this.task.dest ? this.task.src : cds.root
|
|
118
119
|
}
|
|
119
|
-
const csnStr =
|
|
120
|
+
const csnStr = cds_compile_to_json(model, jsonOptions)
|
|
120
121
|
await this.write(csnStr).to(csnFile)
|
|
121
122
|
return csnStr
|
|
122
123
|
}
|
|
@@ -156,4 +157,17 @@ class InternalBuildPlugin extends Plugin {
|
|
|
156
157
|
return null
|
|
157
158
|
}
|
|
158
159
|
}
|
|
160
|
+
|
|
161
|
+
// Emits the 'compile.for.runtime' event, as the produced CSN is for runtime usage.
|
|
162
|
+
// Plugins use it to modify the CSN before it is stringified.
|
|
163
|
+
function cds_compile_to_json (csn,o) {
|
|
164
|
+
TRACE?.time('cds.compile 2json'.padEnd(22)); try {
|
|
165
|
+
let result, next = ()=> result ??= cds.compile.to.json (csn,o)
|
|
166
|
+
cds.emit ('compile.for.runtime', csn, o, next)
|
|
167
|
+
return next() //> in case no handler called next
|
|
168
|
+
}
|
|
169
|
+
finally { TRACE?.timeEnd('cds.compile 2json'.padEnd(22)) }
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
159
173
|
module.exports = InternalBuildPlugin
|
|
@@ -7,6 +7,7 @@ const { OUTPUT_MODE, OUTPUT_MODE_RESULT, FILE_EXT_CDS, CONTENT_LANGUAGE_BUNDLES,
|
|
|
7
7
|
const { getI18nDefaultFolder } = require('../../util')
|
|
8
8
|
const DEFAULT_COMPILE_DEST_FOLDER = path.normalize('src/main/resources/edmx')
|
|
9
9
|
const DEBUG = cds.debug('cli|build')
|
|
10
|
+
const LOG = cds.log('cli|build')
|
|
10
11
|
|
|
11
12
|
class JavaBuildPlugin extends EdmxBuildPlugin {
|
|
12
13
|
init() {
|
|
@@ -25,7 +26,17 @@ class JavaBuildPlugin extends EdmxBuildPlugin {
|
|
|
25
26
|
|
|
26
27
|
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
27
28
|
// generate edmx files containing all features - localized edmx for backward compatibility
|
|
28
|
-
|
|
29
|
+
let localized = this.hasBuildOption(CONTENT_LOCALIZED_EDMX, true)
|
|
30
|
+
|
|
31
|
+
// Force localized to true for OData v2
|
|
32
|
+
if (cds.env.odata.version === 'v2') {
|
|
33
|
+
if (!localized) {
|
|
34
|
+
LOG.info('Forcing localized EDMX generation for OData v2')
|
|
35
|
+
localized = true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (localized) {
|
|
29
40
|
await this.compileToEdmx(model, compileDest, { [FLAVOR_LOCALIZED_EDMX]: true })
|
|
30
41
|
}
|
|
31
42
|
// non-localized edmx
|
|
@@ -86,7 +86,8 @@ class MtxSidecarBuildPlugin extends NodejsBuildPlugin {
|
|
|
86
86
|
return
|
|
87
87
|
}
|
|
88
88
|
const { dictionary, sources } = await this.compileAll(model, destMainSrv, destMain)
|
|
89
|
-
await this.collectAllLanguageBundles(dictionary, sources, destMainSrv, destMain)
|
|
89
|
+
// await this.collectAllLanguageBundles(dictionary, sources, destMainSrv, destMain)
|
|
90
|
+
await this.collectAllLanguageBundles(dictionary, sources, destMain, destMain)
|
|
90
91
|
|
|
91
92
|
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
92
93
|
//inferred model expected by cds.compile.to.edmx()
|
|
@@ -53,7 +53,8 @@ class NodejsBuildPlugin extends EdmxBuildPlugin {
|
|
|
53
53
|
if (model) {
|
|
54
54
|
const { dictionary, sources } = await this.compileAll(model, destSrv, destRoot)
|
|
55
55
|
// collect and write language bundles into single i18n.json file
|
|
56
|
-
await this.collectAllLanguageBundles(dictionary, sources,
|
|
56
|
+
await this.collectAllLanguageBundles(dictionary, sources, destRoot, destRoot)
|
|
57
|
+
// await this.collectAllLanguageBundles(dictionary, sources, destSrv, destRoot)
|
|
57
58
|
|
|
58
59
|
if (!this.hasBuildOption(CONTENT_EDMX, false)) {
|
|
59
60
|
const compileOptions = { [FLAVOR_LOCALIZED_EDMX]: this.hasBuildOption(FLAVOR_LOCALIZED_EDMX, true) }
|
package/lib/build/util.js
CHANGED
|
@@ -165,9 +165,9 @@ function flatten(modelPaths) {
|
|
|
165
165
|
|
|
166
166
|
function getI18nDefaultFolder() {
|
|
167
167
|
// keep '_i18n' as preferred folder for compatibility reasons
|
|
168
|
-
if (cds.env.i18n.folders.includes('_i18n')) return '_i18n'
|
|
169
|
-
if (cds.env.i18n.folders.includes('i18n')) return 'i18n'
|
|
170
|
-
return cds.env.i18n.folders[0] ?? '
|
|
168
|
+
// if (cds.env.i18n.folders.includes('_i18n')) return '_i18n'
|
|
169
|
+
// if (cds.env.i18n.folders.includes('i18n')) return 'i18n'
|
|
170
|
+
return cds.env.i18n.folders?.[0] ?? '_i18n'
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
async function pathExists(path) {
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
|
-
const { compile } = cds._local('@cap-js/openapi') // load a local cap-js/openapi (fallback to our own)
|
|
3
|
-
|
|
4
|
-
function cds_compile_to_openapi (csn, options) {
|
|
5
|
-
return compile(csn, options)
|
|
6
|
-
}
|
|
2
|
+
const { compile: cds_compile_to_openapi } = cds._local('@cap-js/openapi') // load a local cap-js/openapi (fallback to our own)
|
|
7
3
|
|
|
8
4
|
module.exports = cds_compile_to_openapi
|
package/lib/import/common.js
CHANGED
|
@@ -336,12 +336,12 @@ function _printUsageHint(dest, service, cwd) {
|
|
|
336
336
|
dest = dest.replace(extension, '');
|
|
337
337
|
let using
|
|
338
338
|
if (service.length > 1){
|
|
339
|
-
using = `using { ${service.join(', ')}} from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}'
|
|
339
|
+
using = `using { ${service.join(', ')}} from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}';`;
|
|
340
340
|
} else {
|
|
341
|
-
using = `using { ${service} as external } from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}'
|
|
341
|
+
using = `using { ${service} as external } from './${path.relative(cds.env.folders.srv, dest).replace(/\\/g, '/')}';`;
|
|
342
342
|
}
|
|
343
343
|
const message = `\n[cds] - imported API to ${path.relative(cwd, dest)}
|
|
344
|
-
> use it in your CDS models through the
|
|
344
|
+
> use it in your CDS models through the likes of:
|
|
345
345
|
|
|
346
346
|
${require('../util/term').info(using)}
|
|
347
347
|
`;
|
|
@@ -510,7 +510,8 @@ async function postProcess(filePath, options, csn, cwd) {
|
|
|
510
510
|
usingStatements.push(`using from './${path.basename(csnFile.replace(`'`, `''`))}';`);
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
-
|
|
513
|
+
if (!options.dry)
|
|
514
|
+
_printUsageHint(csnFile, services, cwd);
|
|
514
515
|
|
|
515
516
|
if (!options.dry && !options.no_copy)
|
|
516
517
|
await _copyToSrvExternal(filePath, destFilePath, cwd);
|
package/lib/import/message.js
CHANGED
|
@@ -12,7 +12,7 @@ const messages = {
|
|
|
12
12
|
FILE_MODIFIED: 'The file with the same name exists and is been modified. Use --force flag to forcefully overwrite the file.',
|
|
13
13
|
OUTPUT_FILE_MISMATCH: 'Specify the file extension that --as option supports.',
|
|
14
14
|
INVALID_AS_OPTION: 'For --as option, please specify one of these output file format [cds, csn, json].',
|
|
15
|
-
UNSUPPORTED_FROM_OPTION:
|
|
15
|
+
UNSUPPORTED_FROM_OPTION: `No importer is installed for type '{0}'. Known import kinds are: {1}. Make sure the required importer plugin is installed.`,
|
|
16
16
|
INCORRECT_EXTENSION: 'If --as option is not provided, then please provide either .json or .csn output file extension.',
|
|
17
17
|
INCORRECT_INPUT_NAMESPACES: 'Invalid value for --include-namespaces option. Specify namespaces as comma-separated list or "*"',
|
|
18
18
|
MISSING_ASSOCIATION: 'Association is missing in the input file provided. Please check the edmx file.',
|
|
@@ -38,6 +38,7 @@ module.exports = class ParserContext {
|
|
|
38
38
|
this.schemaAliasToNamespace = {};
|
|
39
39
|
this.schemaToSchemaDataIndex = {};
|
|
40
40
|
this.schemaToPrimarySchema = {};
|
|
41
|
+
this.schemaDetails = {};
|
|
41
42
|
this.vocabNamespaceToAlias = {};
|
|
42
43
|
this.entityToBoundActions = {};
|
|
43
44
|
this.entityToBoundFunctions = {};
|