@kumologica/sdk 3.0.27 → 3.0.30
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/cli/KumologicaError.js +17 -0
- package/cli/cli.js +4 -1
- package/cli/commands/create-commands/create-project-iteratively.js +42 -0
- package/cli/commands/create-commands/index.js +5 -0
- package/cli/commands/create.js +51 -22
- package/cli/commands/list-templates.js +24 -0
- package/cli/commands/open.js +1 -1
- package/cli/commands/test-utils/TestSuiteController.js +0 -1
- package/cli/commands/test.js +3 -2
- package/cli/utils/download-template-from-repo.js +338 -0
- package/cli/utils/download-test.js +12 -0
- package/cli/utils/download.js +119 -0
- package/cli/utils/fs/copy-dir-contents-sync.js +15 -0
- package/cli/utils/fs/create-zip-file.js +39 -0
- package/cli/utils/fs/dir-exists-sync.js +14 -0
- package/cli/utils/fs/dir-exists.js +17 -0
- package/cli/utils/fs/file-exists-sync.js +14 -0
- package/cli/utils/fs/file-exists.js +12 -0
- package/cli/utils/fs/get-tmp-dir-path.js +22 -0
- package/cli/utils/fs/parse.js +40 -0
- package/cli/utils/fs/read-file-sync.js +11 -0
- package/cli/utils/fs/read-file.js +10 -0
- package/cli/utils/fs/safe-move-file.js +58 -0
- package/cli/utils/fs/walk-dir-sync.js +34 -0
- package/cli/utils/fs/write-file-sync.js +31 -0
- package/cli/utils/fs/write-file.js +32 -0
- package/cli/{commands/utils.js → utils/logger.js} +7 -1
- package/cli/utils/rename-service.js +49 -0
- package/package.json +27 -6
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class KumologicaError extends Error {
|
|
4
|
+
constructor(message, code, options = {}) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.decoratedMessage = options.decoratedMessage;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
Object.defineProperty(KumologicaError.prototype, 'name', {
|
|
12
|
+
value: KumologicaError.name,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
module.exports = KumologicaError;
|
package/cli/cli.js
CHANGED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const pathlib = require('path');
|
|
2
|
+
const { prompt, Select } = require('enquirer');
|
|
3
|
+
const { logNotice, logError } = require('../../utils/logger');
|
|
4
|
+
const { downloadTemplateFromRepo, listAvailableTemplates } = require('../../utils/download-template-from-repo');
|
|
5
|
+
|
|
6
|
+
async function createProjectIteratively(){
|
|
7
|
+
const availableTemplates = await listAvailableTemplates();
|
|
8
|
+
// Project name
|
|
9
|
+
const projectNameResponse = await prompt({
|
|
10
|
+
type: 'input',
|
|
11
|
+
name: 'projectName',
|
|
12
|
+
message: 'Enter the name for the project'
|
|
13
|
+
});
|
|
14
|
+
let aProjectName = projectNameResponse.projectName;
|
|
15
|
+
|
|
16
|
+
// Target Path
|
|
17
|
+
const directoryResponse = await prompt({
|
|
18
|
+
type: 'input',
|
|
19
|
+
name: 'directory',
|
|
20
|
+
message: 'Enter the path where the project will be created '
|
|
21
|
+
});
|
|
22
|
+
let aProjectPath = directoryResponse.directory;
|
|
23
|
+
|
|
24
|
+
// Template name
|
|
25
|
+
const promptTemplate = new Select({
|
|
26
|
+
name: 'template',
|
|
27
|
+
message: 'Pick one of the available templates',
|
|
28
|
+
choices: availableTemplates
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
let aTemplateName = await promptTemplate.run();
|
|
32
|
+
|
|
33
|
+
try{
|
|
34
|
+
await downloadTemplateFromRepo(undefined, aTemplateName, aProjectPath, aProjectName);
|
|
35
|
+
logNotice(`Successfully created project: ${pathlib.join(aProjectPath, aProjectName)}`)
|
|
36
|
+
} catch (err){
|
|
37
|
+
logError(`Error found while creating project due to: ${err.message}`)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
module.exports = createProjectIteratively;
|
package/cli/commands/create.js
CHANGED
|
@@ -1,32 +1,61 @@
|
|
|
1
1
|
const pathlib = require('path');
|
|
2
|
-
const { logNotice, logError } = require('./utils');
|
|
3
2
|
const { codegen } = require('@kumologica/builder');
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
const { logNotice, logError } = require('../utils/logger');
|
|
5
|
+
const { downloadTemplateFromRepo } = require('../utils/download-template-from-repo');
|
|
6
|
+
const { createProjectIteratively } = require('./create-commands');
|
|
7
|
+
|
|
8
|
+
exports.command = 'create [options]'
|
|
6
9
|
exports.builder = (yargs) => {
|
|
7
10
|
yargs.option(`path`, {
|
|
8
|
-
describe: "The path where project will be created
|
|
9
|
-
demandOption: "The path is required.",
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
describe: "The project path where the project will be created",
|
|
12
|
+
// demandOption: "The path is required.",
|
|
13
|
+
type: 'string',
|
|
14
|
+
alias: 'p',
|
|
15
|
+
nargs: 1
|
|
16
|
+
})
|
|
17
|
+
yargs.option(`template`, {
|
|
18
|
+
describe: `Template for the project`,
|
|
19
|
+
type: 'string',
|
|
20
|
+
alias: 't',
|
|
21
|
+
nargs: 1
|
|
22
|
+
})
|
|
14
23
|
}
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
exports.
|
|
25
|
+
|
|
26
|
+
exports.desc = 'Create a kumologica project. Do not pass any options to create iteratively.'
|
|
27
|
+
exports.handler = async ({ path, template }) => {
|
|
18
28
|
const { version } = require('../../package.json');
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
|
|
30
|
+
if (!path && !template) {
|
|
31
|
+
await createProjectIteratively();
|
|
32
|
+
} else if (!path && template) {
|
|
33
|
+
try{
|
|
34
|
+
await downloadTemplateFromRepo(undefined, template, process.cwd(), template);
|
|
35
|
+
logNotice(`Successfully created project: ${ pathlib.join(process.cwd(), template)}`);
|
|
36
|
+
}catch(e){
|
|
37
|
+
logError(e.message);
|
|
38
|
+
}
|
|
39
|
+
} else if (path && !template){
|
|
40
|
+
try{
|
|
41
|
+
codegen.generateBoilerplateProject({
|
|
42
|
+
basePath :process.cwd(),
|
|
43
|
+
projectName: path,
|
|
44
|
+
runtimeVersion: version });
|
|
45
|
+
logNotice(`Successfully created project: ${ pathlib.join(process.cwd(), path)}`);
|
|
46
|
+
}catch(e){
|
|
47
|
+
logError(e.message);
|
|
48
|
+
}
|
|
49
|
+
} else if (path && template) {
|
|
50
|
+
path = pathlib.resolve(path);
|
|
51
|
+
let projectPath = pathlib.dirname(path);
|
|
52
|
+
let projectName = pathlib.basename(path);
|
|
53
|
+
try{
|
|
54
|
+
await downloadTemplateFromRepo(undefined, template, projectPath, projectName);
|
|
55
|
+
logNotice(`Successfully created project: ${path}`);
|
|
56
|
+
}catch(e){
|
|
57
|
+
logError(e.message);
|
|
58
|
+
}
|
|
29
59
|
}
|
|
30
|
-
|
|
31
|
-
|
|
60
|
+
|
|
32
61
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const { listAvailableTemplates } = require('../utils/download-template-from-repo');
|
|
2
|
+
const { logNotice, logError, logInfo } = require('../utils/logger');
|
|
3
|
+
|
|
4
|
+
exports.command = 'list-templates'
|
|
5
|
+
exports.desc = 'List of available templates'
|
|
6
|
+
exports.handler = async function (argv) {
|
|
7
|
+
try {
|
|
8
|
+
logInfo('List of available templates:');
|
|
9
|
+
let templates = await readerFriendly(listAvailableTemplates())
|
|
10
|
+
logNotice(templates);
|
|
11
|
+
}catch(e){
|
|
12
|
+
logError(e.message);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Utils
|
|
17
|
+
async function readerFriendly(templates) {
|
|
18
|
+
let result = [];
|
|
19
|
+
templates = await templates;
|
|
20
|
+
templates.forEach(t => {
|
|
21
|
+
result.push(`"${t}"`);
|
|
22
|
+
});
|
|
23
|
+
return result.join(',')
|
|
24
|
+
}
|
package/cli/commands/open.js
CHANGED
package/cli/commands/test.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const { Select } = require('enquirer');
|
|
3
3
|
const fs = require('fs');
|
|
4
|
-
const { logError } = require('./utils');
|
|
5
4
|
const path = require('path');
|
|
6
5
|
const wcmatch = require('wildcard-match');
|
|
7
6
|
|
|
8
7
|
const { AppServer } = require('@kumologica/runtime');
|
|
9
|
-
const { TestSuiteController } = require('./test-utils/TestSuiteController');
|
|
10
8
|
const { codegen } = require('@kumologica/builder');
|
|
11
9
|
|
|
10
|
+
const { TestSuiteController } = require('./test-utils/TestSuiteController');
|
|
11
|
+
const { logError } = require('../utils/logger');
|
|
12
|
+
|
|
12
13
|
const log = console.log;
|
|
13
14
|
const APP_SERVER_PORT = 1990;
|
|
14
15
|
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const URL = require('url');
|
|
5
|
+
const BbPromise = require('bluebird');
|
|
6
|
+
const fse = require('fs-extra');
|
|
7
|
+
const qs = require('querystring');
|
|
8
|
+
const fetch = require('node-fetch');
|
|
9
|
+
const spawn = require('child-process-ext/spawn');
|
|
10
|
+
const renameService = require('./rename-service').renameService;
|
|
11
|
+
const KumologicaError = require('../KumologicaError');
|
|
12
|
+
const copyDirContentsSync = require('./fs/copy-dir-contents-sync');
|
|
13
|
+
const dirExistsSync = require('./fs/dir-exists-sync');
|
|
14
|
+
const walkDirSync = require('./fs/walk-dir-sync');
|
|
15
|
+
const { getTmpDirPath, getBaseTmpDirPath } = require('./fs/get-tmp-dir-path');
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const OFFICAL_TEMPLATES_REPO = 'https://github.com/KumologicaHQ/kumologica-templates.git';
|
|
19
|
+
/**
|
|
20
|
+
* Returns directory path
|
|
21
|
+
* @param {Number} length
|
|
22
|
+
* @param {Array} parts
|
|
23
|
+
* @returns {String} directory path
|
|
24
|
+
*/
|
|
25
|
+
function getPathDirectory(length, parts) {
|
|
26
|
+
if (!parts) {
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
return parts.slice(length).filter(Boolean).join(path.sep);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validates URL
|
|
34
|
+
* @param {Object} url
|
|
35
|
+
* @param {String} hostname
|
|
36
|
+
* @param {String} service
|
|
37
|
+
* @param {String} owner
|
|
38
|
+
* @param {String} repo
|
|
39
|
+
*/
|
|
40
|
+
function validateUrl({ url, hostname, service, owner, repo }) {
|
|
41
|
+
// validate if given url is a valid url
|
|
42
|
+
if (url.hostname !== hostname || !owner || !repo) {
|
|
43
|
+
const errorMessage = `The URL must be a valid ${service} URL in the following format: https://${hostname}/serverless/serverless`;
|
|
44
|
+
throw new KumologicaError(errorMessage, 'INVALID_TEMPLATE_URL');
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if the URL is pointing to a Git repository
|
|
50
|
+
* @param {String} url
|
|
51
|
+
*/
|
|
52
|
+
function isPlainGitURL(url) {
|
|
53
|
+
return (url.startsWith('https') || url.startsWith('git@')) && url.endsWith('.git');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {Object} url
|
|
58
|
+
* @returns {Object}
|
|
59
|
+
*/
|
|
60
|
+
function parseGitHubURL(url) {
|
|
61
|
+
const pathLength = 4;
|
|
62
|
+
const parts = url.pathname.split('/');
|
|
63
|
+
const isSubdirectory = parts.length > pathLength;
|
|
64
|
+
const owner = parts[1];
|
|
65
|
+
const repo = parts[2];
|
|
66
|
+
const branch = isSubdirectory ? parts[pathLength] : 'master';
|
|
67
|
+
const isGitHubEnterprise = url.hostname !== 'github.com';
|
|
68
|
+
|
|
69
|
+
if (!isGitHubEnterprise) {
|
|
70
|
+
// validate if given url is a valid GitHub url
|
|
71
|
+
validateUrl({ url, hostname: 'github.com', service: 'GitHub', owner, repo });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const downloadUrl = `https://${
|
|
75
|
+
isGitHubEnterprise ? url.hostname : 'github.com'
|
|
76
|
+
}/${owner}/${repo}/archive/${branch}.zip`;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
owner,
|
|
80
|
+
repo,
|
|
81
|
+
branch,
|
|
82
|
+
downloadUrl,
|
|
83
|
+
isSubdirectory,
|
|
84
|
+
pathToDirectory: getPathDirectory(pathLength + 1, parts),
|
|
85
|
+
username: url.username || '',
|
|
86
|
+
password: url.password || '',
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {Object} url
|
|
92
|
+
* @returns {Object}
|
|
93
|
+
*/
|
|
94
|
+
function parseBitbucketURL(url) {
|
|
95
|
+
const pathLength = 4;
|
|
96
|
+
const parts = url.pathname.split('/');
|
|
97
|
+
const isSubdirectory = parts.length > pathLength;
|
|
98
|
+
const owner = parts[1];
|
|
99
|
+
const repo = parts[2];
|
|
100
|
+
|
|
101
|
+
const query = qs.parse(url.query);
|
|
102
|
+
const branch = 'at' in query ? query.at : 'master';
|
|
103
|
+
|
|
104
|
+
// validate if given url is a valid Bitbucket url
|
|
105
|
+
validateUrl({ url, hostname: 'bitbucket.org', service: 'Bitbucket', owner, repo });
|
|
106
|
+
|
|
107
|
+
const downloadUrl = `https://bitbucket.org/${owner}/${repo}/get/${branch}.zip`;
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
owner,
|
|
111
|
+
repo,
|
|
112
|
+
branch,
|
|
113
|
+
downloadUrl,
|
|
114
|
+
isSubdirectory,
|
|
115
|
+
pathToDirectory: getPathDirectory(pathLength + 1, parts),
|
|
116
|
+
username: url.username || '',
|
|
117
|
+
password: url.password || '',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function parseBitbucketServerURL(url) {
|
|
122
|
+
const pathLength = 9;
|
|
123
|
+
const parts = url.pathname.split('/');
|
|
124
|
+
const isSubdirectory = parts.length > pathLength;
|
|
125
|
+
const owner = parts[5];
|
|
126
|
+
const repo = parts[7];
|
|
127
|
+
|
|
128
|
+
const query = qs.parse(url.query);
|
|
129
|
+
const branch = 'at' in query ? decodeURIComponent(query.at) : 'master';
|
|
130
|
+
|
|
131
|
+
const downloadUrl = `${url.protocol}//${url.hostname}/rest/api/latest/projects/${owner}/repos/${repo}/archive${url.search}&format=zip`;
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
owner,
|
|
135
|
+
repo,
|
|
136
|
+
branch,
|
|
137
|
+
downloadUrl,
|
|
138
|
+
isSubdirectory,
|
|
139
|
+
pathToDirectory: getPathDirectory(pathLength + 1, parts),
|
|
140
|
+
username: url.username || '',
|
|
141
|
+
password: url.password || '',
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Call `/rest/api/1.0/application-properties` to retrieve server info
|
|
147
|
+
* @param {Object} url
|
|
148
|
+
* @returns {Boolean}
|
|
149
|
+
*/
|
|
150
|
+
async function retrieveBitbucketServerInfo(url) {
|
|
151
|
+
const versionInfoPath = `${url.protocol}//${url.hostname}/rest/api/1.0/application-properties`;
|
|
152
|
+
|
|
153
|
+
return fetch(versionInfoPath)
|
|
154
|
+
.then((resp) => resp.json())
|
|
155
|
+
.then((body) => body.displayName === 'Bitbucket');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {Object} url
|
|
160
|
+
* @returns {Object}
|
|
161
|
+
*/
|
|
162
|
+
function parseGitlabURL(url) {
|
|
163
|
+
const pathLength = 4;
|
|
164
|
+
const parts = url.pathname.split('/');
|
|
165
|
+
const isSubdirectory = parts.length > pathLength;
|
|
166
|
+
const owner = parts[1];
|
|
167
|
+
const repo = parts[2];
|
|
168
|
+
|
|
169
|
+
const branch = isSubdirectory ? parts[pathLength] : 'master';
|
|
170
|
+
|
|
171
|
+
// validate if given url is a valid GitLab url
|
|
172
|
+
validateUrl({ url, hostname: 'gitlab.com', service: 'Bitbucket', owner, repo });
|
|
173
|
+
|
|
174
|
+
const downloadUrl = `https://gitlab.com/${owner}/${repo}/-/archive/${branch}/${repo}-${branch}.zip`;
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
owner,
|
|
178
|
+
repo,
|
|
179
|
+
branch,
|
|
180
|
+
downloadUrl,
|
|
181
|
+
isSubdirectory,
|
|
182
|
+
pathToDirectory: getPathDirectory(pathLength + 1, parts),
|
|
183
|
+
username: url.username || '',
|
|
184
|
+
password: url.password || '',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Parses a URL which points to a plain Git repository
|
|
190
|
+
* such as https://example.com/jdoe/project.git
|
|
191
|
+
*
|
|
192
|
+
* @param {String} url
|
|
193
|
+
* @returns {Object}
|
|
194
|
+
*/
|
|
195
|
+
function parsePlainGitURL(url) {
|
|
196
|
+
const branch = 'master';
|
|
197
|
+
const downloadUrl = url;
|
|
198
|
+
const isSubdirectory = false;
|
|
199
|
+
const repo = url.match(/.+\/(.+)\.git/)[1];
|
|
200
|
+
return {
|
|
201
|
+
repo,
|
|
202
|
+
branch,
|
|
203
|
+
downloadUrl,
|
|
204
|
+
isSubdirectory,
|
|
205
|
+
username: url.username || '',
|
|
206
|
+
password: url.password || '',
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Parse URL and call the appropriate adaptor
|
|
212
|
+
*
|
|
213
|
+
* @param {string} inputUrl
|
|
214
|
+
* @throws {KumologicaError}
|
|
215
|
+
* @returns {Promise}
|
|
216
|
+
*/
|
|
217
|
+
async function parseRepoURL(inputUrl) {
|
|
218
|
+
return new BbPromise((resolve, reject) => {
|
|
219
|
+
if (!inputUrl) {
|
|
220
|
+
return reject(new KumologicaError('URL is required', 'MISSING_TEMPLATE_URL'));
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const url = URL.parse(inputUrl.replace(/\/$/, ''));
|
|
224
|
+
if (url.auth) {
|
|
225
|
+
const [username, password] = url.auth.split(':');
|
|
226
|
+
url.username = username;
|
|
227
|
+
url.password = password;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// check if url parameter is a valid url
|
|
231
|
+
if (!url.host && !url.href.startsWith('git@')) {
|
|
232
|
+
return reject(new KumologicaError('The URL you passed is not valid', 'INVALID_TEMPLATE_URL'));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (isPlainGitURL(url.href)) {
|
|
236
|
+
return resolve(parsePlainGitURL(inputUrl));
|
|
237
|
+
} else if (url.hostname === 'github.com' || url.hostname.indexOf('github.') !== -1) {
|
|
238
|
+
return resolve(parseGitHubURL(url));
|
|
239
|
+
} else if (url.hostname === 'bitbucket.org') {
|
|
240
|
+
return resolve(parseBitbucketURL(url));
|
|
241
|
+
} else if (url.hostname === 'gitlab.com') {
|
|
242
|
+
return resolve(parseGitlabURL(url));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const msg =
|
|
246
|
+
'The URL you passed is not one of the valid providers: "GitHub", "GitHub Entreprise", "Bitbucket", "Bitbucket Server" or "GitLab".';
|
|
247
|
+
const err = new KumologicaError(msg, 'INVALID_TEMPLATE_PROVIDER');
|
|
248
|
+
// test if it's a private bitbucket server
|
|
249
|
+
return retrieveBitbucketServerInfo(url)
|
|
250
|
+
.then((isBitbucket) => {
|
|
251
|
+
if (!isBitbucket) {
|
|
252
|
+
return reject(err);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// build download URL
|
|
256
|
+
return resolve(parseBitbucketServerURL(url));
|
|
257
|
+
})
|
|
258
|
+
.catch(() => reject(err));
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Downloads the content of the template into the projectName directory
|
|
264
|
+
* @param {string} [repoUrl] - the git repository
|
|
265
|
+
* @param {string} [templateName] - the name of the project (optional), in case we are only interested in a particular directory within the repo
|
|
266
|
+
* @param {string} projectName - the local project name / directory to be created
|
|
267
|
+
* @returns {Promise}
|
|
268
|
+
*/
|
|
269
|
+
async function downloadTemplateFromRepo(repoUrl, templateName, projectPath, projectName) {
|
|
270
|
+
try{
|
|
271
|
+
repoUrl = repoUrl || OFFICAL_TEMPLATES_REPO;
|
|
272
|
+
const repoInformation = await parseRepoURL(repoUrl);
|
|
273
|
+
|
|
274
|
+
const tempBaseDirectory = getBaseTmpDirPath();
|
|
275
|
+
// clean up temp directory
|
|
276
|
+
fse.removeSync(tempBaseDirectory);
|
|
277
|
+
const tempRepoDirectory = getTmpDirPath();
|
|
278
|
+
// const { username, password } = repoInformation;
|
|
279
|
+
|
|
280
|
+
const renamed = templateName !== projectName;
|
|
281
|
+
|
|
282
|
+
// Source project directory
|
|
283
|
+
const srcProjectDir = templateName? path.join(tempRepoDirectory, templateName) : tempRepoDirectory;
|
|
284
|
+
|
|
285
|
+
// Target project directory
|
|
286
|
+
const targetProjectDir = path.join(projectPath, projectName);
|
|
287
|
+
|
|
288
|
+
if (dirExistsSync(targetProjectDir)) {
|
|
289
|
+
const errorMessage = `A project already exist in path: "${targetProjectDir}".`;
|
|
290
|
+
throw new KumologicaError(errorMessage, 'TARGET_FOLDER_ALREADY_EXISTS');
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (isPlainGitURL(repoUrl)) {
|
|
294
|
+
return spawn('git', ['clone', repoUrl, tempRepoDirectory]).then(() => {
|
|
295
|
+
try {
|
|
296
|
+
copyDirContentsSync(srcProjectDir, targetProjectDir);
|
|
297
|
+
fse.removeSync(tempBaseDirectory);
|
|
298
|
+
if (renamed) renameService(targetProjectDir, projectName);
|
|
299
|
+
}catch(err){
|
|
300
|
+
throw new KumologicaError(`Template name: "${templateName}" does not exist. Run "kl list-templates" to see list of available templates.`);
|
|
301
|
+
}
|
|
302
|
+
return "";
|
|
303
|
+
});
|
|
304
|
+
} else {
|
|
305
|
+
throw new KumologicaError(`Git repo URL is incorrect`);
|
|
306
|
+
}
|
|
307
|
+
}catch(err){
|
|
308
|
+
throw new KumologicaError(err.message);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function listAvailableTemplates() {
|
|
313
|
+
try{
|
|
314
|
+
const tempBaseDirectory = getBaseTmpDirPath();
|
|
315
|
+
const tempRepoDirectory = getTmpDirPath();
|
|
316
|
+
|
|
317
|
+
await parseRepoURL(OFFICAL_TEMPLATES_REPO);
|
|
318
|
+
|
|
319
|
+
const listTemplates = spawn('git', ['clone', OFFICAL_TEMPLATES_REPO, tempRepoDirectory]).then(() => {
|
|
320
|
+
try {
|
|
321
|
+
const projects = walkDirSync(tempRepoDirectory);
|
|
322
|
+
fse.removeSync(tempBaseDirectory);
|
|
323
|
+
return projects;
|
|
324
|
+
}catch(err){
|
|
325
|
+
throw new KumologicaError(err.message);
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
// ListTemplates: ["t1", "t2"]
|
|
329
|
+
return listTemplates;
|
|
330
|
+
} catch(err){
|
|
331
|
+
throw new KumologicaError(err.message);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
module.exports = {
|
|
336
|
+
downloadTemplateFromRepo,
|
|
337
|
+
listAvailableTemplates,
|
|
338
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
const { readerFriendly } = require('../commands/utils');
|
|
2
|
+
const { downloadTemplateFromRepo, listAvailableTemplates } = require('./download-template-from-repo');
|
|
3
|
+
|
|
4
|
+
(async()=> {
|
|
5
|
+
try{
|
|
6
|
+
// await downloadTemplateFromRepo(undefined, 'aws-api-helloworldWRONG', 'kk');
|
|
7
|
+
const result = await readerFriendly(listAvailableTemplates());
|
|
8
|
+
console.log(result);
|
|
9
|
+
} catch(err){
|
|
10
|
+
console.log('Error creating template due to: ', err.message);
|
|
11
|
+
}
|
|
12
|
+
})();
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
// This file is mostly adapted from https://github.com/kevva/download repository
|
|
2
|
+
|
|
3
|
+
// License of the original module - https://github.com/kevva/download/blob/master/license
|
|
4
|
+
|
|
5
|
+
// MIT License
|
|
6
|
+
|
|
7
|
+
// Copyright (c) Kevin Mårtensson <kevinmartensson@gmail.com> (github.com/kevva)
|
|
8
|
+
|
|
9
|
+
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
10
|
+
|
|
11
|
+
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const fsp = require('fs').promises;
|
|
18
|
+
const path = require('path');
|
|
19
|
+
const { URL } = require('url');
|
|
20
|
+
const contentDisposition = require('content-disposition');
|
|
21
|
+
const archiveType = require('archive-type');
|
|
22
|
+
const decompress = require('decompress');
|
|
23
|
+
const filenamify = require('filenamify');
|
|
24
|
+
const getStream = require('get-stream');
|
|
25
|
+
const got = require('got');
|
|
26
|
+
const makeDir = require('make-dir');
|
|
27
|
+
const pEvent = require('p-event');
|
|
28
|
+
const FileType = require('file-type');
|
|
29
|
+
const extName = require('ext-name');
|
|
30
|
+
|
|
31
|
+
const filenameFromPath = (res) => path.basename(new URL(res.requestUrl).pathname);
|
|
32
|
+
|
|
33
|
+
const getExtFromMime = (res) => {
|
|
34
|
+
const header = res.headers['content-type'];
|
|
35
|
+
|
|
36
|
+
if (!header) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const exts = extName.mime(header);
|
|
41
|
+
|
|
42
|
+
if (exts.length !== 1) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return exts[0].ext;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const getFilename = async (res, data) => {
|
|
50
|
+
const header = res.headers['content-disposition'];
|
|
51
|
+
|
|
52
|
+
if (header) {
|
|
53
|
+
const parsed = contentDisposition.parse(header);
|
|
54
|
+
|
|
55
|
+
if (parsed.parameters && parsed.parameters.filename) {
|
|
56
|
+
return parsed.parameters.filename;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let filename = filenameFromPath(res);
|
|
61
|
+
|
|
62
|
+
if (!path.extname(filename)) {
|
|
63
|
+
const ext = ((await FileType.fromBuffer(data)) || {}).ext || getExtFromMime(res);
|
|
64
|
+
|
|
65
|
+
if (ext) {
|
|
66
|
+
filename = `${filename}.${ext}`;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return filename;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
module.exports = (uri, output, opts) => {
|
|
74
|
+
if (typeof output === 'object') {
|
|
75
|
+
opts = output;
|
|
76
|
+
output = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
opts = Object.assign(
|
|
80
|
+
{
|
|
81
|
+
https: {
|
|
82
|
+
rejectUnauthorized: process.env.npm_config_strict_ssl !== 'false',
|
|
83
|
+
},
|
|
84
|
+
responseType: 'buffer',
|
|
85
|
+
},
|
|
86
|
+
opts
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const stream = got.stream(uri, opts);
|
|
90
|
+
|
|
91
|
+
const promise = pEvent(stream, 'response')
|
|
92
|
+
.then((res) => {
|
|
93
|
+
const encoding = opts.responseType === 'buffer' ? 'buffer' : opts.encoding;
|
|
94
|
+
return Promise.all([getStream(stream, { encoding }), res]);
|
|
95
|
+
})
|
|
96
|
+
.then(async (result) => {
|
|
97
|
+
const [data, res] = result;
|
|
98
|
+
|
|
99
|
+
if (!output) {
|
|
100
|
+
return opts.extract && archiveType(data) ? decompress(data, opts) : data;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const filename = opts.filename || filenamify(await getFilename(res, data));
|
|
104
|
+
const outputFilepath = path.join(output, filename);
|
|
105
|
+
|
|
106
|
+
if (opts.extract && archiveType(data)) {
|
|
107
|
+
return decompress(data, path.dirname(outputFilepath), opts);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return makeDir(path.dirname(outputFilepath))
|
|
111
|
+
.then(() => fsp.writeFile(outputFilepath, data))
|
|
112
|
+
.then(() => data);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
stream.then = promise.then.bind(promise);
|
|
116
|
+
stream.catch = promise.catch.bind(promise);
|
|
117
|
+
|
|
118
|
+
return stream;
|
|
119
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fse = require('fs-extra');
|
|
4
|
+
|
|
5
|
+
const isNotSymbolicLink = (src) => !fse.lstatSync(src).isSymbolicLink();
|
|
6
|
+
|
|
7
|
+
function copyDirContentsSync(srcDir, destDir, { noLinks = false } = {}) {
|
|
8
|
+
const copySyncOptions = {
|
|
9
|
+
dereference: true,
|
|
10
|
+
filter: noLinks ? isNotSymbolicLink : null,
|
|
11
|
+
};
|
|
12
|
+
fse.copySync(srcDir, destDir, copySyncOptions);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = copyDirContentsSync;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const archiver = require('archiver');
|
|
6
|
+
const BbPromise = require('bluebird');
|
|
7
|
+
const walkDirSync = require('./walk-dir-sync');
|
|
8
|
+
|
|
9
|
+
async function createZipFile(srcDirPath, outputFilePath) {
|
|
10
|
+
const files = walkDirSync(srcDirPath).map((file) => ({
|
|
11
|
+
input: file,
|
|
12
|
+
output: file.replace(path.join(srcDirPath, path.sep), ''),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
return new BbPromise((resolve, reject) => {
|
|
16
|
+
const output = fs.createWriteStream(outputFilePath);
|
|
17
|
+
const archive = archiver('zip', {
|
|
18
|
+
zlib: { level: 9 },
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
output.on('open', () => {
|
|
22
|
+
archive.pipe(output);
|
|
23
|
+
|
|
24
|
+
files.forEach((file) => {
|
|
25
|
+
// TODO: update since this is REALLY slow
|
|
26
|
+
if (fs.lstatSync(file.input).isFile()) {
|
|
27
|
+
archive.append(fs.createReadStream(file.input), { name: file.output });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
archive.finalize();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
archive.on('error', (err) => reject(err));
|
|
35
|
+
output.on('close', () => resolve(outputFilePath));
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = createZipFile;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fsp = require('fs').promises;
|
|
4
|
+
|
|
5
|
+
async function dirExists(path) {
|
|
6
|
+
return fsp.lstat(path).then(
|
|
7
|
+
(stats) => stats.isDirectory(),
|
|
8
|
+
(error) => {
|
|
9
|
+
if (error.code === 'ENOENT') {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = dirExists;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
function getBaseTmpDirPath() {
|
|
9
|
+
return path.join(os.tmpdir(), 'tmp-templates-kumologica');
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function getTmpDirPath() {
|
|
13
|
+
return path.join(
|
|
14
|
+
getBaseTmpDirPath(),
|
|
15
|
+
crypto.randomBytes(8).toString('hex')
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
getBaseTmpDirPath,
|
|
21
|
+
getTmpDirPath
|
|
22
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const jc = require('json-cycle');
|
|
4
|
+
const yaml = require('js-yaml');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
// const cloudformationSchema = require('@serverless/utils/cloudformation-schema');
|
|
7
|
+
|
|
8
|
+
const loadYaml = (contents, options) => {
|
|
9
|
+
let data;
|
|
10
|
+
let error;
|
|
11
|
+
try {
|
|
12
|
+
data = yaml.load(contents.toString(), options || {});
|
|
13
|
+
} catch (exception) {
|
|
14
|
+
error = exception;
|
|
15
|
+
}
|
|
16
|
+
return { data, error };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
function parse(filePath, contents) {
|
|
20
|
+
// Auto-parse JSON
|
|
21
|
+
if (filePath.endsWith('.json') || filePath.endsWith('.tfstate')) {
|
|
22
|
+
return jc.parse(contents);
|
|
23
|
+
} else if (filePath.endsWith('.yml') || filePath.endsWith('.yaml')) {
|
|
24
|
+
const options = {
|
|
25
|
+
filename: filePath,
|
|
26
|
+
};
|
|
27
|
+
let result = loadYaml(contents.toString(), options);
|
|
28
|
+
// if (result.error && result.error.name === 'YAMLException') {
|
|
29
|
+
// _.merge(options, { schema: cloudformationSchema });
|
|
30
|
+
// result = loadYaml(contents.toString(), options);
|
|
31
|
+
// }
|
|
32
|
+
if (result.error) {
|
|
33
|
+
throw result.error;
|
|
34
|
+
}
|
|
35
|
+
return result.data;
|
|
36
|
+
}
|
|
37
|
+
return contents.toString().trim();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = parse;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fsp = require('fs').promises;
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
const crypto = require('crypto');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Given a path that designates a location of a file on another device,
|
|
10
|
+
* will return a path to file in the same folder, but with a unique name
|
|
11
|
+
* to avoid collisions.
|
|
12
|
+
*
|
|
13
|
+
* @param {*} destPath the path to the final location of the file being moved
|
|
14
|
+
* @returns a unique path to a file on the same device as the file being moved
|
|
15
|
+
*/
|
|
16
|
+
const generateTemporaryPathOnDestinationDevice = (destPath) => {
|
|
17
|
+
const dirName = path.dirname(destPath);
|
|
18
|
+
// Generate a unique destination file name to get the file onto the destination filesystem
|
|
19
|
+
const tempName = path.basename(destPath) + crypto.randomBytes(8).toString('hex');
|
|
20
|
+
return path.join(dirName, tempName);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Allows a file to be moved (renamed) even across filesystem boundaries.
|
|
25
|
+
*
|
|
26
|
+
* If the rename fails because the file is getting renamed across file system boundaries,
|
|
27
|
+
* the file is first copied to the destination file system under a temporary name,
|
|
28
|
+
* and then renamed from there.
|
|
29
|
+
*
|
|
30
|
+
* This is done because rename is atomic but copy is not, and can leave partially copied files.
|
|
31
|
+
*
|
|
32
|
+
* @param {*} oldPath the original file that should be moved
|
|
33
|
+
* @param {*} newPath the path to move the file to
|
|
34
|
+
*/
|
|
35
|
+
async function safeMoveFile(oldPath, newPath) {
|
|
36
|
+
try {
|
|
37
|
+
// Golden path, we simply rename the file in an atomic operation
|
|
38
|
+
await fsp.rename(oldPath, newPath);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
// The EXDEV error indicates that the rename failed because the rename was across filesystem boundaries
|
|
41
|
+
// This might occur if a distro uses tmpfs for temporary directories
|
|
42
|
+
if (err.code === 'EXDEV') {
|
|
43
|
+
// Generate a unique destination file name to get the file onto the destination filesystem
|
|
44
|
+
const tempPath = generateTemporaryPathOnDestinationDevice(newPath);
|
|
45
|
+
|
|
46
|
+
// Copy onto the destination filesystem (not guaranteed to be atomic)
|
|
47
|
+
await fse.copy(oldPath, tempPath);
|
|
48
|
+
// Atomically move the file onto the destination path, overwriting it
|
|
49
|
+
await fsp.rename(tempPath, newPath);
|
|
50
|
+
// Delete the old file once both the above operations succeed
|
|
51
|
+
await fse.remove(oldPath);
|
|
52
|
+
} else {
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
module.exports = safeMoveFile;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
|
|
6
|
+
function walkDirSync(dirPath, opts) {
|
|
7
|
+
const options = Object.assign(
|
|
8
|
+
{
|
|
9
|
+
noLinks: false,
|
|
10
|
+
gitIgnore: true,
|
|
11
|
+
recursive: false
|
|
12
|
+
},
|
|
13
|
+
opts
|
|
14
|
+
);
|
|
15
|
+
let filePaths = [];
|
|
16
|
+
const list = fs.readdirSync(dirPath);
|
|
17
|
+
list.forEach((filePathParam) => {
|
|
18
|
+
let filePath = filePathParam;
|
|
19
|
+
filePath = path.join(dirPath, filePath);
|
|
20
|
+
const stat = options.noLinks ? fs.lstatSync(filePath) : fs.statSync(filePath);
|
|
21
|
+
// skipping symbolic links when noLinks option
|
|
22
|
+
if ((options.noLinks && stat && stat.isSymbolicLink()) || (options.gitIgnore && filePathParam === '.git')){
|
|
23
|
+
return;
|
|
24
|
+
} else if (stat && stat.isDirectory() && options.recursive) {
|
|
25
|
+
filePaths = filePaths.concat(walkDirSync(filePath, opts));
|
|
26
|
+
} else {
|
|
27
|
+
filePaths.push(filePathParam);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
return filePaths;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = walkDirSync;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fse = require('fs-extra');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const jc = require('json-cycle');
|
|
6
|
+
const yaml = require('js-yaml');
|
|
7
|
+
|
|
8
|
+
function writeFileSync(filePath, conts, cycles) {
|
|
9
|
+
let contents = conts || '';
|
|
10
|
+
|
|
11
|
+
fse.mkdirsSync(path.dirname(filePath));
|
|
12
|
+
|
|
13
|
+
if (filePath.indexOf('.json') !== -1 && typeof contents !== 'string') {
|
|
14
|
+
if (cycles) {
|
|
15
|
+
contents = jc.stringify(contents, null, 2);
|
|
16
|
+
} else {
|
|
17
|
+
contents = JSON.stringify(contents, null, 2);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const yamlFileExists = filePath.indexOf('.yaml') !== -1;
|
|
22
|
+
const ymlFileExists = filePath.indexOf('.yml') !== -1;
|
|
23
|
+
|
|
24
|
+
if ((yamlFileExists || ymlFileExists) && typeof contents !== 'string') {
|
|
25
|
+
contents = yaml.dump(contents);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return fse.writeFileSync(filePath, contents);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = writeFileSync;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fsp = require('fs').promises;
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const jc = require('json-cycle');
|
|
7
|
+
const yaml = require('js-yaml');
|
|
8
|
+
|
|
9
|
+
async function writeFile(filePath, conts, cycles) {
|
|
10
|
+
let contents = conts || '';
|
|
11
|
+
|
|
12
|
+
return fse.mkdirs(path.dirname(filePath)).then(() => {
|
|
13
|
+
if (filePath.indexOf('.json') !== -1 && typeof contents !== 'string') {
|
|
14
|
+
if (cycles) {
|
|
15
|
+
contents = jc.stringify(contents, null, 2);
|
|
16
|
+
} else {
|
|
17
|
+
contents = JSON.stringify(contents, null, 2);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const yamlFileExists = filePath.indexOf('.yaml') !== -1;
|
|
22
|
+
const ymlFileExists = filePath.indexOf('.yml') !== -1;
|
|
23
|
+
|
|
24
|
+
if ((yamlFileExists || ymlFileExists) && typeof contents !== 'string') {
|
|
25
|
+
contents = yaml.dump(contents);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return fsp.writeFile(filePath, contents);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = writeFile;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
|
|
3
|
+
/* Loggers */
|
|
3
4
|
function logError(message) {
|
|
4
5
|
console.log(chalk.red(message));
|
|
5
6
|
}
|
|
@@ -8,7 +9,12 @@ function logNotice(message) {
|
|
|
8
9
|
console.log(chalk.yellow(message));
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
function logInfo(message) {
|
|
13
|
+
console.log(message);
|
|
14
|
+
}
|
|
15
|
+
|
|
11
16
|
module.exports = {
|
|
12
17
|
logError,
|
|
13
|
-
logNotice
|
|
18
|
+
logNotice,
|
|
19
|
+
logInfo
|
|
14
20
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fse = require('fs-extra');
|
|
5
|
+
|
|
6
|
+
const fileExistsSync = require('./fs/file-exists-sync');
|
|
7
|
+
const readFileSync = require('./fs/read-file-sync');
|
|
8
|
+
const writeFileSync = require('./fs/write-file-sync');
|
|
9
|
+
const KumologicaError = require('../KumologicaError');
|
|
10
|
+
|
|
11
|
+
function renameYmlService(name, ymlServiceFile) {
|
|
12
|
+
const serverlessYml = fse
|
|
13
|
+
.readFileSync(ymlServiceFile, 'utf-8')
|
|
14
|
+
.replace(/(^|\s|#)service\s*:.+/, (ignore, prefix) => `${prefix}service: ${name}`)
|
|
15
|
+
.replace(
|
|
16
|
+
/(^|\s|#)service\s*:\s*\n(\s+)name:.+/,
|
|
17
|
+
(match, prefix, indent) => `${prefix}service:\n${indent}name: ${name}`
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
fse.writeFileSync(ymlServiceFile, serverlessYml);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function renameTsService(name, tsServicefile) {
|
|
24
|
+
const serverlessTs = fse
|
|
25
|
+
.readFileSync(tsServicefile, 'utf-8')
|
|
26
|
+
.replace(/(^|\s)service\s*:\s*('|").+('|")/, (ignore, prefix) => `${prefix}service: '${name}'`)
|
|
27
|
+
.replace(
|
|
28
|
+
/(^|\s)service\s*:\s*{\s*\n(\s+)name:\s*('|").+('|")/,
|
|
29
|
+
(match, prefix, indent) => `${prefix}service: {\n${indent}name: '${name}'`
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
fse.writeFileSync(tsServicefile, serverlessTs);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function renameService(targetProjectDir, projectName) {
|
|
36
|
+
const name = projectName;
|
|
37
|
+
const packageFile = path.join(targetProjectDir, 'package.json');
|
|
38
|
+
if (fileExistsSync(packageFile)) {
|
|
39
|
+
const json = readFileSync(packageFile);
|
|
40
|
+
writeFileSync(packageFile, Object.assign(json, { name }));
|
|
41
|
+
}
|
|
42
|
+
const packageLockFile = path.join(targetProjectDir, 'package-lock.json');
|
|
43
|
+
if (fileExistsSync(packageLockFile)) {
|
|
44
|
+
const json = readFileSync(packageLockFile);
|
|
45
|
+
writeFileSync(packageLockFile, Object.assign(json, { name }));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports.renameService = renameService;
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"productName": "Kumologica Designer",
|
|
4
4
|
"copyright": "Copyright 2020 Kumologica Pty Ltd, All Rights Reserved.",
|
|
5
5
|
"author": "Kumologica Pty Ltd <contact@kumologica.com>",
|
|
6
|
-
"version": "3.0.
|
|
6
|
+
"version": "3.0.30",
|
|
7
7
|
"description": "Kumologica Designer, harnessing Serverless for your cloud integration needs",
|
|
8
8
|
"main": "src/app/main.js",
|
|
9
9
|
"files": [
|
|
@@ -65,30 +65,41 @@
|
|
|
65
65
|
"license": "Proprietary",
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@electron/remote": "^2.0.8",
|
|
68
|
-
"@kumologica/builder": "3.0.
|
|
69
|
-
"@kumologica/devkit": "3.0.
|
|
70
|
-
"@kumologica/runtime": "3.0.
|
|
68
|
+
"@kumologica/builder": "3.0.30",
|
|
69
|
+
"@kumologica/devkit": "3.0.30",
|
|
70
|
+
"@kumologica/runtime": "3.0.30",
|
|
71
71
|
"adm-zip": "0.4.13",
|
|
72
72
|
"ajv": "8.10.0",
|
|
73
|
+
"archive-type": "^4.0.0",
|
|
74
|
+
"archiver": "^1.3.0",
|
|
73
75
|
"aws-sdk": "2.513.0",
|
|
74
76
|
"basic-auth": "2.0.1",
|
|
75
77
|
"bcryptjs": "2.4.3",
|
|
78
|
+
"bluebird": "^3.7.2",
|
|
76
79
|
"body-parser": "1.19.0",
|
|
77
80
|
"chalk": "2.4.2",
|
|
78
81
|
"child_process": "1.0.2",
|
|
82
|
+
"child-process-ext": "^2.1.1",
|
|
79
83
|
"clone": "2.1.2",
|
|
84
|
+
"content-disposition": "0.5.4",
|
|
80
85
|
"cookie-parser": "1.4.4",
|
|
81
86
|
"cors": "^2.8.5",
|
|
82
87
|
"dagre": "^0.8.5",
|
|
83
|
-
"debounce": "
|
|
88
|
+
"debounce": "1.2.1",
|
|
89
|
+
"decompress": "4.2.1",
|
|
84
90
|
"electron": "19.0.0",
|
|
85
91
|
"electron-tabs": "^1.0.1",
|
|
86
92
|
"electron-updater": "4.3.9",
|
|
87
|
-
"enquirer": "
|
|
93
|
+
"enquirer": "2.3.6",
|
|
88
94
|
"express": "4.17.3",
|
|
89
95
|
"express-session": "1.16.2",
|
|
96
|
+
"ext-name": "5.0.0",
|
|
90
97
|
"extract-json-from-string": "1.0.1",
|
|
98
|
+
"file-type": "16.5.4",
|
|
99
|
+
"filenamify": "4.0.0",
|
|
100
|
+
"fs": "^0.0.1-security",
|
|
91
101
|
"fs-extra": "10.1.0",
|
|
102
|
+
"get-stream": "^6.0.1",
|
|
92
103
|
"glob": "7.1.6",
|
|
93
104
|
"got": "11.8.2",
|
|
94
105
|
"hash-sum": "2.0.0",
|
|
@@ -96,18 +107,24 @@
|
|
|
96
107
|
"iconv-lite": "0.5.0",
|
|
97
108
|
"ini": "^2.0.0",
|
|
98
109
|
"js-yaml": "3.13.1",
|
|
110
|
+
"json-cycle": "^1.3.0",
|
|
99
111
|
"jsonata": "1.7.0",
|
|
100
112
|
"jsonpath": "1.0.2",
|
|
113
|
+
"lazystream": "^1.0.1",
|
|
114
|
+
"make-dir": "^3.1.0",
|
|
101
115
|
"memorystore": "1.6.1",
|
|
102
116
|
"mime": "2.4.4",
|
|
103
117
|
"minimist": "^1.2.3",
|
|
104
118
|
"multer": "1.4.1",
|
|
105
119
|
"mustache": "3.0.1",
|
|
120
|
+
"node-fetch": "2.6.7",
|
|
106
121
|
"oauth2orize": "1.11.0",
|
|
107
122
|
"os": "0.1.1",
|
|
123
|
+
"p-event": "4.2.0",
|
|
108
124
|
"passport": "0.4.0",
|
|
109
125
|
"passport-http-bearer": "1.0.1",
|
|
110
126
|
"passport-oauth2-client-password": "0.1.2",
|
|
127
|
+
"querystring": "^0.2.1",
|
|
111
128
|
"request": "2.88.0",
|
|
112
129
|
"rimraf": "3.0.0",
|
|
113
130
|
"shell-path": "2.1.0",
|
|
@@ -117,6 +134,7 @@
|
|
|
117
134
|
"socket.io": "2.3.0",
|
|
118
135
|
"tcp-port-used": "1.0.2",
|
|
119
136
|
"util": "0.12.1",
|
|
137
|
+
"whatwg-url": "^11.0.0",
|
|
120
138
|
"when": "3.7.8",
|
|
121
139
|
"wide-align": "^1.1.5",
|
|
122
140
|
"wildcard-match": "^5.1.2",
|
|
@@ -160,5 +178,8 @@
|
|
|
160
178
|
},
|
|
161
179
|
"engines": {
|
|
162
180
|
"node": ">=8"
|
|
181
|
+
},
|
|
182
|
+
"kumologica": {
|
|
183
|
+
"dev": false
|
|
163
184
|
}
|
|
164
185
|
}
|