@sap/cds-dk 7.5.1 → 7.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/add.js +28 -24
- package/bin/bind.js +59 -32
- package/bin/build.js +1 -1
- package/bin/cds.js +1 -1
- package/bin/env.js +1 -6
- package/bin/import.js +2 -2
- package/bin/init.js +7 -3
- package/bin/logout.js +2 -2
- package/bin/migrate.js +1 -1
- package/bin/version.js +1 -0
- package/bin/watch.js +8 -1
- package/lib/bind/bindingUtil.js +14 -20
- package/lib/bind/cfKeyProvider.js +8 -10
- package/lib/bind/index.js +4 -2
- package/lib/build/buildTaskFactory.js +31 -20
- package/lib/build/constants.js +9 -8
- package/lib/build/provider/featureToggleBuildPlugin.js +1 -7
- package/lib/build/provider/hana/index.js +44 -9
- package/lib/build/provider/internalBuildTaskProvider.js +7 -5
- package/lib/build/provider/java/index.js +0 -6
- package/lib/build/provider/mtx-sidecar/index.js +5 -0
- package/lib/build/provider/nodejs/index.js +32 -47
- package/lib/build/util.js +134 -33
- package/lib/deploy/to-hana/hana.js +47 -77
- package/lib/deploy/to-hana/hdiDeployUtil.js +15 -18
- package/lib/deploy/to-hana/mtaUtil.js +1 -1
- package/lib/env/schema.js +157 -17
- package/lib/env/schemas/cds-rc.js +118 -0
- package/lib/import/common.js +18 -23
- package/lib/import/odata/metadataconverter/MetadataConverter.js +12 -8
- package/lib/import/odata/v4parser.js +67 -12
- package/lib/init/constants.js +2 -4
- package/lib/init/merge.js +2 -1
- package/lib/init/template/application-logging/index.js +1 -2
- package/lib/init/template/approuter/files/mta.yaml.hbs +2 -0
- package/lib/init/template/approuter/files/package.json +1 -1
- package/lib/init/template/approuter/files/values.yaml.hbs +8 -1
- package/lib/init/template/hana/files/undeploy.json +3 -2
- package/lib/init/template/helm/files/chart/templates/NOTES.txt +2 -0
- package/lib/init/template/helm/files/chart/templates/_deployment_helpers.tpl +10 -2
- package/lib/init/template/helm/files/content-deployment/Chart.yaml +1 -1
- package/lib/init/template/helm/files/content-deployment/templates/_helpers.tpl +6 -5
- package/lib/init/template/helm/files/content-deployment/values.yaml +1 -1
- package/lib/init/template/helm/files/service-instance/Chart.yaml +1 -1
- package/lib/init/template/helm/files/service-instance/templates/_helpers.tpl +6 -5
- package/lib/init/template/helm/files/service-instance/values.yaml +1 -1
- package/lib/init/template/helm/files/web-application/Chart.yaml +1 -1
- package/lib/init/template/helm/files/web-application/templates/_api.tpl +6 -5
- package/lib/init/template/helm/files/web-application/values.yaml +1 -1
- package/lib/init/template/helm/index.js +1 -12
- package/lib/init/template/helm-unified-runtime/index.js +3 -2
- package/lib/init/template/html5-repo/files/values.yaml.hbs +9 -0
- package/lib/init/template/multitenancy/files/package.sidecar.json.hbs +1 -2
- package/lib/init/template/multitenancy/index.js +3 -2
- package/lib/init/template/xsuaa/files/package.json.hbs +1 -2
- package/lib/lint/Linter.js +2 -2
- package/lib/lint/checks.js +0 -51
- package/lib/mtx/activate.js +1 -1
- package/lib/mtx/auth_manager.js +50 -57
- package/lib/mtx/download_legacy.js +1 -1
- package/lib/mtx/extend.js +2 -2
- package/lib/mtx/pull.js +1 -1
- package/lib/mtx/push.js +1 -1
- package/lib/mtx/settings_manager.js +90 -44
- package/lib/mtx/tenant_action.js +1 -1
- package/lib/mtx/util/dependencies.js +1 -1
- package/lib/mtx/util/legacy_ext.js +0 -1
- package/lib/mtx/util/logging.js +10 -8
- package/lib/util/axios.js +6 -19
- package/lib/util/cf.js +15 -10
- package/npm-shrinkwrap.json +104 -48
- package/package.json +1 -10
- package/lib/env/schemas/cds-rc.json +0 -101
- package/lib/init/template/helm-unified-runtime/lib/build.js +0 -0
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
const cp = require('child_process');
|
|
2
1
|
const path = require('path');
|
|
3
|
-
const util = require('util');
|
|
4
2
|
|
|
5
3
|
const fs = require('fs').promises;
|
|
6
4
|
|
|
7
5
|
const cds = require('../../../lib/cds');
|
|
6
|
+
const { exists, read } = cds.utils;
|
|
8
7
|
|
|
9
8
|
const { build } = require('../../build');
|
|
10
9
|
const buildConstants = require('../../build/constants');
|
|
11
10
|
|
|
12
|
-
const
|
|
11
|
+
const hdi = require('./hdiDeployUtil');
|
|
13
12
|
const cfUtil = require('../../util/cf');
|
|
14
13
|
const gitUtil = require('../../util/git');
|
|
15
14
|
const mtaUtil = require('./mtaUtil');
|
|
@@ -18,15 +17,9 @@ const { bold, info } = require('../../util/term');
|
|
|
18
17
|
|
|
19
18
|
const { bind } = require('../../bind');
|
|
20
19
|
|
|
21
|
-
const
|
|
20
|
+
const DEBUG = cds.debug('deploy');
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
class HanaDeployer {
|
|
25
|
-
|
|
26
|
-
constructor() {
|
|
27
|
-
this.execAsync = util.promisify(cp.exec);
|
|
28
|
-
this.spawnSync = cp.spawnSync;
|
|
29
|
-
}
|
|
22
|
+
module.exports = new class HanaDeployer {
|
|
30
23
|
|
|
31
24
|
getFromEnv(...varNames) {
|
|
32
25
|
const result = {}
|
|
@@ -42,11 +35,8 @@ class HanaDeployer {
|
|
|
42
35
|
return result;
|
|
43
36
|
}
|
|
44
37
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
async deploy(options = {}) { // NOSONAR
|
|
48
|
-
let { model, serviceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions, dry } = options;
|
|
49
|
-
|
|
38
|
+
async deploy(options = {}) {
|
|
39
|
+
let { model, serviceName, tunnelAddress, vcapFile, undeployWhitelist, hdiOptions = {}, dry, storeCredentials } = options;
|
|
50
40
|
if (model) {
|
|
51
41
|
throw `Specifying a CDS model for HANA deployment is not supported.
|
|
52
42
|
'cds deploy --to hana' performs 'cds build --for hana' which in turn uses the configured HANA build task.
|
|
@@ -65,15 +55,16 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
65
55
|
|
|
66
56
|
const buildResults = await this._build();
|
|
67
57
|
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
const fromFile = vcapFile && exists(vcapFile) ? await read(vcapFile) : {};
|
|
59
|
+
const fromEnv = this.getFromEnv('VCAP_SERVICES', 'SERVICE_REPLACEMENTS');
|
|
60
|
+
let vcapEnv = { ...fromFile, ...fromEnv };
|
|
70
61
|
if (process.env.TARGET_CONTAINER) {
|
|
71
|
-
|
|
62
|
+
vcapEnv.TARGET_CONTAINER = process.env.TARGET_CONTAINER;
|
|
72
63
|
}
|
|
73
64
|
|
|
74
65
|
if (dry) {
|
|
75
|
-
for (const
|
|
76
|
-
const model = await cds.load(
|
|
66
|
+
for (const { task } of buildResults) {
|
|
67
|
+
const model = await cds.load(task.src);
|
|
77
68
|
const data = cds.compile.to.hdbtable(model);
|
|
78
69
|
for (const [src, { file }] of data) {
|
|
79
70
|
console.log(`-- ${file}`);
|
|
@@ -82,49 +73,47 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
82
73
|
}
|
|
83
74
|
}
|
|
84
75
|
} else {
|
|
85
|
-
for (const
|
|
86
|
-
|
|
87
|
-
let serviceKeyName;
|
|
88
|
-
const currentModelFolder = buildResult.task.dest;
|
|
76
|
+
for (const { task } of buildResults) {
|
|
77
|
+
const dest = task.dest;
|
|
89
78
|
|
|
90
79
|
if (undeployWhitelist) {
|
|
91
80
|
console.log('Writing undeploy.json');
|
|
92
|
-
await fs.write(path.join(
|
|
81
|
+
await fs.write(path.join(dest, 'undeploy.json'), JSON.stringify(undeployWhitelist, null, 2));
|
|
93
82
|
}
|
|
94
83
|
|
|
95
84
|
const hasVCAPEnv = Object.keys(vcapEnv).length > 0;
|
|
96
85
|
if (!serviceName && hasVCAPEnv) {
|
|
97
|
-
await fs.mkdir(
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
await this._getOrCreateCFService(projectPath,
|
|
101
|
-
serviceKeyName = cfServiceInstanceKeyName;
|
|
102
|
-
serviceName = serviceName || cfServiceInstanceName;
|
|
86
|
+
await fs.mkdir(dest, { recursive: true });
|
|
87
|
+
}
|
|
88
|
+
const { cfServiceInstanceName, cfServiceInstanceKeyName, serviceKey } =
|
|
89
|
+
await this._getOrCreateCFService(projectPath, dest, serviceName, tunnelAddress);
|
|
103
90
|
|
|
104
|
-
|
|
91
|
+
serviceName ??= cfServiceInstanceName;
|
|
92
|
+
if (!vcapEnv.VCAP_SERVICES || serviceName) {
|
|
93
|
+
vcapEnv.VCAP_SERVICES = this._getVCAPServicesEntry(cfServiceInstanceName, serviceKey);
|
|
94
|
+
}
|
|
105
95
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
96
|
+
if (storeCredentials) {
|
|
97
|
+
await this._addInstanceToDefaultEnvJson([dest, projectPath], cfServiceInstanceName, serviceKey);
|
|
109
98
|
}
|
|
110
99
|
|
|
111
100
|
console.log(`${bold('Starting deployment to SAP HANA ...')}`);
|
|
112
101
|
|
|
113
|
-
|
|
114
|
-
await
|
|
102
|
+
DEBUG?.({ dest, vcapEnv, hdiOptions })
|
|
103
|
+
await hdi.deploy(dest, vcapEnv, hdiOptions);
|
|
115
104
|
|
|
116
105
|
if (!hasVCAPEnv) {
|
|
117
106
|
const bindOptions = {
|
|
118
107
|
...options,
|
|
119
|
-
to: `${serviceName}:${
|
|
108
|
+
to: `${serviceName}:${cfServiceInstanceKeyName}`,
|
|
120
109
|
for: options.for,
|
|
121
|
-
kind:
|
|
110
|
+
kind: task.for
|
|
122
111
|
}
|
|
123
112
|
await bind(bindOptions);
|
|
124
113
|
}
|
|
125
114
|
}
|
|
126
115
|
|
|
127
|
-
if (
|
|
116
|
+
if (storeCredentials) {
|
|
128
117
|
await gitUtil.ensureFileIsGitignored('default-env.json', projectPath);
|
|
129
118
|
}
|
|
130
119
|
|
|
@@ -155,11 +144,11 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
155
144
|
this._validateServiceInstanceName(cfServiceInstanceName);
|
|
156
145
|
console.log(`Using container ${bold(cfServiceInstanceName)}`);
|
|
157
146
|
|
|
158
|
-
|
|
147
|
+
const cfConfig = cfServiceInstanceMta?.parameters?.config;
|
|
159
148
|
const serviceInstance = await this.createHanaService(cfServiceInstanceName, cfConfig);
|
|
160
149
|
|
|
161
150
|
const cfServiceInstanceKeyName = `${cfServiceInstanceName}-key`;
|
|
162
|
-
let serviceKey = await cfUtil.getOrCreateServiceKey(serviceInstance, cfServiceInstanceKeyName);
|
|
151
|
+
let serviceKey = await cfUtil.getOrCreateServiceKey(serviceInstance, cfServiceInstanceKeyName, { permissions: 'development' });
|
|
163
152
|
this._validateServiceKey(serviceKey, cfServiceInstanceKeyName);
|
|
164
153
|
|
|
165
154
|
if (tunnelAddress) {
|
|
@@ -191,13 +180,17 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
191
180
|
|
|
192
181
|
_validateServiceKey(serviceKey, cfServiceInstanceKey) {
|
|
193
182
|
if (!serviceKey) {
|
|
194
|
-
throw
|
|
183
|
+
throw `Could not create service key ${bold(cfServiceInstanceKey)}.`;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (serviceKey['sm_url']) {
|
|
187
|
+
throw `Service key credentials are matching to a Service Manager instance of a multitenant environment. Make sure there is no conflict with ${cfServiceInstanceKey}.`;
|
|
195
188
|
}
|
|
196
189
|
|
|
197
190
|
const fields = ['schema', 'user', 'password', 'url'];
|
|
198
191
|
for (const field of fields) {
|
|
199
192
|
if (!serviceKey[field]) {
|
|
200
|
-
throw
|
|
193
|
+
throw `Service key is missing mandatory field '${field}'. Make sure you are ${bold('not')} using a managed service.`;
|
|
201
194
|
}
|
|
202
195
|
}
|
|
203
196
|
}
|
|
@@ -209,40 +202,21 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
209
202
|
}
|
|
210
203
|
|
|
211
204
|
console.log(`Running build`);
|
|
212
|
-
|
|
213
|
-
return buildResults;
|
|
205
|
+
return build(buildTaskOptions);
|
|
214
206
|
}
|
|
215
207
|
|
|
216
|
-
async _loadEnvFile(defaultEnvFile) {
|
|
217
|
-
try {
|
|
218
|
-
const content = await fs.readFile(defaultEnvFile);
|
|
219
|
-
return JSON.parse(content);
|
|
220
|
-
} catch (err) {
|
|
221
|
-
throw new Error(`Error reading default env file: ${err.message}`);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
208
|
async _addInstanceToDefaultEnvJson(currentFolders, serviceInstanceName, serviceKey) {
|
|
227
209
|
for (const currentFolder of currentFolders) {
|
|
228
|
-
let defaultEnvJson = {};
|
|
229
210
|
const defaultEnvJsonPath = path.join(currentFolder, 'default-env.json');
|
|
211
|
+
const defaultEnvJson = exists(defaultEnvJsonPath) ? await read(defaultEnvJsonPath) : {};
|
|
230
212
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
// ignore any errors
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
defaultEnvJson.VCAP_SERVICES = defaultEnvJson.VCAP_SERVICES || {};
|
|
238
|
-
for (const serviceKey of Object.keys(defaultEnvJson.VCAP_SERVICES)) {
|
|
239
|
-
defaultEnvJson.VCAP_SERVICES[serviceKey] = defaultEnvJson.VCAP_SERVICES[serviceKey].filter((service) => {
|
|
240
|
-
return (service.name !== serviceInstanceName);
|
|
241
|
-
});
|
|
213
|
+
defaultEnvJson.VCAP_SERVICES ??= {};
|
|
214
|
+
for (const [serviceKey, services] of Object.entries(defaultEnvJson.VCAP_SERVICES)) {
|
|
215
|
+
defaultEnvJson.VCAP_SERVICES[serviceKey] = services.filter(service => service.name !== serviceInstanceName);
|
|
242
216
|
}
|
|
243
217
|
|
|
244
218
|
const hanaEntry = this._getVCAPServicesEntry(serviceInstanceName, serviceKey)
|
|
245
|
-
Object.assign(defaultEnvJson, hanaEntry);
|
|
219
|
+
Object.assign(defaultEnvJson.VCAP_SERVICES, hanaEntry);
|
|
246
220
|
|
|
247
221
|
console.log(`Writing ${defaultEnvJsonPath}`);
|
|
248
222
|
await fs.mkdir(path.dirname(defaultEnvJsonPath), { recursive: true })
|
|
@@ -253,15 +227,13 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
253
227
|
|
|
254
228
|
_getVCAPServicesEntry(serviceInstanceName, serviceKey) {
|
|
255
229
|
return {
|
|
256
|
-
VCAP_SERVICES: {
|
|
257
230
|
hana: [
|
|
258
231
|
{
|
|
259
232
|
name: serviceInstanceName,
|
|
260
233
|
tags: ['hana'],
|
|
261
234
|
credentials: serviceKey
|
|
262
235
|
}
|
|
263
|
-
|
|
264
|
-
}
|
|
236
|
+
]
|
|
265
237
|
};
|
|
266
238
|
}
|
|
267
239
|
|
|
@@ -269,14 +241,14 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
269
241
|
_validateServiceInstanceName(serviceInstanceName) {
|
|
270
242
|
// valid service name chars: alpha-numeric, hyphens, and underscores
|
|
271
243
|
if (/[^\w-_]+/g.exec(serviceInstanceName)) {
|
|
272
|
-
throw
|
|
244
|
+
throw `Service name ${serviceInstanceName} must only contain alpha-numeric, hyphens, and underscores.`;
|
|
273
245
|
}
|
|
274
246
|
}
|
|
275
247
|
|
|
276
248
|
|
|
277
249
|
_injectTunnelAddress(serviceKey, tunnelAddress) {
|
|
278
250
|
if (!/\w+:\d+/.test(tunnelAddress)) {
|
|
279
|
-
throw
|
|
251
|
+
throw `Invalid tunnel address '${tunnelAddress}' - must be in form 'host:port'`;
|
|
280
252
|
}
|
|
281
253
|
const [tunnelHost, tunnelPort] = tunnelAddress.split(':')
|
|
282
254
|
const { host, port } = serviceKey
|
|
@@ -288,5 +260,3 @@ Therefore, specifying the CDS model is not required when calling 'cds deploy'.`;
|
|
|
288
260
|
return serviceKey
|
|
289
261
|
}
|
|
290
262
|
}
|
|
291
|
-
|
|
292
|
-
module.exports = new HanaDeployer();
|
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const util = require('util');
|
|
3
|
+
const { bold } = require('../../util/term');
|
|
3
4
|
|
|
4
5
|
const HDI_DEPLOY_LIB = '@sap/hdi-deploy';
|
|
5
6
|
|
|
6
|
-
class HdiDeployUtil {
|
|
7
|
+
module.exports = new class HdiDeployUtil {
|
|
7
8
|
|
|
8
|
-
async deploy(dbDir, vcapEnv = {},
|
|
9
|
+
async deploy(dbDir, vcapEnv = {}, { autoUndeploy } = {}) {
|
|
9
10
|
console.log(`Deploying to HANA from ${dbDir}`);
|
|
10
11
|
|
|
11
|
-
const
|
|
12
|
+
const { clean_env, deploy } = this._loadHdiDeployLib(dbDir);
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
const deployerEnv = JSON.parse(JSON.stringify(process.env));
|
|
14
15
|
const hdiDeployOptions = deployerEnv.HDI_DEPLOY_OPTIONS ? JSON.parse(deployerEnv.HDI_DEPLOY_OPTIONS) : {};
|
|
15
16
|
delete hdiDeployOptions.root;
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
deployerEnv = hdiDeployLib.clean_env(deployerEnv);
|
|
19
|
-
}
|
|
18
|
+
clean_env?.(deployerEnv);
|
|
20
19
|
|
|
21
20
|
if (vcapEnv.VCAP_SERVICES) {
|
|
22
21
|
deployerEnv.VCAP_SERVICES = JSON.stringify(vcapEnv.VCAP_SERVICES);
|
|
@@ -28,8 +27,8 @@ class HdiDeployUtil {
|
|
|
28
27
|
deployerEnv.TARGET_CONTAINER = vcapEnv.TARGET_CONTAINER;
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
if (
|
|
32
|
-
console.log(`
|
|
30
|
+
if (autoUndeploy) {
|
|
31
|
+
console.log(`HDI deployer automatically undeploys deleted resources using --auto-undeploy.`);
|
|
33
32
|
hdiDeployOptions.auto_undeploy = true;
|
|
34
33
|
}
|
|
35
34
|
|
|
@@ -37,12 +36,12 @@ class HdiDeployUtil {
|
|
|
37
36
|
deployerEnv.HDI_DEPLOY_OPTIONS = JSON.stringify(hdiDeployOptions);
|
|
38
37
|
}
|
|
39
38
|
|
|
40
|
-
const asyncDeploy = util.promisify(
|
|
41
|
-
const
|
|
42
|
-
if (
|
|
43
|
-
let message = `HDI deployment failed with exit code ${
|
|
44
|
-
if (
|
|
45
|
-
message += `. ${
|
|
39
|
+
const asyncDeploy = util.promisify(deploy);
|
|
40
|
+
const { exitCode, signal } = await asyncDeploy(dbDir, deployerEnv);
|
|
41
|
+
if (exitCode) {
|
|
42
|
+
let message = `HDI deployment failed with exit code ${exitCode}`;
|
|
43
|
+
if (signal) {
|
|
44
|
+
message += `. ${signal}`;
|
|
46
45
|
}
|
|
47
46
|
throw new Error(message);
|
|
48
47
|
}
|
|
@@ -55,7 +54,7 @@ class HdiDeployUtil {
|
|
|
55
54
|
if (!libPath) {
|
|
56
55
|
throw new Error(`Required library '${HDI_DEPLOY_LIB}' not found in
|
|
57
56
|
${searchPaths.join('\n ')}
|
|
58
|
-
Add it either
|
|
57
|
+
Add it either to ${bold('devDependencies')} using 'npm install -D ${HDI_DEPLOY_LIB}' or install it globally using 'npm install -g ${HDI_DEPLOY_LIB}'.`);
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
console.log(`Using HDI deployer from ${libPath}`)
|
|
@@ -64,5 +63,3 @@ Add it either as a devDependency using 'npm install -D ${HDI_DEPLOY_LIB}' or ins
|
|
|
64
63
|
return require(libPath);
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
|
-
|
|
68
|
-
module.exports = new HdiDeployUtil();
|
|
@@ -98,7 +98,7 @@ async function _getProjectInfo(projectPath, mta) {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
async function _getMta(projectPath) {
|
|
101
|
-
// yaml.parse
|
|
101
|
+
// yaml.parse doesn't like null
|
|
102
102
|
const mtaFilePath = path.join(projectPath, MTA_YAML)
|
|
103
103
|
|
|
104
104
|
const existsMtaYaml = existsSync(mtaFilePath)
|
package/lib/env/schema.js
CHANGED
|
@@ -1,35 +1,175 @@
|
|
|
1
|
-
const { merge } = require('../init/merge')
|
|
2
|
-
const { readJSON } = require('../util/fs')
|
|
3
1
|
|
|
4
|
-
|
|
2
|
+
function _local(id) {
|
|
3
|
+
return require.resolve(id, {
|
|
4
|
+
paths: [
|
|
5
|
+
process.cwd(), // project-local module is preferred
|
|
6
|
+
__dirname // otherwise from our own dependencies
|
|
7
|
+
]
|
|
8
|
+
})
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function _robustRequire(file) {
|
|
12
|
+
const fs = require('fs')
|
|
13
|
+
const path = require('path')
|
|
14
|
+
|
|
15
|
+
file = path.normalize(file)
|
|
16
|
+
let content
|
|
17
|
+
try {
|
|
18
|
+
content = fs.readFileSync(file, 'utf8')
|
|
19
|
+
} catch (e) {
|
|
20
|
+
// file is not readable -> nothing to do
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(content)
|
|
26
|
+
} catch (e) {
|
|
27
|
+
// invalid json -> ignore and try regex
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const result = {}
|
|
31
|
+
try {
|
|
32
|
+
const deps = JSON.parse(content.match(/"dependencies"\s*:\s*({[^}]*})/)[1])
|
|
33
|
+
result.dependencies = deps
|
|
34
|
+
} catch (e) { /* ignore */ }
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const devDeps = JSON.parse(content.match(/"devDependencies"\s*:\s*({[^}]*})/)[1])
|
|
38
|
+
result.devDependencies = devDeps
|
|
39
|
+
} catch (e) { /* ignore */ }
|
|
40
|
+
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
_robustRequire.resolve = function (id, options = {}) {
|
|
45
|
+
const fs = require('fs')
|
|
46
|
+
const path = require('path')
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
return require.resolve(id, options)
|
|
50
|
+
} catch (err) {
|
|
51
|
+
// ignore
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const [, mod, file] = /(.+)\/([^/]+)$/.exec(id)
|
|
55
|
+
let modPath = path.join(process.cwd(), 'node_modules', mod, file)
|
|
56
|
+
const ext = path.extname(modPath)
|
|
57
|
+
if (!ext) {
|
|
58
|
+
modPath = modPath + '.js'
|
|
59
|
+
}
|
|
60
|
+
if (fs.existsSync(modPath)) {
|
|
61
|
+
return modPath
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
throw new Error('not found')
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function mergePluginSchema(pluginSchema) {
|
|
68
|
+
return {
|
|
69
|
+
into: async (targetSchema) => {
|
|
70
|
+
if (pluginSchema.buildTaskType) {
|
|
71
|
+
targetSchema.$defs?.buildTaskType?.enum?.push(pluginSchema.buildTaskType.name);
|
|
72
|
+
targetSchema.$defs?.buildTaskType?.enumDescriptions?.push(pluginSchema.buildTaskType.description);
|
|
73
|
+
targetSchema._cds_schema_overlays.push('buildTaskType')
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (pluginSchema.databaseType) {
|
|
77
|
+
targetSchema.$defs?.databaseType?.enum?.push(pluginSchema.databaseType.name);
|
|
78
|
+
targetSchema.$defs?.databaseType?.enumDescriptions?.push(pluginSchema.databaseType.description);
|
|
79
|
+
targetSchema._cds_schema_overlays.push('databaseType')
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (pluginSchema.cds) {
|
|
83
|
+
if (targetSchema.$defs?.cdsRoot?.properties) {
|
|
84
|
+
Object.assign(targetSchema.$defs.cdsRoot.properties, pluginSchema.cds)
|
|
85
|
+
targetSchema._cds_schema_overlays.push('cdsRoot')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// backwards compatibility
|
|
89
|
+
if (targetSchema.properties) {
|
|
90
|
+
Object.assign(targetSchema.properties, pluginSchema.cds)
|
|
91
|
+
targetSchema._cds_schema_overlays.push('root')
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = async function overlay4(schemaName) {
|
|
99
|
+
const path = require('path')
|
|
100
|
+
const { merge } = require('../init/merge')
|
|
5
101
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const cds = require(require.resolve('@sap/cds', {
|
|
9
|
-
paths: [cwd, __dirname]
|
|
10
|
-
}))
|
|
102
|
+
const cds = require('../cds')
|
|
103
|
+
cds.root = process.cwd()
|
|
11
104
|
|
|
12
|
-
|
|
13
|
-
cds.root = cwd
|
|
105
|
+
const { readJSON } = require('../util/fs')
|
|
14
106
|
|
|
15
|
-
// load defaults, throw err if schema not found
|
|
16
107
|
let result;
|
|
17
108
|
if (typeof cds.schema?.default4 === 'function') {
|
|
18
109
|
result = await cds.schema.default4(schemaName)
|
|
19
110
|
} else if (cds.env?.schemas?.[schemaName]) {
|
|
20
|
-
// backwards compatibility
|
|
111
|
+
// backwards compatibility, fails for invalid json files
|
|
21
112
|
result = await readJSON(cds.env.schemas[schemaName])
|
|
22
113
|
} else {
|
|
23
|
-
throw new Error(`cds ${cds.version} in ${
|
|
114
|
+
throw new Error(`cds ${cds.version} in ${cds.root} does not support schema retrieval.`)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (schemaName !== 'cds-rc.json') {
|
|
118
|
+
return result
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const oldTitle = result.title
|
|
122
|
+
const oldDescription = result.description
|
|
123
|
+
result._cds_schema_overlays = []
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const scheamFile = path.join(__dirname, 'schemas', schemaName.replace(/\.json$/, '.js'))
|
|
127
|
+
const schema = require(scheamFile)
|
|
128
|
+
|
|
129
|
+
await merge(schema).into(result)
|
|
130
|
+
result._cds_schema_overlays.push('cdsRoot')
|
|
131
|
+
|
|
132
|
+
if (result.properties) {
|
|
133
|
+
// backwards compatibility
|
|
134
|
+
await merge(schema.$defs.cdsRoot.properties).into(result.properties)
|
|
135
|
+
result._cds_schema_overlays.push('root')
|
|
136
|
+
}
|
|
137
|
+
} catch (err) {
|
|
138
|
+
console.error(err.message)
|
|
24
139
|
}
|
|
25
140
|
|
|
26
|
-
// optional, don't throw if file not found
|
|
27
141
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
142
|
+
const pluginsLib = require(_local('@sap/cds/lib/plugins.js'))
|
|
143
|
+
if (pluginsLib) {
|
|
144
|
+
const oldRequire = pluginsLib.require
|
|
145
|
+
try {
|
|
146
|
+
pluginsLib.require = _robustRequire
|
|
147
|
+
const plugins = pluginsLib.fetch?.()
|
|
148
|
+
if (plugins) {
|
|
149
|
+
for (const [key, { impl, packageJson }] of Object.entries(plugins)) {
|
|
150
|
+
const pluginPackageJsonFile = packageJson || path.join(path.dirname(impl), 'package.json')
|
|
151
|
+
try {
|
|
152
|
+
const pluginPackageJson = require(pluginPackageJsonFile)
|
|
153
|
+
if (pluginPackageJson.cds?.schema) {
|
|
154
|
+
await mergePluginSchema(pluginPackageJson.cds.schema).into(result);
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
if (err.code !== 'ENOENT') {
|
|
158
|
+
console.error(`Error loading schema from plugin ${key}: ${err.message}`)
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
} finally {
|
|
164
|
+
pluginsLib.require = oldRequire
|
|
165
|
+
}
|
|
166
|
+
}
|
|
30
167
|
} catch (err) {
|
|
31
|
-
|
|
168
|
+
console.error(err.message)
|
|
32
169
|
}
|
|
33
170
|
|
|
171
|
+
|
|
172
|
+
result.title = oldTitle
|
|
173
|
+
result.description = oldDescription
|
|
34
174
|
return result
|
|
35
175
|
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
title: "JSON schema for CDS-DK configuration",
|
|
3
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
4
|
+
description: "Build overlay",
|
|
5
|
+
$defs: {
|
|
6
|
+
cdsRoot: {
|
|
7
|
+
properties: {
|
|
8
|
+
// build specific schema
|
|
9
|
+
build: {
|
|
10
|
+
type: "object",
|
|
11
|
+
description: "Customize CDS build settings.",
|
|
12
|
+
additionalProperties: true,
|
|
13
|
+
properties: {
|
|
14
|
+
target: {
|
|
15
|
+
type: "string",
|
|
16
|
+
description: "Specify the folder which contains generated build results. Defaults to './gen'.",
|
|
17
|
+
format: "uri-reference"
|
|
18
|
+
},
|
|
19
|
+
tasks: {
|
|
20
|
+
type: "array",
|
|
21
|
+
description: "Control which tasks 'cds build' will execute on your project modules. This is a typical task configuration: '\"tasks\": [{ \"for\": \"hana\"}];",
|
|
22
|
+
uniqueItems: true,
|
|
23
|
+
default: [
|
|
24
|
+
{}
|
|
25
|
+
],
|
|
26
|
+
items: {
|
|
27
|
+
type: "object",
|
|
28
|
+
additionalProperties: true,
|
|
29
|
+
properties: {
|
|
30
|
+
dest: {
|
|
31
|
+
type: "string",
|
|
32
|
+
description: "Optional: Define the destination folder for this build task, relative to build target folder. \nFor example: Destination defined as 'custom' results in an output written to 'gen/custom'.",
|
|
33
|
+
format: "uri-reference"
|
|
34
|
+
},
|
|
35
|
+
src: {
|
|
36
|
+
type: "string",
|
|
37
|
+
description: "Optional: Prefer defining 'cds.folders'. Use 'src' if you want it only to be valid for this specific build task, which excludes, for example, 'cds watch'.",
|
|
38
|
+
format: "uri-reference"
|
|
39
|
+
},
|
|
40
|
+
for: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Define the target technology for this build task.",
|
|
43
|
+
minLength: 1,
|
|
44
|
+
$ref: "#/$defs/buildTaskType"
|
|
45
|
+
},
|
|
46
|
+
options: {
|
|
47
|
+
type: "object",
|
|
48
|
+
description: "Optional: Set target technology specific options for this build task.",
|
|
49
|
+
additionalProperties: true,
|
|
50
|
+
properties: {
|
|
51
|
+
model: {
|
|
52
|
+
$ref: "#/$defs/foldersStringArray"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
// deploy specific schema
|
|
63
|
+
deploy: {
|
|
64
|
+
type: "object",
|
|
65
|
+
description: "Customize 'cds deploy' settings.",
|
|
66
|
+
additionalProperties: true,
|
|
67
|
+
properties: {
|
|
68
|
+
no_save: {
|
|
69
|
+
type: "boolean",
|
|
70
|
+
description: "Do / don't update configuration in package.json.",
|
|
71
|
+
default: true
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
buildTaskType: {
|
|
79
|
+
enum: [
|
|
80
|
+
"nodejs",
|
|
81
|
+
"java",
|
|
82
|
+
"hana",
|
|
83
|
+
"mtx-sidecar",
|
|
84
|
+
"mtx-extension",
|
|
85
|
+
"mtx",
|
|
86
|
+
"node-cf",
|
|
87
|
+
"java-cf"
|
|
88
|
+
],
|
|
89
|
+
enumDescriptions: [
|
|
90
|
+
"Node.js build plugin",
|
|
91
|
+
"Java build plugin",
|
|
92
|
+
"SAP HANA build plugin",
|
|
93
|
+
"MTX sidecar build plugin",
|
|
94
|
+
"MTX extension build plugin",
|
|
95
|
+
"MTX build plugin",
|
|
96
|
+
"Node.js Cloud Foundry build plugin",
|
|
97
|
+
"Java Cloud Foundry build plugin"
|
|
98
|
+
]
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
foldersStringArray: {
|
|
102
|
+
oneOf: [
|
|
103
|
+
{
|
|
104
|
+
type: "string",
|
|
105
|
+
format: "uri-reference"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
type: "array",
|
|
109
|
+
uniqueItems: true,
|
|
110
|
+
items: {
|
|
111
|
+
type: "string",
|
|
112
|
+
format: "uri-reference"
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|