@sap/cds-dk 9.3.0 → 9.3.2
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 +23 -0
- package/bin/up.js +10 -29
- package/lib/import/odata/metadataconverter/MetadataConverter.js +5 -0
- package/lib/import/odata/v2parser.js +4 -0
- package/lib/init/constants.js +1 -1
- package/lib/init/projectReader.js +15 -1
- package/lib/init/template/data/as-csv.js +10 -7
- package/lib/init/template/data/as-json.js +7 -4
- package/lib/init/template/enterprise-messaging/index.js +3 -2
- package/lib/init/template/enterprise-messaging-shared/index.js +3 -2
- package/lib/init/template/github-actions/files/kyma-setup/action.yaml +1 -9
- package/lib/init/template/github-actions/files/workflows/cf.yaml.hbs +16 -6
- package/lib/init/template/github-actions/files/workflows/kyma.yaml.hbs +2 -2
- package/lib/init/template/github-actions/files/workflows/release.yaml.hbs +30 -0
- package/lib/init/template/github-actions/files/workflows/test.yaml.hbs +1 -1
- package/lib/init/template/github-actions/index.js +21 -8
- package/lib/init/template/html5-repo/files/mta.yaml.hbs +2 -0
- package/lib/init/template/ias/files/mta.yaml.hbs +1 -1
- package/lib/init/template/mta/files/mta.yaml.hbs +1 -1
- package/lib/init/template/multitenancy/files/mta.yaml.hbs +1 -1
- package/lib/init/template/multitenancy/files/values.yaml.hbs +1 -1
- package/lib/init/template/xsuaa/files/mta.yaml.hbs +2 -1
- package/lib/init/template/xsuaa/index.js +7 -5
- package/lib/lint/eslintHelp.json +1 -1
- package/npm-shrinkwrap.json +18 -18
- package/package.json +1 -1
- package/lib/init/template/github-actions/files/github-deployment/action.yaml +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,28 @@ 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.2 - 2025-09-16
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `cds add html5-repo` avoids some superfluous configuration combination with `cds add portal`.
|
|
14
|
+
- `cds import` correctly imports OData v2 EDMX files with `Edm.Time` properties having a precision.
|
|
15
|
+
|
|
16
|
+
## Version 9.3.1 - 2025-09-08
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- `cds add ias` correctly sets the `subscription-manager` dependencies endpoint for Node.js.
|
|
21
|
+
- `cds add data --records` respects the max length of a string field when it is annotated with `@Communication.IsEmailAddress`
|
|
22
|
+
- `cds add data --records --format csv` correctly escaping complex properties nested within structs.
|
|
23
|
+
- `cds add data --records` correctly generates values for structs in cases when the entity and the struct, used by the entity, have both a property with the same name.
|
|
24
|
+
- `cds add html5-repo` also binds the `html5-repo-host` service to the approuter.
|
|
25
|
+
- `cds up` has improved support for setups with multiple microservices.
|
|
26
|
+
- `cds add mta` will add no `role-collections` parameters if there are some specified in `xs-security.json`.
|
|
27
|
+
- `cds add github-actions` correctly generates a release workflow.
|
|
28
|
+
- `cds add github-actions` uses a simplified and more resilient Kyma setup script.
|
|
29
|
+
- `cds import` now correctly imports EDMX files with empty NavigationPropertyPath tags.
|
|
30
|
+
|
|
9
31
|
## Version 9.3.0 - 2025-09-01
|
|
10
32
|
|
|
11
33
|
### Added
|
|
@@ -23,6 +45,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
23
45
|
|
|
24
46
|
### Fixed
|
|
25
47
|
|
|
48
|
+
- `cds add workzone` with missing `sap.app` config in `manifest.json` does not throw a `TypeError`.
|
|
26
49
|
- `cds add ias` adds a subdomain-less application URL in `redirect_uris`.
|
|
27
50
|
|
|
28
51
|
## Version 9.2.1 - 2025-08-22
|
package/bin/up.js
CHANGED
|
@@ -27,9 +27,9 @@ module.exports = Object.assign(up, {
|
|
|
27
27
|
*cds up --to k8s --namespace e2e-tests*
|
|
28
28
|
`})
|
|
29
29
|
|
|
30
|
-
const { execSync
|
|
30
|
+
const { execSync } = require('node:child_process')
|
|
31
31
|
const cds = require('../lib/cds')
|
|
32
|
-
const { highlight
|
|
32
|
+
const { highlight } = require('../lib/util/term')
|
|
33
33
|
const { exists, fs, path, path: { join } } = cds.utils
|
|
34
34
|
const { readProject } = require('../lib/init/projectReader')
|
|
35
35
|
|
|
@@ -45,29 +45,9 @@ const run = cmd => {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
const runDetectingError = async (cmd, errorRegex) => {
|
|
49
|
-
console.log(highlight(cmd))
|
|
50
|
-
return new Promise((resolve, reject) => {
|
|
51
|
-
const child = spawn(cmd, { shell: true, stdio: 'pipe' })
|
|
52
|
-
let out = '';
|
|
53
|
-
['stdout', 'stderr'].forEach(key => {
|
|
54
|
-
child[key].on('data', data => {
|
|
55
|
-
process[key].write(data.toString())
|
|
56
|
-
out += data.toString()
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
child.on('close', () => {
|
|
60
|
-
const error = out.match(errorRegex)
|
|
61
|
-
if (error) {
|
|
62
|
-
return reject('Error in command: ' + bold(cmd) + ' → exiting')
|
|
63
|
-
}
|
|
64
|
-
resolve(out)
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
48
|
async function up() {
|
|
70
|
-
const
|
|
49
|
+
const project = readProject()
|
|
50
|
+
const { apps, appName, hasApprouter, hasXsuaa, isMonorepoMicroservice } = project
|
|
71
51
|
|
|
72
52
|
for (const { app } of apps) {
|
|
73
53
|
const appPath = path.join(cds.root, cds.env.folders.app, app)
|
|
@@ -104,11 +84,12 @@ async function up() {
|
|
|
104
84
|
run('npm i --package-lock-only')
|
|
105
85
|
}
|
|
106
86
|
}
|
|
107
|
-
|
|
108
|
-
if (
|
|
109
|
-
const
|
|
87
|
+
|
|
88
|
+
if (isMonorepoMicroservice) {
|
|
89
|
+
const { npmWorkspaceRoot } = project
|
|
90
|
+
const source = path.join(npmWorkspaceRoot, 'package-lock.json')
|
|
110
91
|
const target = path.join(process.cwd(), 'package-lock.json')
|
|
111
|
-
if (exists(target)
|
|
92
|
+
if (exists(target) && fs.lstatSync(target).isSymbolicLink()) fs.unlinkSync(target)
|
|
112
93
|
const relative = path.relative(process.cwd(), source)
|
|
113
94
|
fs.symlinkSync(relative, target, 'junction')
|
|
114
95
|
}
|
|
@@ -119,7 +100,7 @@ async function up() {
|
|
|
119
100
|
const nsKubectl = ns ? `-n ${ns}` : ''
|
|
120
101
|
|
|
121
102
|
run('npm i --package-lock')
|
|
122
|
-
|
|
103
|
+
run('ctz containerize.yaml --log --push')
|
|
123
104
|
let helmUpgrade = `helm upgrade --install ${appName} ./gen/chart ${nsHelm} --wait --wait-for-jobs --timeout=10m`
|
|
124
105
|
if (hasXsuaa) helmUpgrade += ' --set-file xsuaa.jsonParameters=xs-security.json'
|
|
125
106
|
run(helmUpgrade)
|
|
@@ -1620,6 +1620,11 @@ class PropertyPathExpression extends Expression {
|
|
|
1620
1620
|
const targetOption = this.getContext().getConverterOptions().target;
|
|
1621
1621
|
if (targetOption === MetadataConverter.TARGETS.LIBRARY) {
|
|
1622
1622
|
target = { [this._targetName]: this.getElement().elements[0].text };
|
|
1623
|
+
} else if (!this.getElement().elements && this._targetName === '$NavigationPropertyPath') {
|
|
1624
|
+
//elements are not given, when <NavigationPropertyPath/> is in EDMX, which is used to refresh the current context
|
|
1625
|
+
return {
|
|
1626
|
+
"=": "($self)"
|
|
1627
|
+
};
|
|
1623
1628
|
} else {
|
|
1624
1629
|
target = this.getElement().elements[0].text;
|
|
1625
1630
|
}
|
|
@@ -453,6 +453,10 @@ function _getServiceEntityProperty(
|
|
|
453
453
|
if (cdsDataType === undefined) {
|
|
454
454
|
cdsDataType = edmxncdsdatatype[dataType + "_a"];
|
|
455
455
|
hasInvalidPrecision = true;
|
|
456
|
+
// Additional fallback for Edm.Time with precision
|
|
457
|
+
if (cdsDataType === undefined && dataType === 'Edm.Time') {
|
|
458
|
+
cdsDataType = edmxncdsdatatype[dataType];
|
|
459
|
+
}
|
|
456
460
|
}
|
|
457
461
|
} else if (displayFormat && displayFormat === "Date") {
|
|
458
462
|
cdsDataType = edmxncdsdatatype[dataType + "_f"];
|
package/lib/init/constants.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const cds = require('../cds')
|
|
2
|
-
const { join, resolve, basename } = require('path')
|
|
2
|
+
const { join, resolve, basename } = require('node:path')
|
|
3
|
+
const { execSync } = require('node:child_process')
|
|
3
4
|
const { fs, exists, isfile, isdir, path } = cds.utils
|
|
4
5
|
const { parseXml } = require('./xml')
|
|
5
6
|
const { REGEX_JAVA_VERSION, JAVA_LTS_VERSIONS } = require('./constants')
|
|
@@ -9,6 +10,7 @@ module.exports = new class ProjectReader {
|
|
|
9
10
|
|
|
10
11
|
constructor() {
|
|
11
12
|
this.readProject = this.readProject.bind(this)
|
|
13
|
+
this.cachedWorkspaceRoot = null
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
#setPackageInfo() {
|
|
@@ -105,6 +107,9 @@ module.exports = new class ProjectReader {
|
|
|
105
107
|
* @property {boolean} hasDynatrace Indicates if the project uses Dynatrace.
|
|
106
108
|
* @property {boolean} hasCloudLogging Indicates if the project uses Cloud Logging.
|
|
107
109
|
* @property {String} extendsApp Indicates which project is extended if any.
|
|
110
|
+
* @property {String} githubSlug GitHub org/repo.
|
|
111
|
+
* @property {boolean} isMonorepoMicroservice Indicates if the project is a monorepo microservice.
|
|
112
|
+
* @property {String} npmWorkspaceRoot The root path of the monorepo workspace.
|
|
108
113
|
*/
|
|
109
114
|
|
|
110
115
|
/**
|
|
@@ -198,7 +203,16 @@ module.exports = new class ProjectReader {
|
|
|
198
203
|
sap: () => cds.cli.options.sap,
|
|
199
204
|
imageRegistry: () => exists('chart/values.yaml') ? cds.load.yaml(join(cds.root, 'chart/values.yaml')).global?.image?.registry ?? '<your-container-registry-server>' : '<your-container-registry-server>',
|
|
200
205
|
extendsApp: _extends,
|
|
206
|
+
npmWorkspaceRoot: () => {
|
|
207
|
+
if (this.cachedWorkspaceRoot) return this.cachedWorkspaceRoot
|
|
208
|
+
this.cachedWorkspaceRoot = execSync('npm prefix', { encoding: 'utf8', cwd: cds.root, stdio: 'pipe' }).trim()
|
|
209
|
+
return this.cachedWorkspaceRoot
|
|
210
|
+
},
|
|
211
|
+
isMonorepoMicroservice: function () {
|
|
212
|
+
return this.npmWorkspaceRoot() !== cds.root
|
|
213
|
+
},
|
|
201
214
|
hasRoles: () => {}, roles: () => {}, // for xs-security generation
|
|
215
|
+
hasCustomRoleCollections: () => exists('xs-security.json') && !!JSON.parse(fs.readFileSync(path.join(cds.root, 'xs-security.json')))['role-collections']
|
|
202
216
|
}
|
|
203
217
|
|
|
204
218
|
// Automatically creates availability checks for `cds add` commands.
|
|
@@ -48,11 +48,7 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
|
|
|
48
48
|
}
|
|
49
49
|
else {
|
|
50
50
|
if (i === 0) header.push(name)
|
|
51
|
-
|
|
52
|
-
value = JSON.stringify(value)
|
|
53
|
-
}
|
|
54
|
-
value = escape(value)
|
|
55
|
-
record.push(value)
|
|
51
|
+
record.push(processValue(value))
|
|
56
52
|
}
|
|
57
53
|
}
|
|
58
54
|
records.push(record.join(separator))
|
|
@@ -71,16 +67,23 @@ module.exports = async (data={}, csn, { headerOnly=false, separator=',' }={}) =>
|
|
|
71
67
|
return value
|
|
72
68
|
}
|
|
73
69
|
|
|
70
|
+
function processValue(v) {
|
|
71
|
+
if (Array.isArray(v) || typeof v === 'object') { // `array of`/`many` and `cds.Map`
|
|
72
|
+
return escape(JSON.stringify(v))
|
|
73
|
+
}
|
|
74
|
+
return escape(v)
|
|
75
|
+
}
|
|
76
|
+
|
|
74
77
|
function resolveStruct(v, prefix) {
|
|
75
78
|
return Object.entries(v).reduce((acc, [key, val]) => {
|
|
76
79
|
//structs can be within structs
|
|
77
|
-
if (typeof val === 'object') {
|
|
80
|
+
if (typeof val === 'object' && !Array.isArray(val)) {
|
|
78
81
|
const {headers, records} = resolveStruct(val, prefix + '_' + key);
|
|
79
82
|
acc.headers = acc.headers.concat(headers);
|
|
80
83
|
acc.records = acc.records.concat(records);
|
|
81
84
|
} else {
|
|
82
85
|
acc.headers.push(prefix + '_' + key);
|
|
83
|
-
acc.records.push(val);
|
|
86
|
+
acc.records.push(processValue(val));
|
|
84
87
|
}
|
|
85
88
|
return acc;
|
|
86
89
|
}, {headers: [], records: []});
|
|
@@ -125,8 +125,9 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
|
|
|
125
125
|
|
|
126
126
|
if (element.is_struct) {
|
|
127
127
|
let struct = {}
|
|
128
|
+
Object.defineProperty(struct, '$id', {value: record['$id'], enumerable: false})
|
|
128
129
|
for (const el of element.elements) {
|
|
129
|
-
struct[el.name] = dataForField(el, artifact, csn,
|
|
130
|
+
struct[el.name] = dataForField(el, artifact, csn, struct, data, null, depth+1, referenceData)
|
|
130
131
|
}
|
|
131
132
|
return struct
|
|
132
133
|
}
|
|
@@ -176,7 +177,7 @@ function dataForField(element, artifact, csn, record, data, parent, depth, refer
|
|
|
176
177
|
|| element['@Communication.IsEmailAddress']
|
|
177
178
|
|| element['@cds.on.insert']?.['='] === '$user'
|
|
178
179
|
|| element['@cds.on.update']?.['='] === '$user') {
|
|
179
|
-
return randomEmailAddress(element
|
|
180
|
+
return randomEmailAddress(element, record['$id'], csn)
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
let str = `${element.name}-${record['$id']}`
|
|
@@ -450,11 +451,13 @@ function randomFromList(list) {
|
|
|
450
451
|
return list[randomNumber() % list.length]
|
|
451
452
|
}
|
|
452
453
|
|
|
453
|
-
function randomEmailAddress(
|
|
454
|
+
function randomEmailAddress(element, num, csn) {
|
|
454
455
|
let addr = fakerValue('faker.internet.email', csn, false)
|
|
455
456
|
if (addr) return addr
|
|
456
457
|
const provider = randomFromList(['example.com', 'example.org', 'example.net'])
|
|
457
|
-
|
|
458
|
+
let localPart = `${element.name}.${num.toString(36)}`
|
|
459
|
+
// -1 in substring for @
|
|
460
|
+
return (element.length ? localPart.substring(0, element.length - 1 - provider.length) : localPart) + `@${provider}`
|
|
458
461
|
}
|
|
459
462
|
|
|
460
463
|
function randomStrForRegex(regexStr, csn) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require('../../../cds')
|
|
2
|
-
const { read, yaml: YAML } = cds.utils
|
|
2
|
+
const { read, exists, yaml: YAML } = cds.utils
|
|
3
3
|
const { readProject } = require('../../projectReader')
|
|
4
4
|
const { merge } = require('../../merge')
|
|
5
5
|
const { srv4, enterpriseMessaging, xsuaa } = require('../../registries/mta')
|
|
@@ -37,7 +37,6 @@ module.exports = class EnterpriseMessagingTemplate extends require('../../plugin
|
|
|
37
37
|
project.cloudevents = cds.cli.options.cloudevents
|
|
38
38
|
await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
|
|
39
39
|
}
|
|
40
|
-
await merge(__dirname, 'files/event-mesh.json.hbs').into('event-mesh.json', { project })
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
async combine() {
|
|
@@ -76,5 +75,7 @@ module.exports = class EnterpriseMessagingTemplate extends require('../../plugin
|
|
|
76
75
|
if (hasHelm || hasHelmUnifiedRuntime) {
|
|
77
76
|
await merge(__dirname, 'files', 'values.yaml').into('chart/values.yaml')
|
|
78
77
|
}
|
|
78
|
+
|
|
79
|
+
if (!exists('event-mesh.json')) await merge(__dirname, 'files/event-mesh.json.hbs').into('event-mesh.json', { project })
|
|
79
80
|
}
|
|
80
81
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const cds = require('../../../cds')
|
|
2
|
-
const { read } = cds.utils
|
|
2
|
+
const { read, exists } = cds.utils
|
|
3
3
|
const { readProject } = require('../../projectReader')
|
|
4
4
|
const { merge } = require('../../merge')
|
|
5
5
|
const { srv4, enterpriseMessaging } = require('../../registries/mta')
|
|
@@ -38,7 +38,6 @@ module.exports = class EnterpriseMessagingSharedTemplate extends require('../../
|
|
|
38
38
|
project.cloudevents = cds.cli.options.cloudevents
|
|
39
39
|
await merge(__dirname, 'files/package.json.hbs').into(configFile, { with: project })
|
|
40
40
|
}
|
|
41
|
-
await merge(__dirname, 'files/event-mesh.json.hbs').into('event-mesh.json', { project })
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
async combine() {
|
|
@@ -60,5 +59,7 @@ module.exports = class EnterpriseMessagingSharedTemplate extends require('../../
|
|
|
60
59
|
if (hasHelm || hasHelmUnifiedRuntime) {
|
|
61
60
|
await merge(__dirname, 'files/values.yaml').into('chart/values.yaml')
|
|
62
61
|
}
|
|
62
|
+
|
|
63
|
+
if (!exists('event-mesh.json')) await merge(__dirname, 'files/event-mesh.json.hbs').into('event-mesh.json', { project })
|
|
63
64
|
}
|
|
64
65
|
}
|
|
@@ -43,15 +43,7 @@ runs:
|
|
|
43
43
|
shell: bash
|
|
44
44
|
- run: apt-get install kubectl -y
|
|
45
45
|
shell: bash
|
|
46
|
-
- run: curl https://
|
|
47
|
-
shell: bash
|
|
48
|
-
- run: sudo apt-get install apt-transport-https --yes
|
|
49
|
-
shell: bash
|
|
50
|
-
- run: echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
|
|
51
|
-
shell: bash
|
|
52
|
-
- run: sudo apt-get update
|
|
53
|
-
shell: bash
|
|
54
|
-
- run: sudo apt-get install helm
|
|
46
|
+
- run: curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
|
55
47
|
shell: bash
|
|
56
48
|
- run: npm install -g ctz
|
|
57
49
|
shell: bash
|
|
@@ -2,6 +2,10 @@ name: Cloud Foundry
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
workflow_call:
|
|
5
|
+
inputs:
|
|
6
|
+
environment:
|
|
7
|
+
default: Staging
|
|
8
|
+
type: string
|
|
5
9
|
workflow_dispatch:
|
|
6
10
|
push:
|
|
7
11
|
branches:
|
|
@@ -22,10 +26,10 @@ jobs:
|
|
|
22
26
|
runs-on: {{#sap}}[self-hosted, solinas]{{/sap}}{{^sap}}ubuntu-latest{{/sap}}
|
|
23
27
|
steps:
|
|
24
28
|
- uses: actions/checkout@v5
|
|
25
|
-
- uses: actions/setup-node@
|
|
29
|
+
- uses: actions/setup-node@v5
|
|
26
30
|
with:
|
|
27
31
|
node-version: 22
|
|
28
|
-
- uses: ./.github/actions/cf-setup
|
|
32
|
+
- uses: ./.github/actions/cf-setup
|
|
29
33
|
with:
|
|
30
34
|
cf-api: {{#literal}}${{ vars.CF_API }}{{/literal}}
|
|
31
35
|
cf-org: {{#literal}}${{ vars.CF_ORG }}{{/literal}}
|
|
@@ -35,10 +39,6 @@ jobs:
|
|
|
35
39
|
- run: npm install
|
|
36
40
|
- run: npx cds up
|
|
37
41
|
|
|
38
|
-
- uses: ./.github/actions/github-deployment
|
|
39
|
-
with:
|
|
40
|
-
app-name: {{#literal}}${{ env.APP_NAME }}{{/literal}}
|
|
41
|
-
|
|
42
42
|
{{#hasApprouter}}
|
|
43
43
|
- run: cf logs {{#literal}}"${{ env.APP_NAME }}"{{/literal}} --recent
|
|
44
44
|
if: always()
|
|
@@ -53,3 +53,13 @@ jobs:
|
|
|
53
53
|
- run: cf logs {{#literal}}"${{ env.APP_NAME }}-mtx"{{/literal}} --recent
|
|
54
54
|
if: always()
|
|
55
55
|
{{/hasMultitenancy}}
|
|
56
|
+
|
|
57
|
+
- name: Get application URL
|
|
58
|
+
id: route
|
|
59
|
+
shell: bash
|
|
60
|
+
run: |
|
|
61
|
+
{{#literal}}host=$(cf app "${APP_NAME}" | awk '/routes:/ {print $2}' | sed -E 's#^https?://##; s/,.*$//')
|
|
62
|
+
echo "url=https://$host" >> "$GITHUB_OUTPUT"{{/literal}}
|
|
63
|
+
environment:
|
|
64
|
+
name: {{#literal}}${{ inputs.environment || 'Staging' }}{{/literal}}
|
|
65
|
+
url: {{#literal}}${{ steps.route.outputs.url }}{{/literal}}
|
|
@@ -12,7 +12,7 @@ permissions:
|
|
|
12
12
|
deployments: write
|
|
13
13
|
|
|
14
14
|
concurrency:
|
|
15
|
-
group: {{#literal}}
|
|
15
|
+
group: {{#literal}}kyma-${{ github.workflow }}-${{ github.head_ref || github.run_id }}{{/literal}}
|
|
16
16
|
cancel-in-progress: true
|
|
17
17
|
|
|
18
18
|
env:
|
|
@@ -24,7 +24,7 @@ jobs:
|
|
|
24
24
|
image: public.int.repositories.cloud.sap/ubuntu:24.04
|
|
25
25
|
steps:
|
|
26
26
|
- uses: actions/checkout@v5
|
|
27
|
-
- uses: actions/setup-node@
|
|
27
|
+
- uses: actions/setup-node@v5
|
|
28
28
|
with:
|
|
29
29
|
node-version: 22
|
|
30
30
|
- uses: ./.github/actions/kyma-setup
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
release:
|
|
6
|
+
types: [published]
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
deployments: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
tests:
|
|
14
|
+
uses: {{& githubSlug}}/.github/workflows/test.yaml@main
|
|
15
|
+
secrets: inherit
|
|
16
|
+
{{#hasMta}}
|
|
17
|
+
deploy-cf:
|
|
18
|
+
needs: [tests]
|
|
19
|
+
uses: {{& githubSlug}}/.github/workflows/cf.yaml@main
|
|
20
|
+
secrets: inherit
|
|
21
|
+
with:
|
|
22
|
+
environment: Production
|
|
23
|
+
{{/hasMta}}{{^hasMta}}{{#hasHelm}}
|
|
24
|
+
deploy-kyma:
|
|
25
|
+
needs: [tests]
|
|
26
|
+
uses: {{& githubSlug}}/.github/workflows/kyma.yaml@main
|
|
27
|
+
secrets: inherit
|
|
28
|
+
with:
|
|
29
|
+
environment: Production
|
|
30
|
+
{{/hasHelm}}{{/hasMta}}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const path = require('node:path')
|
|
2
|
+
const { execSync } = require('node:child_process')
|
|
2
3
|
const { readProject } = require('../../projectReader')
|
|
3
4
|
const { renderAndCopy } = require('../../template')
|
|
4
5
|
const cds = require('../../../cds')
|
|
@@ -23,11 +24,27 @@ module.exports = class GitHubActionTemplate extends require('../../plugin') {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
async run() {
|
|
26
|
-
const
|
|
27
|
-
if (!exists(
|
|
28
|
-
await renderAndCopy(path.join(__dirname, 'files/workflows/test.yaml.hbs'),
|
|
27
|
+
const test = `.github/workflows/test.yaml`
|
|
28
|
+
if (!exists(test) || cds.cli.options.force) {
|
|
29
|
+
await renderAndCopy(path.join(__dirname, 'files/workflows/test.yaml.hbs'), test, { sap: cds.cli.options.sap })
|
|
29
30
|
} else {
|
|
30
|
-
console.log('> skipping ' +
|
|
31
|
+
console.log('> skipping ' + test)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const release = `.github/workflows/release.yaml`
|
|
35
|
+
if (!exists(release) || cds.cli.options.force) {
|
|
36
|
+
|
|
37
|
+
let githubSlug
|
|
38
|
+
try {
|
|
39
|
+
githubSlug = execSync('git remote get-url origin', { stdio: 'pipe' }).toString().split('/').slice(-2).join('/').trim().replace(/\.git$/, '')
|
|
40
|
+
} catch (error) {
|
|
41
|
+
githubSlug = '<org/repo>'
|
|
42
|
+
// console.log(`\ncould not determine GitHub URL for the project – replace '<org/repo>' in ${release}`, DEBUG ? error : '')
|
|
43
|
+
}
|
|
44
|
+
const { hasMta, hasHelm } = readProject()
|
|
45
|
+
await renderAndCopy(path.join(__dirname, 'files/workflows/release.yaml.hbs'), release, { sap: cds.cli.options.sap, githubSlug, hasMta, hasHelm })
|
|
46
|
+
} else {
|
|
47
|
+
console.log('> skipping ' + release)
|
|
31
48
|
}
|
|
32
49
|
}
|
|
33
50
|
|
|
@@ -44,10 +61,6 @@ module.exports = class GitHubActionTemplate extends require('../../plugin') {
|
|
|
44
61
|
if (force || !exists(cfSetup)) {
|
|
45
62
|
await copy(path.join(__dirname, 'files/cf-setup'), cfSetup)
|
|
46
63
|
}
|
|
47
|
-
const cfGithub = '.github/actions/github-deployment'
|
|
48
|
-
if (force || !exists(cfGithub)) {
|
|
49
|
-
await copy(path.join(__dirname, 'files/github-deployment'), cfGithub)
|
|
50
|
-
}
|
|
51
64
|
}
|
|
52
65
|
if (hasHelm) {
|
|
53
66
|
const kyma = '.github/workflows/kyma.yaml'
|
|
@@ -46,6 +46,7 @@ modules:
|
|
|
46
46
|
- {{app}}.zip
|
|
47
47
|
target-path: {{#isNodejs}}{{& appPath }}{{/isNodejs}}{{#isJava}}resources{{/isJava}}
|
|
48
48
|
{{/apps}}
|
|
49
|
+
{{#hasWorkzoneStandard}}
|
|
49
50
|
parameters:
|
|
50
51
|
config:
|
|
51
52
|
HTML5Runtime_enabled: true
|
|
@@ -65,6 +66,7 @@ modules:
|
|
|
65
66
|
Authentication: NoAuthentication
|
|
66
67
|
Type: HTTP
|
|
67
68
|
ProxyType: Internet
|
|
69
|
+
{{/hasWorkzoneStandard}}
|
|
68
70
|
|
|
69
71
|
{{#apps}}
|
|
70
72
|
- name: {{strippedAppName}}{{strippedApp}}
|
|
@@ -57,10 +57,10 @@ resources:
|
|
|
57
57
|
service-plan: application
|
|
58
58
|
config:
|
|
59
59
|
display-name: {{appName}}
|
|
60
|
-
{{#hasApprouter}}
|
|
61
60
|
oauth2-configuration:
|
|
62
61
|
token-policy:
|
|
63
62
|
access-token-format: "jwt"
|
|
63
|
+
{{#hasApprouter}}
|
|
64
64
|
redirect-uris:
|
|
65
65
|
- ~{app-api/app-protocol}://*.~{app-api/app-uri}/login/callback
|
|
66
66
|
- ~{app-api/app-protocol}://~{app-api/app-uri}/login/callback
|
|
@@ -91,7 +91,7 @@ resources:
|
|
|
91
91
|
appName: {{appName}}-${org}-${space}
|
|
92
92
|
appCallbacks:
|
|
93
93
|
dependenciesCallbacks:
|
|
94
|
-
url: {{#isJava}}~{srv-api/srv-cert-url}/mt/sms/subscriptions/tenants/{app_tid}/dependencies{{/isJava}}{{#isNodejs}}~{mtx-api/mtx-cert-url}/-/cds/sms-provisioning/dependencies
|
|
94
|
+
url: {{#isJava}}~{srv-api/srv-cert-url}/mt/sms/subscriptions/tenants/{app_tid}/dependencies{{/isJava}}{{#isNodejs}}~{mtx-api/mtx-cert-url}/-/cds/sms-provisioning/dependencies{{/isNodejs}}
|
|
95
95
|
subscriptionCallbacks:
|
|
96
96
|
url: {{#isJava}}~{srv-api/srv-cert-url}/mt/sms/subscriptions/tenants/{app_tid}{{/isJava}}{{#isNodejs}}~{mtx-api/mtx-cert-url}/-/cds/sms-provisioning/tenant/{app_tid}{{/isNodejs}}
|
|
97
97
|
async:
|
|
@@ -50,7 +50,7 @@ subscription-manager:
|
|
|
50
50
|
parameters:
|
|
51
51
|
appCallbacks:
|
|
52
52
|
dependenciesCallbacks:
|
|
53
|
-
{{#literal}}url: https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.cert.{{ .Values.global.domain }}/-/cds/sms-provisioning/dependencies
|
|
53
|
+
{{#literal}}url: https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.cert.{{ .Values.global.domain }}/-/cds/sms-provisioning/dependencies{{/literal}}
|
|
54
54
|
subscriptionCallbacks:
|
|
55
55
|
{{#literal}}url: https://{{ .Release.Name }}-sidecar-{{ .Release.Namespace }}.cert.{{ .Values.global.domain }}/-/cds/sms-provisioning/tenant/{app_tid}{{/literal}}
|
|
56
56
|
appName: {{appName}}
|
|
@@ -24,11 +24,12 @@ resources:
|
|
|
24
24
|
config:
|
|
25
25
|
xsappname: {{appName}}-${org}-${space}
|
|
26
26
|
tenant-mode: {{#hasMultitenancy}}shared{{/hasMultitenancy}}{{^hasMultitenancy}}dedicated{{/hasMultitenancy}}
|
|
27
|
-
{{#hasRoles}}
|
|
27
|
+
{{#hasRoles}}{{^hasCustomRoleCollections}}
|
|
28
28
|
role-collections:
|
|
29
29
|
{{#roles}}
|
|
30
30
|
- name: '{{name}} ({{appName}} ${org}-${space})'
|
|
31
31
|
description: 'generated'
|
|
32
32
|
role-template-references: ['$XSAPPNAME.{{name}}']
|
|
33
33
|
{{/roles}}
|
|
34
|
+
{{/hasCustomRoleCollections}}
|
|
34
35
|
{{/hasRoles}}
|
|
@@ -67,11 +67,13 @@ module.exports = class XsuaaTemplate extends require('../../plugin') {
|
|
|
67
67
|
project.hasRoles = xsSecurity['role-templates'].length > 0
|
|
68
68
|
project.roles = xsSecurity['role-templates'].map(role => role)
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
if (!project.hasCustomRoleCollections) {
|
|
71
|
+
for (const role of xsSecurity['role-templates']) {
|
|
72
|
+
additions.push({
|
|
73
|
+
in: [xsuaa, 'parameters.role-collections'],
|
|
74
|
+
where: { 'role-template-references': ['XSAPPNAME'+role.name] }
|
|
75
|
+
})
|
|
76
|
+
}
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
await merge(__dirname, 'files/mta.yaml.hbs').into('mta.yaml', { project, additions, overwrites, relationships })
|
package/lib/lint/eslintHelp.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "9.
|
|
2
|
+
"version": "9.35.0",
|
|
3
3
|
"help": "\n# SYNOPSIS\n\n *cds lint* [options] file.js [file.js] [dir]\n\n Runs environment checks and/or checks the specified models\n based on the ESLint framework\n\n# OPTIONS\n \n Basic configuration:\n --no-config-lookup Disable look up for eslint.config.js\n -c, --config path::String Use this configuration instead of eslint.config.js, eslint.config.mjs, or eslint.config.cjs\n --inspect-config Open the config inspector with the current configuration\n --ext [String] Specify additional file extensions to lint\n --global [String] Define global variables\n --parser String Specify the parser to be used\n --parser-options Object Specify parser options\n \n Specify Rules and Plugins:\n --plugin [String] Specify plugins\n --rule Object Specify rules\n \n Fix Problems:\n --fix Automatically fix problems\n --fix-dry-run Automatically fix problems without saving the changes to the file system\n --fix-type Array Specify the types of fixes to apply (directive, problem, suggestion, layout)\n \n Ignore Files:\n --no-ignore Disable use of ignore files and patterns\n --ignore-pattern [String] Patterns of files to ignore\n \n Use stdin:\n --stdin Lint code provided on <STDIN> - default: false\n --stdin-filename String Specify filename to process STDIN as\n \n Handle Warnings:\n --quiet Report errors only - default: false\n --max-warnings Int Number of warnings to trigger nonzero exit code - default: -1\n \n Output:\n -o, --output-file path::String Specify file to write report to\n -f, --format String Use a specific output format - default: stylish\n --color, --no-color Force enabling/disabling of color\n \n Inline configuration comments:\n --no-inline-config Prevent comments from changing config or rules\n --report-unused-disable-directives Adds reported errors for unused eslint-disable and eslint-enable directives\n --report-unused-disable-directives-severity String Chooses severity level for reporting unused eslint-disable and eslint-enable directives - either: off, warn, error, 0, 1, or 2\n --report-unused-inline-configs String Adds reported errors for unused eslint inline config comments - either: off, warn, error, 0, 1, or 2\n \n Caching:\n --cache Only check changed files - default: false\n --cache-file path::String Path to the cache file. Deprecated: use --cache-location - default: .eslintcache\n --cache-location path::String Path to the cache file or directory\n --cache-strategy String Strategy to use for detecting changed files in the cache - either: metadata or content - default: metadata\n \n Suppressing Violations:\n --suppress-all Suppress all violations - default: false\n --suppress-rule [String] Suppress specific rules\n --suppressions-location path::String Specify the location of the suppressions file\n --prune-suppressions Prune unused suppressions - default: false\n --pass-on-unpruned-suppressions Ignore unused suppressions - default: false\n \n Miscellaneous:\n --init Run config initialization wizard - default: false\n --env-info Output execution environment information - default: false\n --no-error-on-unmatched-pattern Prevent errors when pattern is unmatched\n --exit-on-fatal-error Exit with exit code 2 in case of fatal error - default: false\n --no-warn-ignored Suppress warnings when the file list includes ignored files\n --pass-on-no-patterns Exit with exit code 0 in case no file patterns are passed\n --debug Output debugging information\n -h, --help Show help\n -v, --version Output the version number\n --print-config path::String Print the configuration for the given file\n --stats Add statistics to the lint report - default: false\n --flag [String] Enable a feature flag\n --mcp Start the ESLint MCP server\n --concurrency Int|String Number of linting threads, auto to choose automatically, off for no multithreading - default: off\n \n",
|
|
4
4
|
"options": [
|
|
5
5
|
"--config",
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sap/cds-dk",
|
|
3
|
-
"version": "9.3.
|
|
3
|
+
"version": "9.3.2",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@sap/cds-dk",
|
|
9
|
-
"version": "9.3.
|
|
9
|
+
"version": "9.3.2",
|
|
10
10
|
"license": "SEE LICENSE IN LICENSE",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@cap-js/asyncapi": "^1.0.0",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
80
|
"node_modules/@eslint/js": {
|
|
81
|
-
"version": "9.
|
|
82
|
-
"integrity": "sha512-
|
|
81
|
+
"version": "9.35.0",
|
|
82
|
+
"integrity": "sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==",
|
|
83
83
|
"license": "MIT",
|
|
84
84
|
"peer": true,
|
|
85
85
|
"engines": {
|
|
@@ -90,8 +90,8 @@
|
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
"node_modules/@sap/cds": {
|
|
93
|
-
"version": "9.3.
|
|
94
|
-
"integrity": "sha512-
|
|
93
|
+
"version": "9.3.1",
|
|
94
|
+
"integrity": "sha512-xA9EN29X8HKaz83Aod1e3Bs6LRvqcCVoaTvCtyOup5rMYDQumcoZ7UOKH6NIUTeO0zOtafUiMHucMG+dBnq5XQ==",
|
|
95
95
|
"license": "SEE LICENSE IN LICENSE",
|
|
96
96
|
"dependencies": {
|
|
97
97
|
"@sap/cds-compiler": "^6.1",
|
|
@@ -120,8 +120,8 @@
|
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
122
|
"node_modules/@sap/cds-compiler": {
|
|
123
|
-
"version": "6.3.
|
|
124
|
-
"integrity": "sha512-
|
|
123
|
+
"version": "6.3.4",
|
|
124
|
+
"integrity": "sha512-3Ip+PgK0yiZXftAC1Pc5euiz4L56t0vX/a4k8xJwTxD0Ccr6G3AvmEFLju5f96D18nr4lH1djC05BeK9yWUpGg==",
|
|
125
125
|
"license": "SEE LICENSE IN LICENSE",
|
|
126
126
|
"bin": {
|
|
127
127
|
"cdsc": "bin/cdsc.js",
|
|
@@ -142,8 +142,8 @@
|
|
|
142
142
|
}
|
|
143
143
|
},
|
|
144
144
|
"node_modules/@sap/cds-mtxs": {
|
|
145
|
-
"version": "3.3.
|
|
146
|
-
"integrity": "sha512-
|
|
145
|
+
"version": "3.3.1",
|
|
146
|
+
"integrity": "sha512-66yWxkVlqu5FsOHxDF8w2JJSlcbgtT2PTegBSECEq9+fjMOaqlxnBHpXXTODCUgK762Ad3kyCUZqW///gmpuMA==",
|
|
147
147
|
"license": "SEE LICENSE IN LICENSE",
|
|
148
148
|
"dependencies": {
|
|
149
149
|
"@sap/hdi-deploy": "^5"
|
|
@@ -261,8 +261,8 @@
|
|
|
261
261
|
"license": "MIT"
|
|
262
262
|
},
|
|
263
263
|
"node_modules/axios": {
|
|
264
|
-
"version": "1.
|
|
265
|
-
"integrity": "sha512-
|
|
264
|
+
"version": "1.12.2",
|
|
265
|
+
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
|
|
266
266
|
"license": "MIT",
|
|
267
267
|
"dependencies": {
|
|
268
268
|
"follow-redirects": "^1.15.6",
|
|
@@ -558,8 +558,8 @@
|
|
|
558
558
|
}
|
|
559
559
|
},
|
|
560
560
|
"node_modules/detect-libc": {
|
|
561
|
-
"version": "2.0
|
|
562
|
-
"integrity": "sha512-
|
|
561
|
+
"version": "2.1.0",
|
|
562
|
+
"integrity": "sha512-vEtk+OcP7VBRtQZ1EJ3bdgzSfBjgnEalLTp5zjJrS+2Z1w2KZly4SBdac/WDU3hhsNAZ9E8SC96ME4Ey8MZ7cg==",
|
|
563
563
|
"license": "Apache-2.0",
|
|
564
564
|
"optional": true,
|
|
565
565
|
"engines": {
|
|
@@ -1213,8 +1213,8 @@
|
|
|
1213
1213
|
"license": "MIT"
|
|
1214
1214
|
},
|
|
1215
1215
|
"node_modules/node-abi": {
|
|
1216
|
-
"version": "3.
|
|
1217
|
-
"integrity": "sha512-
|
|
1216
|
+
"version": "3.77.0",
|
|
1217
|
+
"integrity": "sha512-DSmt0OEcLoK4i3NuscSbGjOf3bqiDEutejqENSplMSFA/gmB8mkED9G4pKWnPl7MDU4rSHebKPHeitpDfyH0cQ==",
|
|
1218
1218
|
"license": "MIT",
|
|
1219
1219
|
"optional": true,
|
|
1220
1220
|
"dependencies": {
|
|
@@ -1665,8 +1665,8 @@
|
|
|
1665
1665
|
}
|
|
1666
1666
|
},
|
|
1667
1667
|
"node_modules/tar-fs": {
|
|
1668
|
-
"version": "2.1.
|
|
1669
|
-
"integrity": "sha512-
|
|
1668
|
+
"version": "2.1.4",
|
|
1669
|
+
"integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==",
|
|
1670
1670
|
"license": "MIT",
|
|
1671
1671
|
"optional": true,
|
|
1672
1672
|
"dependencies": {
|
package/package.json
CHANGED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
name: GitHub Deployment for Cloud Foundry
|
|
2
|
-
description: "Creates a GitHub deployment for your Cloud Foundry app"
|
|
3
|
-
|
|
4
|
-
inputs:
|
|
5
|
-
app-name:
|
|
6
|
-
description: "Base name of the deployed CAP app"
|
|
7
|
-
required: true
|
|
8
|
-
environment:
|
|
9
|
-
description: "Deployment environment name"
|
|
10
|
-
required: true
|
|
11
|
-
|
|
12
|
-
runs:
|
|
13
|
-
using: "composite"
|
|
14
|
-
steps:
|
|
15
|
-
- name: Get deployed server URL
|
|
16
|
-
id: get-url
|
|
17
|
-
shell: bash
|
|
18
|
-
run: |
|
|
19
|
-
raw=$(cf app "${{ inputs.app-name }}" | awk '/routes:/ {print $2}')
|
|
20
|
-
raw=$(echo "$raw" | sed -E 's#^https?://##')
|
|
21
|
-
echo "url=https://$raw" >> "$GITHUB_OUTPUT"
|
|
22
|
-
|
|
23
|
-
- name: Create GitHub Deployment
|
|
24
|
-
uses: actions/github-script@v7
|
|
25
|
-
env:
|
|
26
|
-
URL: ${{ steps.get-url.outputs.url }}
|
|
27
|
-
ENVIRONMENT: ${{ inputs.environment }}
|
|
28
|
-
with:
|
|
29
|
-
script: |
|
|
30
|
-
const { owner, repo } = context.repo
|
|
31
|
-
const ref = context.ref
|
|
32
|
-
const url = process.env.URL
|
|
33
|
-
const environment = process.env.ENVIRONMENT
|
|
34
|
-
|
|
35
|
-
const deployment = await github.rest.repos.createDeployment({
|
|
36
|
-
owner,
|
|
37
|
-
repo,
|
|
38
|
-
ref,
|
|
39
|
-
required_contexts: [],
|
|
40
|
-
environment
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
await github.rest.repos.createDeploymentStatus({
|
|
44
|
-
owner,
|
|
45
|
-
repo,
|
|
46
|
-
deployment_id: deployment.data.id,
|
|
47
|
-
state: 'success',
|
|
48
|
-
environment_url: url,
|
|
49
|
-
log_url: `https://github.com/${owner}/${repo}/actions/runs/${context.runId}`
|
|
50
|
-
})
|