@sap/cds-dk 6.2.3 → 6.3.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/CHANGELOG.md +39 -0
- package/bin/bind.js +4 -4
- package/bin/deploy/to-h2.js +4 -0
- package/bin/deploy/to-sqlite.js +6 -4
- package/bin/help.js +1 -1
- package/bin/login.js +1 -0
- package/bin/push.js +29 -7
- package/lib/bind/userCdsrcJsonStorageProvider.js +4 -4
- package/lib/client/activate.js +1 -1
- package/lib/client/base_command.js +10 -10
- package/lib/client/helper/dependency_helper.js +10 -1
- package/lib/client/helper/logging.js +6 -2
- package/lib/client/pull.js +6 -6
- package/lib/client/push.js +33 -14
- package/lib/client/settings_manager.js +53 -22
- package/lib/compile/openapi/csdl2openapi.js +1 -1
- package/lib/compile/to-xsuaa.js +1 -1
- package/lib/import/odata/v2parser.js +37 -1
- package/lib/import/odata/v4parser.js +101 -23
- package/lib/init/constants.js +1 -1
- package/lib/init/index.js +23 -45
- package/lib/init/template/approuter/index.js +44 -48
- package/lib/init/template/auditlog/index.js +14 -10
- package/lib/init/template/cf-manifest/index.js +9 -11
- package/lib/init/template/connectivity/index.js +10 -10
- package/lib/init/template/data/index.js +20 -19
- package/lib/init/template/destinations/index.js +14 -9
- package/lib/init/template/enterprise-messaging/index.js +14 -9
- package/lib/init/template/extensibility/index.js +14 -9
- package/lib/init/template/hana/index.js +51 -57
- package/lib/init/template/helm/index.js +27 -59
- package/lib/init/template/helm/subchart/templates/_helpers.tpl +38 -0
- package/lib/init/template/helm/subchart/templates/deployment.yaml +12 -12
- package/lib/init/template/helm/subchart/values.md +565 -194
- package/lib/init/template/helm/subchart/values.schema.json +163 -8
- package/lib/init/template/helm/subchart/values.yaml +17 -1
- package/lib/init/template/html5-repo/index.js +15 -11
- package/lib/init/template/java/index.js +15 -11
- package/lib/init/template/kibana-logging/index.js +30 -31
- package/lib/init/template/lint/index.js +17 -18
- package/lib/init/template/mta/index.js +18 -40
- package/lib/init/template/mtx/index.js +2 -3
- package/lib/init/template/multitenancy/index.js +27 -20
- package/lib/init/template/nodejs/index.js +18 -20
- package/lib/init/template/notebook/index.js +5 -434
- package/lib/init/template/pipeline/index.js +10 -10
- package/lib/init/template/samples/index.js +7 -16
- package/lib/init/template/templateBase.js +17 -18
- package/lib/init/template/toggles/files/cdsrc.json +5 -0
- package/lib/init/template/toggles/files/cdsrc.json.hbs +7 -0
- package/lib/init/template/toggles/index.js +12 -6
- package/lib/init/template/xsuaa/index.js +44 -41
- package/lib/init/types.d.ts +0 -59
- package/lib/init/util/commandUtil.js +6 -7
- package/lib/init/util/fsUtil.js +23 -109
- package/lib/init/util/merge.js +319 -0
- package/lib/init/util/mvnArchetypeUtil.js +6 -5
- package/lib/init/util/projectReader.js +125 -300
- package/lib/init/util/spinner.js +10 -8
- package/lib/init/util/templateUtil.js +25 -368
- package/lib/lint/Linter.js +2 -3
- package/lib/lint/checks.js +0 -1
- package/lib/lint/io.js +18 -34
- package/npm-shrinkwrap.json +1132 -1190
- package/package.json +3 -3
- package/lib/init/template/notebook/cap_magic.py +0 -118
- package/lib/init/template/notebook/files/{{{name}}}.ipynb.hbs +0 -90
- package/lib/init/template/notebook/files/{{{name}}}.json +0 -102
- package/lib/init/template/notebook/notebook.js +0 -394
- package/lib/init/util/logger.js +0 -42
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const cds = require('../../../cds')
|
|
2
|
-
const
|
|
1
|
+
const cds = require('../../../cds')
|
|
2
|
+
const { path, copy, exists } = cds.utils
|
|
3
3
|
const ProjectReader = require('../../util/projectReader')
|
|
4
|
-
|
|
4
|
+
const { mergeJSON, mergeYAML, sortDependencies } = require('../../util/merge')
|
|
5
5
|
const {
|
|
6
6
|
srvNode, srvJava, // Server
|
|
7
7
|
mtxSidecar, // Additional Modules
|
|
@@ -9,10 +9,17 @@ const {
|
|
|
9
9
|
providedMtxAPI, srvAPI, providedMtxSidecarAPI, requiredMtxSidecarAPI // APIs
|
|
10
10
|
} = require('../_merging/registry-mta');
|
|
11
11
|
|
|
12
|
-
module.exports = class MultitenancyTemplate extends
|
|
12
|
+
module.exports = class MultitenancyTemplate extends require('../templateBase') {
|
|
13
13
|
constructor(projectPath, generator) {
|
|
14
14
|
super(projectPath, generator, __dirname)
|
|
15
|
-
this.projectReader = new ProjectReader(
|
|
15
|
+
this.projectReader = new ProjectReader(projectPath)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
static hasFacet(env, projectPath, options) {
|
|
19
|
+
// REVISIT: Check if this can be simplified
|
|
20
|
+
return options?.add?.has('mtx') || options?.add?.has('multitenancy') ||
|
|
21
|
+
!!env.requires?.multitenancy || !!env.requires?.db?.multiTenant ||
|
|
22
|
+
this.isJava && exists(path.join(projectPath, 'mtx', 'sidecar')) || false
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
async run() {
|
|
@@ -21,22 +28,22 @@ module.exports = class MultitenancyTemplate extends TemplateBase {
|
|
|
21
28
|
if (isNodejs) {
|
|
22
29
|
const packageJSONPath = path.join(this.projectPath, 'package.json')
|
|
23
30
|
const cdsTemplateFile = forProfile ? 'cds.package.json.hbs' : 'cds.package.json'
|
|
24
|
-
await
|
|
25
|
-
await
|
|
26
|
-
await
|
|
31
|
+
await mergeJSON(packageJSONPath, path.join(__dirname, 'files', cdsTemplateFile), projectDescriptor)
|
|
32
|
+
await mergeJSON(packageJSONPath, path.join(__dirname, 'files', 'dependencies.package.json'))
|
|
33
|
+
await sortDependencies(packageJSONPath)
|
|
27
34
|
} else if (isJava) {
|
|
28
35
|
const cdsrcJSONPath = path.join(this.projectPath, '.cdsrc.json')
|
|
29
|
-
await
|
|
30
|
-
await
|
|
36
|
+
await mergeJSON(cdsrcJSONPath, path.join(__dirname, 'files', 'java', '.cdsrc.json'))
|
|
37
|
+
await copy(path.join(__dirname, 'files', 'java', 'package.json'), path.join(this.projectPath, 'mtx', 'sidecar', 'package.json'))
|
|
31
38
|
}
|
|
32
39
|
await this.runDependentMerging()
|
|
33
40
|
}
|
|
34
41
|
|
|
35
42
|
async runDependentMerging() {
|
|
36
43
|
const projectDescriptor = await this.projectReader.read()
|
|
37
|
-
const { isNodejs, isJava,
|
|
44
|
+
const { isNodejs, isJava, hasMta, hasHelm, hasApprouter, hasXsuaa, hasHana } = projectDescriptor.cap
|
|
38
45
|
|
|
39
|
-
if (
|
|
46
|
+
if (hasMta) {
|
|
40
47
|
const modules = isNodejs ? [srvNode] : [srvJava, mtxSidecar]
|
|
41
48
|
const srv = isNodejs ? srvNode : srvJava
|
|
42
49
|
const services = [saasRegistry]
|
|
@@ -68,11 +75,11 @@ module.exports = class MultitenancyTemplate extends TemplateBase {
|
|
|
68
75
|
const templateMtaPath = path.join(__dirname, `files`, `mta.yaml.hbs`)
|
|
69
76
|
const mtaPath = path.join(this.projectPath, 'mta.yaml')
|
|
70
77
|
const mergingRules = { additions, overwrites, relationships }
|
|
71
|
-
await
|
|
78
|
+
await mergeYAML(mtaPath, templateMtaPath, projectDescriptor, mergingRules)
|
|
72
79
|
}
|
|
73
80
|
|
|
74
|
-
if (
|
|
75
|
-
await
|
|
81
|
+
if (hasHelm) {
|
|
82
|
+
await mergeYAML(
|
|
76
83
|
path.join(this.projectPath, 'chart', 'values.yaml'),
|
|
77
84
|
{
|
|
78
85
|
xsuaa: {
|
|
@@ -83,12 +90,12 @@ module.exports = class MultitenancyTemplate extends TemplateBase {
|
|
|
83
90
|
null, { forceOverwrite: true }
|
|
84
91
|
)
|
|
85
92
|
//if (hasXsuaa) { // REVISIT: Can we get rid of this dependency?
|
|
86
|
-
await
|
|
93
|
+
await copy(
|
|
87
94
|
path.join(__dirname, 'files', 'xsuaa.yaml'),
|
|
88
95
|
path.join(this.projectPath, 'chart', 'templates', 'xsuaa.yaml')
|
|
89
96
|
)
|
|
90
97
|
//}
|
|
91
|
-
await
|
|
98
|
+
await copy(
|
|
92
99
|
path.join(__dirname, 'files', 'saas-registry.yaml'),
|
|
93
100
|
path.join(this.projectPath, 'chart', 'templates', 'saas-registry.yaml')
|
|
94
101
|
)
|
|
@@ -107,7 +114,7 @@ module.exports = class MultitenancyTemplate extends TemplateBase {
|
|
|
107
114
|
}
|
|
108
115
|
|
|
109
116
|
if (hasXsuaa) {
|
|
110
|
-
await
|
|
117
|
+
await mergeJSON(
|
|
111
118
|
path.join(this.projectPath, 'xs-security.json'),
|
|
112
119
|
path.join(__dirname, 'files', 'xs-security.json.hbs'),
|
|
113
120
|
projectDescriptor,
|
|
@@ -125,9 +132,9 @@ module.exports = class MultitenancyTemplate extends TemplateBase {
|
|
|
125
132
|
)
|
|
126
133
|
}
|
|
127
134
|
|
|
128
|
-
if (hasXsuaa &&
|
|
135
|
+
if (hasXsuaa && hasHelm) {
|
|
129
136
|
// REVISIT: Shared xs-security.json location for Helm and MTA?
|
|
130
|
-
await
|
|
137
|
+
await copy(
|
|
131
138
|
path.join(this.projectPath, 'xs-security.json'),
|
|
132
139
|
path.join(this.projectPath, 'chart', 'xs-security.json')
|
|
133
140
|
)
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
+
const { mkdirp } = require('../../../cds').utils
|
|
3
|
+
const { COMMAND_ADD, OPTION_JAVA, OPTION_NODEJS, URLS } = require('../../constants')
|
|
4
|
+
const { copyFiles } = require('../../util/templateUtil')
|
|
5
|
+
const LOG = console
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
const { COMMAND_ADD, OPTION_JAVA, OPTION_NODEJS, URLS } = require('../../constants');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
module.exports = class NodeJSTemplate extends TemplateBase {
|
|
7
|
+
module.exports = class NodeJSTemplate extends require('../templateBase') {
|
|
8
8
|
constructor(projectPath, generator) {
|
|
9
9
|
super(projectPath, generator, __dirname);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
static hasFacet() {
|
|
13
|
+
// TODO: Implement
|
|
14
|
+
//return this.pLanguage === P_LANGUAGE_NODEJS
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async canRun() {
|
|
13
18
|
if (this.options._cmd === COMMAND_ADD) {
|
|
14
19
|
throw new Error(`You can't change the type of an existing project.`);
|
|
15
20
|
}
|
|
@@ -27,27 +32,20 @@ module.exports = class NodeJSTemplate extends TemplateBase {
|
|
|
27
32
|
const values = {
|
|
28
33
|
projectName: this.projectName
|
|
29
34
|
}
|
|
30
|
-
await
|
|
31
|
-
|
|
32
|
-
const dbFolderPath = path.join(this.projectPath, env.folders.db);
|
|
33
|
-
await this.fsUtil.mkdirp(dbFolderPath);
|
|
34
|
-
|
|
35
|
-
const srvFolderPath = path.join(this.projectPath, env.folders.srv);
|
|
36
|
-
await this.fsUtil.mkdirp(srvFolderPath);
|
|
37
|
-
|
|
38
|
-
const appFolderPath = path.join(this.projectPath, env.folders.app);
|
|
39
|
-
await this.fsUtil.mkdirp(appFolderPath);
|
|
35
|
+
await copyFiles(path.join(__dirname, 'files'), this.projectPath, values, this.options.force)
|
|
40
36
|
|
|
41
|
-
|
|
37
|
+
await mkdirp(this.projectPath, env.folders.db);
|
|
38
|
+
await mkdirp(this.projectPath, env.folders.srv);
|
|
39
|
+
await mkdirp(this.projectPath, env.folders.app);
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
async finalize() {
|
|
45
43
|
const relativeProjectPath = path.relative(this.cwd, this.projectPath);
|
|
46
44
|
if (relativeProjectPath && relativeProjectPath !== '.') {
|
|
47
|
-
|
|
45
|
+
LOG.info(`Continue with 'cd ${relativeProjectPath}'`);
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
LOG.info(`Find samples on ${URLS.SAMPLES}`);
|
|
49
|
+
LOG.info(`Learn about next steps at ${URLS.CAPIRE}`);
|
|
52
50
|
}
|
|
53
51
|
}
|
|
@@ -1,442 +1,13 @@
|
|
|
1
|
-
const TemplateBase = require('../templateBase');
|
|
2
|
-
|
|
3
|
-
const os = require('os');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const axios = require('axios');
|
|
6
|
-
const cp = require('child_process');
|
|
7
|
-
|
|
8
|
-
const commandUtil = require('../../util/commandUtil');
|
|
9
1
|
const term = require('../../../util/term');
|
|
10
|
-
const Notebook = require('./notebook');
|
|
11
|
-
|
|
12
|
-
const { URLS } = require('../../constants');
|
|
13
|
-
const { IS_MAC, IS_WIN, PYTHON_VENV_NAME } = require('../../constants');
|
|
14
|
-
|
|
15
|
-
const nbType = 'default';
|
|
16
|
-
const nbRequired = [];
|
|
17
|
-
const nbTypes = {
|
|
18
|
-
'default': ['/docs/get-started/in-a-nutshell'],
|
|
19
|
-
'mta': [
|
|
20
|
-
'/docs/guides/databases#configure-hana',
|
|
21
|
-
'/docs/advanced/deploy-to-cloud'
|
|
22
|
-
],
|
|
23
|
-
'mtx': ['/docs/guides/multitenancy#nodejs-stack']
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const REGEX_REDIRECT = /<meta\s+http-equiv\s*=\s*"refresh"\s+content\s*=\s*".*\s+url\s*=\s*(.*)\s*"/i;
|
|
27
2
|
|
|
28
|
-
module.exports = class NotebookTemplate extends
|
|
3
|
+
module.exports = class NotebookTemplate extends require('../templateBase') {
|
|
29
4
|
constructor(projectPath, generator) {
|
|
30
5
|
super(projectPath, generator, __dirname);
|
|
31
|
-
this.pythonCmd = null;
|
|
32
|
-
this.pythonPckCmd = null;
|
|
33
|
-
this.pythonPath = '';
|
|
34
|
-
this.pythonPackages = '';
|
|
35
|
-
this.pythonPathLib = '';
|
|
36
|
-
this.hasVscode = false;
|
|
37
|
-
this.hasJupyterlab = false;
|
|
38
|
-
this.validPythonVenv = false;
|
|
39
|
-
this.binPythonVenv = 'bin';
|
|
40
|
-
this.libPythonVenv = 'lib';
|
|
41
|
-
this.notebookEnv = '';
|
|
42
|
-
this.notebookExtensions = '';
|
|
43
|
-
this.hasNotebookExtension = false;
|
|
44
|
-
this.scrapedCapire = {};
|
|
45
|
-
this.nb_json = null;
|
|
46
|
-
this.missingDependencies = [];
|
|
47
|
-
this.logOutput = '';
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async _collectMatchingCmds(matches) {
|
|
51
|
-
let allCmds = '';
|
|
52
|
-
for (const match of matches) {
|
|
53
|
-
if (IS_WIN) {
|
|
54
|
-
const matchReceived = await this._tryCatchReturn({
|
|
55
|
-
error: null,
|
|
56
|
-
cmd: `where`,
|
|
57
|
-
args: [`${match}*`]
|
|
58
|
-
})
|
|
59
|
-
if (matchReceived) {
|
|
60
|
-
allCmds += matchReceived;
|
|
61
|
-
}
|
|
62
|
-
} else {
|
|
63
|
-
let compgenCmd = `compgen -c ${match}`;
|
|
64
|
-
if (IS_MAC) {
|
|
65
|
-
compgenCmd = `compgen -ac | grep ${match}`
|
|
66
|
-
}
|
|
67
|
-
const matchReceived = await this._tryCatchReturn({
|
|
68
|
-
error: null,
|
|
69
|
-
cmd: '/bin/bash',
|
|
70
|
-
args: ['-c', `${compgenCmd}`]
|
|
71
|
-
})
|
|
72
|
-
if (matchReceived) {
|
|
73
|
-
allCmds += matchReceived;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
const cmds = [];
|
|
78
|
-
for (const cmd of allCmds.split('\n')) {
|
|
79
|
-
cmds.push(path.basename(cmd.trim()))
|
|
80
|
-
}
|
|
81
|
-
return cmds
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async _pythonCmdCheck() {
|
|
85
|
-
const pythonAliases = await this._collectMatchingCmds(['python']);
|
|
86
|
-
for (const pythonAlias of pythonAliases) {
|
|
87
|
-
try {
|
|
88
|
-
let pythonVersion = await commandUtil.spawnCommand(pythonAlias, ['--version'], {
|
|
89
|
-
cwd: this.projectPath
|
|
90
|
-
}, undefined, true);
|
|
91
|
-
pythonVersion = pythonVersion.replace('Python ', '');
|
|
92
|
-
if (parseFloat(pythonVersion) >= parseFloat('3.8.5')) {
|
|
93
|
-
this.pythonCmd = pythonAlias;
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
} catch (err) {
|
|
97
|
-
// Do nothing
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
if (this.pythonCmd === null) {
|
|
101
|
-
this.missingDependencies.push('Python (>=v3.8.5):\n' +
|
|
102
|
-
term.warn(` ${URLS.PYTHON}\n`));
|
|
103
|
-
if (!IS_WIN) {
|
|
104
|
-
this.logger.log(`When using the configure script, you must add ${term.bold('--with-ensurepip=install')}`)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async _pythonPckCmdCheck() {
|
|
110
|
-
const pipAliases = await this._collectMatchingCmds(['pip', 'conda']);
|
|
111
|
-
for (const pipAlias of pipAliases) {
|
|
112
|
-
try {
|
|
113
|
-
await commandUtil.spawnCommand(pipAlias, ['--version'], {
|
|
114
|
-
cwd: this.projectPath
|
|
115
|
-
}, undefined);
|
|
116
|
-
this.pythonPckCmd = pipAlias;
|
|
117
|
-
break;
|
|
118
|
-
} catch (err) {
|
|
119
|
-
// Do nothing
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (this.pythonPckCmd === null) {
|
|
123
|
-
try {
|
|
124
|
-
await commandUtil.spawnCommand('conda', ['info'], {
|
|
125
|
-
cwd: this.projectPath
|
|
126
|
-
}, undefined, true);
|
|
127
|
-
this.pythonPckCmd = 'conda';
|
|
128
|
-
} catch (err) {
|
|
129
|
-
this.missingDependencies.push(
|
|
130
|
-
'Python package manager:\n' +
|
|
131
|
-
term.warn(` ${URLS.PYTHON_PIP}`)
|
|
132
|
-
);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async _pythonPcksCheck() {
|
|
138
|
-
const argPckCmd = 'list';
|
|
139
|
-
if (this.pythonPckCmd !== null) {
|
|
140
|
-
try {
|
|
141
|
-
if (!this.pythonPackages) {
|
|
142
|
-
this.pythonPackages = await commandUtil.spawnCommand(this.pythonPckCmd, [argPckCmd], {
|
|
143
|
-
cwd: this.projectPath
|
|
144
|
-
}, undefined, true);
|
|
145
|
-
}
|
|
146
|
-
} catch (err) {
|
|
147
|
-
// Do nothing
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
if (this.pythonPackages.includes('jupyterlab')) {
|
|
151
|
-
this.hasJupyterlab = true;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async _notebookViewerCheck() {
|
|
156
|
-
try {
|
|
157
|
-
await commandUtil.spawnCommand('code', ['--version'], {
|
|
158
|
-
cwd: this.projectPath
|
|
159
|
-
});
|
|
160
|
-
this.hasVscode = true;
|
|
161
|
-
} catch (err) {
|
|
162
|
-
this.logger.log(`Consider installing vscode: ${URLS.VSCODE}`)
|
|
163
|
-
}
|
|
164
|
-
if (this.hasVscode) {
|
|
165
|
-
this.notebookExtensions = await commandUtil.spawnCommand('code',
|
|
166
|
-
['--list-extensions', '--category', 'notebooks'], {
|
|
167
|
-
cwd: this.projectPath
|
|
168
|
-
}, undefined, true);
|
|
169
|
-
if (this.notebookExtensions && this.notebookExtensions.includes('ms-python.python')) {
|
|
170
|
-
this.hasNotebookExtension = true;
|
|
171
|
-
}
|
|
172
|
-
if (!this.hasNotebookExtension) {
|
|
173
|
-
this.missingDependencies.push('No notebook viewer found!\n' +
|
|
174
|
-
term.info(`Consider installing the VS Code Python extension:\n
|
|
175
|
-
'ms-python.python': ${URLS.VSCODE_NBVIEWER}\n`));
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
_getNotebookEnv() {
|
|
181
|
-
this.notebookEnv = path.join(os.homedir(), PYTHON_VENV_NAME);
|
|
182
|
-
let cmd;
|
|
183
|
-
if (IS_WIN) {
|
|
184
|
-
cmd = 'where';
|
|
185
|
-
} else {
|
|
186
|
-
cmd = 'which';
|
|
187
|
-
}
|
|
188
|
-
this.pythonPath = cp.execSync(`${cmd} ${this.pythonCmd}`).toString();
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
async _tryCatch(task) {
|
|
192
|
-
try {
|
|
193
|
-
await commandUtil.spawnCommand(
|
|
194
|
-
task.cmd,
|
|
195
|
-
task.args, {
|
|
196
|
-
cwd: this.projectPath
|
|
197
|
-
}, undefined, true);
|
|
198
|
-
} catch (err) {
|
|
199
|
-
if (task.error) {
|
|
200
|
-
throw new Error(task.error);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async _tryCatchReturn(task) {
|
|
206
|
-
try {
|
|
207
|
-
const result = await commandUtil.spawnCommand(
|
|
208
|
-
task.cmd,
|
|
209
|
-
task.args, {
|
|
210
|
-
cwd: this.projectPath
|
|
211
|
-
}, undefined, true);
|
|
212
|
-
return result;
|
|
213
|
-
} catch (err) {
|
|
214
|
-
if (task.error) {
|
|
215
|
-
throw new Error(task.error);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
async _createPythonVenv(pythonPackagesReq) {
|
|
221
|
-
await this._tryCatch({
|
|
222
|
-
error: "Failed to initialize virtualenv.\n" +
|
|
223
|
-
"If you get the error [ ... 'ensurepip', '--upgrade', '--default-pip']', please install ensurepip via:\n" +
|
|
224
|
-
`${term.bold('sudo apt-get install ' + this.pythonCmd + '-venv')}`,
|
|
225
|
-
cmd: `${this.pythonCmd}`,
|
|
226
|
-
args: ['-m', 'venv', `${this.notebookEnv}`]
|
|
227
|
-
})
|
|
228
|
-
if (IS_WIN) {
|
|
229
|
-
await this._tryCatch({
|
|
230
|
-
error: "Failed to prepare virtualenv",
|
|
231
|
-
cmd: `"${path.join(this.notebookEnv, this.binPythonVenv, 'activate')} && ` +
|
|
232
|
-
`pip install ${pythonPackagesReq.join(' ')} && ` +
|
|
233
|
-
`${path.join(this.notebookEnv, this.binPythonVenv, 'ipython')} kernel install --user --name=${PYTHON_VENV_NAME} && ` +
|
|
234
|
-
`deactivate"`,
|
|
235
|
-
args: []
|
|
236
|
-
})
|
|
237
|
-
} else {
|
|
238
|
-
await this._tryCatch({
|
|
239
|
-
error: "Failed to prepare virtualenv",
|
|
240
|
-
cmd: '/bin/sh',
|
|
241
|
-
args: ['-c', `. ${path.join(this.notebookEnv, this.binPythonVenv, 'activate')}
|
|
242
|
-
pip install ${pythonPackagesReq.join(' ')}
|
|
243
|
-
${path.join(this.notebookEnv, this.binPythonVenv, 'ipython')} kernel install --user --name=${PYTHON_VENV_NAME}
|
|
244
|
-
deactivate`]
|
|
245
|
-
})
|
|
246
|
-
}
|
|
247
6
|
}
|
|
248
7
|
|
|
249
|
-
async
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (IS_WIN) {
|
|
254
|
-
this.pythonPathLib = path.join(this.notebookEnv, this.libPythonVenv, 'site-packages');
|
|
255
|
-
} else {
|
|
256
|
-
this.pythonPathLib = path.join(this.notebookEnv, this.libPythonVenv, 'python*', 'site-packages');
|
|
257
|
-
}
|
|
258
|
-
// Check if required packages exist
|
|
259
|
-
for (const pck of pythonPackagesReq) {
|
|
260
|
-
const pckExists = await this.fsUtil.pathExists(this.pythonPathLib, pck);
|
|
261
|
-
if (!pckExists) {
|
|
262
|
-
return false;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
// Check if required scripts exist
|
|
266
|
-
let pythonScriptsReq;
|
|
267
|
-
if (IS_WIN) {
|
|
268
|
-
pythonScriptsReq = ['python.exe', 'ipython.exe', 'jupyter-kernel.exe'];
|
|
269
|
-
} else {
|
|
270
|
-
pythonScriptsReq = ['python', 'ipython', 'jupyter-kernel'];
|
|
271
|
-
}
|
|
272
|
-
for (const pck of pythonScriptsReq) {
|
|
273
|
-
const pckExists = await this.fsUtil.pathExists(this.pythonPathLib, pck);
|
|
274
|
-
if (!pckExists) {
|
|
275
|
-
return false;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
} else {
|
|
279
|
-
return false
|
|
280
|
-
}
|
|
281
|
-
return true;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
async _url2Html(contentUrl) {
|
|
285
|
-
const response = await axios.get(contentUrl, {
|
|
286
|
-
headers: {
|
|
287
|
-
'Access-Control-Allow-Origin': '*'
|
|
288
|
-
}, 'timeout': 20000
|
|
289
|
-
});
|
|
290
|
-
const match = REGEX_REDIRECT.exec(response.data);
|
|
291
|
-
if (match && match[1]) {
|
|
292
|
-
const newContentUrl = new URL(contentUrl);
|
|
293
|
-
const newUrl = new URL(match[1], newContentUrl.origin);
|
|
294
|
-
this._url2Html(newUrl)
|
|
295
|
-
} else {
|
|
296
|
-
return response.data;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
async _collectHtmlPages(nbReq) {
|
|
301
|
-
if (!this.scrapedCapire[nbReq]) {
|
|
302
|
-
let pageCollection = '';
|
|
303
|
-
for (let p = 0; p < nbTypes[nbReq].length; p++) {
|
|
304
|
-
const page = nbTypes[nbReq][p];
|
|
305
|
-
try {
|
|
306
|
-
const html = await this._url2Html(URLS.CAPIRE + page);
|
|
307
|
-
if (!pageCollection) {
|
|
308
|
-
pageCollection = html;
|
|
309
|
-
} else {
|
|
310
|
-
pageCollection += html;
|
|
311
|
-
}
|
|
312
|
-
} catch (err) {
|
|
313
|
-
// Do nothing
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
this.scrapedCapire[nbReq] = pageCollection;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
async _notebookInit(projectName) {
|
|
321
|
-
// Create new notebook, overwrite if already exists
|
|
322
|
-
await this.templateUtil.copyFiles('.', this.projectPath, { name: projectName, cap: URLS.CAPIRE }, true);
|
|
323
|
-
this.nb_json = path.resolve('.', this.projectPath, `${projectName}.ipynb`);
|
|
324
|
-
const exists = await this.fsUtil.pathExists(this.nb_json);
|
|
325
|
-
const nb = exists ? await this.fsUtil.readJSON(this.nb_json) : {};
|
|
326
|
-
return nb;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
async _notebookWriteFromComponents(type) {
|
|
330
|
-
if (type === 'mta') {
|
|
331
|
-
nbRequired.push('default')
|
|
332
|
-
} else if (type === 'mtx') {
|
|
333
|
-
nbRequired.push('default')
|
|
334
|
-
nbRequired.push('mta')
|
|
335
|
-
}
|
|
336
|
-
nbRequired.push(type)
|
|
337
|
-
|
|
338
|
-
for (let n = 0; n < nbRequired.length; n++) {
|
|
339
|
-
const nbReq = nbRequired[n];
|
|
340
|
-
|
|
341
|
-
// Get notebook name
|
|
342
|
-
let projectName = `${this.projectName}`
|
|
343
|
-
if (nbReq !== 'default') {
|
|
344
|
-
projectName = `${this.projectName}-${nbReq}`
|
|
345
|
-
}
|
|
346
|
-
await this._collectHtmlPages(nbReq);
|
|
347
|
-
const nb = await this._notebookInit(projectName);
|
|
348
|
-
|
|
349
|
-
// Add cap magic
|
|
350
|
-
const stringPython = await this.fsUtil.readFile(path.join(__dirname, 'cap_magic.py'), 'UTF-8');
|
|
351
|
-
nb['metadata']['cap_magic'] = stringPython;
|
|
352
|
-
|
|
353
|
-
// Generate notebook
|
|
354
|
-
this.logger.debug(` writing ${type} notebook...`)
|
|
355
|
-
const notebook = new Notebook({ name: projectName, nb: nb, html: this.scrapedCapire[nbReq], url: URLS.CAPIRE, route: nbTypes[nbReq][0] });
|
|
356
|
-
await this.fsUtil.writeJSON(this.nb_json, notebook.nb);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
async checkEnabled() {
|
|
361
|
-
this.logger.warn('There is a new generation of notebooks. Find out more about the new VS Code based CAP Notebooks on https://cap.cloud.sap/docs/releases/jun22#vs-code-cap-notebooks.');
|
|
362
|
-
this.logger.log();
|
|
363
|
-
return true;
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
async run() {
|
|
367
|
-
|
|
368
|
-
// REQUIRED: Python [python or python3]
|
|
369
|
-
if (!this.pythonCmd) {
|
|
370
|
-
await this._pythonCmdCheck()
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// REQUIRED: Python package manager
|
|
374
|
-
if (!this.pythonPckCmd) {
|
|
375
|
-
await this._pythonPckCmdCheck()
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// OPTIONAL: JupyterLab viewer in python packages
|
|
379
|
-
if (!this.hasJupyterlab) {
|
|
380
|
-
await this._pythonPcksCheck()
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// OPTIONAL: Jupyter Notebook viewer in VS Code extensions
|
|
384
|
-
if (!this.hasVscode && !this.hasNotebookExtension) {
|
|
385
|
-
await this._notebookViewerCheck()
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// If missing dependencies, throw error
|
|
389
|
-
if (this.missingDependencies.length > 0) {
|
|
390
|
-
let error = '\nYou are missing ' + this.missingDependencies.length + ' notebook dependencies!\n' +
|
|
391
|
-
'Please install the following components (in order!) and rerun your cds command:\n\n'
|
|
392
|
-
for (let i = 0; i < this.missingDependencies.length; i++) {
|
|
393
|
-
error += '(' + (i + 1) + ') ' + this.missingDependencies[i] + '\n';
|
|
394
|
-
}
|
|
395
|
-
throw new Error(error);
|
|
396
|
-
} else {
|
|
397
|
-
if (IS_WIN) {
|
|
398
|
-
this.binPythonVenv = 'Scripts';
|
|
399
|
-
this.libPythonVenv = 'Lib';
|
|
400
|
-
}
|
|
401
|
-
if (!this.pythonPath) {
|
|
402
|
-
this._getNotebookEnv();
|
|
403
|
-
}
|
|
404
|
-
// If not exists, install virtualenv with required packages
|
|
405
|
-
const pythonPackagesReq = ['ipykernel', 'notebook'];
|
|
406
|
-
if (!this.validPythonVenv) {
|
|
407
|
-
this.validPythonVenv = await this._checkPythonVenv(pythonPackagesReq);
|
|
408
|
-
if (!this.validPythonVenv) {
|
|
409
|
-
await this._createPythonVenv(pythonPackagesReq);
|
|
410
|
-
}
|
|
411
|
-
}
|
|
412
|
-
// Generate notebook from components
|
|
413
|
-
await this._notebookWriteFromComponents(nbType);
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
async finalize() {
|
|
418
|
-
this.logOutput = [' '];
|
|
419
|
-
if (this.hasVscode && this.hasNotebookExtension) {
|
|
420
|
-
this.logOutput.push(`To view the notebook in VS Code:`);
|
|
421
|
-
this.logOutput.push(`\t1. ${term.info('Ctrl + Click')} the following link: ${term.info(`${this.nb_json}`)}`);
|
|
422
|
-
this.logOutput.push(`\t2. Make sure to ${term.info('Trust')} the notebook in the popup window to enable markdown and code execution`);
|
|
423
|
-
this.logOutput.push(`\t3. Run the notebook`);
|
|
424
|
-
this.logOutput.push('');
|
|
425
|
-
}
|
|
426
|
-
if (this.pythonPackages.includes('notebook') && this.hasJupyterlab) {
|
|
427
|
-
this.logOutput.push(`To view the notebook in JupyterLab:`);
|
|
428
|
-
this.logOutput.push(`\t1. Open with JupyterLab: ${term.info(`python -m jupyterlab ${path.relative(process.cwd(), this.nb_json)}`)}`);
|
|
429
|
-
this.logOutput.push(`\t2. Run the notebook`);
|
|
430
|
-
this.logOutput.push('');
|
|
431
|
-
}
|
|
432
|
-
this.logOutput.push(`To view the notebook in your browser:`);
|
|
433
|
-
this.logOutput.push(`\t1. Open with Jupyter: ${term.info(path.join(this.notebookEnv, this.binPythonVenv, 'jupyter') +
|
|
434
|
-
' notebook ' + path.relative(process.cwd(), this.nb_json))}`);
|
|
435
|
-
this.logOutput.push(`\t2. Run the notebook`);
|
|
436
|
-
this.logOutput.push('');
|
|
437
|
-
|
|
438
|
-
for (const line of this.logOutput) {
|
|
439
|
-
this.logger.log(line);
|
|
440
|
-
}
|
|
8
|
+
async canRun() {
|
|
9
|
+
console.log(term.warn('CAP Jupyter Notebooks have been replaced by Custom CAP Notebooks for VS Code!'));
|
|
10
|
+
console.log(`They are now part of the CDS Editor: ${term.info('https://cap.cloud.sap/docs/tools/#add-cds-editor')}`);
|
|
11
|
+
return false;
|
|
441
12
|
}
|
|
442
13
|
}
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
+
const { exists } = require('../../../cds').utils
|
|
3
|
+
const { copyFiles } = require('../../util/templateUtil')
|
|
4
|
+
const { URLS } = require('../../constants')
|
|
5
|
+
const LOG = console
|
|
2
6
|
|
|
3
|
-
|
|
4
|
-
const { URLS } = require('../../constants');
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
module.exports = class PipelineTemplate extends TemplateBase {
|
|
7
|
+
module.exports = class PipelineTemplate extends require('../templateBase') {
|
|
8
8
|
constructor(projectPath, generator) {
|
|
9
9
|
super(projectPath, generator, __dirname);
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
async
|
|
12
|
+
async canRun() {
|
|
13
13
|
if (this.options.force) {
|
|
14
14
|
return true;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
if (
|
|
18
|
-
|
|
17
|
+
if (exists(path.join(this.projectPath, 'Jenkinsfile')) ||
|
|
18
|
+
exists(path.join(this.projectPath, '.pipeline/config.yml'))) {
|
|
19
19
|
throw new Error(`Pipeline support file exists. Use --force to overwrite.`);
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -23,10 +23,10 @@ module.exports = class PipelineTemplate extends TemplateBase {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
async run() {
|
|
26
|
-
await
|
|
26
|
+
await copyFiles(path.join(__dirname, 'files'), this.projectPath, {}, this.options.force);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
async finalize() {
|
|
30
|
-
|
|
30
|
+
LOG.info(`For information on project "Piper" and its pipelines see ${URLS.PIPELINE_HELP}`);
|
|
31
31
|
}
|
|
32
32
|
}
|