@corva/create-app 0.39.0-rc.0 → 0.40.0-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/README.md +4 -0
- package/lib/constants/manifest.js +5 -0
- package/lib/flow.js +5 -4
- package/lib/flows/lib/api.js +225 -0
- package/lib/flows/rerun.js +10 -0
- package/lib/flows/steps/release-upload-zip-to-corva.js +3 -15
- package/lib/flows/steps/rerun-create-task.js +51 -0
- package/lib/flows/steps/rerun-prepare-data.js +243 -0
- package/lib/flows/steps/rerun.js +8 -0
- package/lib/flows/steps/zip-prepare.js +2 -1
- package/lib/helpers/logger.js +27 -0
- package/lib/helpers/versioning.js +3 -2
- package/lib/index.js +59 -38
- package/lib/scripts/utils/version.js +3 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ Commands:
|
|
|
26
26
|
create [options] [project-directory] Create a new app
|
|
27
27
|
zip [options] <project-directory> [patterns...] Bundle app
|
|
28
28
|
release [options] <project-directory> [patterns...] Release app
|
|
29
|
+
rerun [options] <project-directory> Rerun app
|
|
29
30
|
help [command] display help for command
|
|
30
31
|
```
|
|
31
32
|
|
|
@@ -59,6 +60,7 @@ Options:
|
|
|
59
60
|
--segments [string] Choose segments (choices: "drilling", "completion")
|
|
60
61
|
--summary [string] Enter summary (default: "More information about this app goes here")
|
|
61
62
|
--website [string] Enter website (default: "https://www.oandgexample.com/my-app/")
|
|
63
|
+
--silent [boolean] Only log result of the operation
|
|
62
64
|
-V, --version output the version number
|
|
63
65
|
-h, --help display help for command
|
|
64
66
|
-p, --packageManager [string] Please select the desired package manager (choices: "yarn", "npm", default: "yarn")
|
|
@@ -101,6 +103,7 @@ Arguments:
|
|
|
101
103
|
|
|
102
104
|
Options:
|
|
103
105
|
--bump-version <string> bump version (choices: "major", "minor", "patch", "skip")
|
|
106
|
+
--silent [boolean] Only log result of the operation
|
|
104
107
|
-h, --help display help for command
|
|
105
108
|
```
|
|
106
109
|
|
|
@@ -172,6 +175,7 @@ Arguments:
|
|
|
172
175
|
|
|
173
176
|
Options:
|
|
174
177
|
--bump-version <string> bump version (choices: "major", "minor", "patch", "skip")
|
|
178
|
+
--silent [boolean] Only log result of the operation
|
|
175
179
|
-h, --help display help for command
|
|
176
180
|
```
|
|
177
181
|
|
|
@@ -204,6 +204,11 @@ const manifestOptions = (projectName = 'Corva Dev Center App') => [
|
|
|
204
204
|
(answers.runtime && answers.runtime.startsWith(TEMPLATE_TYPES.NODE)) ||
|
|
205
205
|
answers.appType === APP_TYPES.UI,
|
|
206
206
|
},
|
|
207
|
+
{
|
|
208
|
+
name: 'silent',
|
|
209
|
+
message: 'Only log result of the operation',
|
|
210
|
+
default: false,
|
|
211
|
+
},
|
|
207
212
|
];
|
|
208
213
|
|
|
209
214
|
module.exports = {
|
package/lib/flow.js
CHANGED
|
@@ -3,17 +3,18 @@
|
|
|
3
3
|
const chalk = require('chalk');
|
|
4
4
|
const { SUCCESS_ICON } = require('./constants/messages');
|
|
5
5
|
const { resolve, sep } = require('path');
|
|
6
|
+
const logger = require('./helpers/logger');
|
|
6
7
|
|
|
7
8
|
const debug = require('debug')('cca:flow');
|
|
8
9
|
|
|
9
10
|
const runFlow = async (flow, context, indent = '') => {
|
|
10
|
-
|
|
11
|
+
logger.write(
|
|
11
12
|
`${indent}Running ${chalk.cyan(flow.name)} in ${chalk.cyan(resolve(context.dirName).split(sep).pop())}\n`
|
|
12
13
|
);
|
|
13
14
|
|
|
14
15
|
const result = await runSteps(flow.steps, context, indent + ' ');
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
logger.write(`${indent}Done!` + SUCCESS_ICON);
|
|
17
18
|
|
|
18
19
|
return result;
|
|
19
20
|
};
|
|
@@ -30,14 +31,14 @@ const runSteps = async (steps = [], context = {}, indent = '') => {
|
|
|
30
31
|
|
|
31
32
|
const message = indent + step.message;
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
logger.write(message);
|
|
34
35
|
debug('Context: %o', context);
|
|
35
36
|
|
|
36
37
|
const result = await step.fn(context);
|
|
37
38
|
|
|
38
39
|
Object.assign(context, result);
|
|
39
40
|
|
|
40
|
-
|
|
41
|
+
logger.write(SUCCESS_ICON);
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
return context;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Connect to Corva API
|
|
5
|
+
*
|
|
6
|
+
*/
|
|
7
|
+
class Api {
|
|
8
|
+
constructor(envName, apiKey, authToken = null) {
|
|
9
|
+
this.baseURL = `https://api${
|
|
10
|
+
envName === 'production' ? '' : `.${envName}`
|
|
11
|
+
}.corva.ai`;
|
|
12
|
+
|
|
13
|
+
this.config = {
|
|
14
|
+
headers: {
|
|
15
|
+
'Authorization': authToken ? `Bearer ${authToken}` :`API ${apiKey}`,
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
'Accept': 'application/json',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get api url
|
|
24
|
+
*
|
|
25
|
+
* @private
|
|
26
|
+
*
|
|
27
|
+
* @param {string} path
|
|
28
|
+
*
|
|
29
|
+
* @returns {string}
|
|
30
|
+
*/
|
|
31
|
+
getUrl(path) {
|
|
32
|
+
return `${this.baseURL}${path}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Send api request
|
|
37
|
+
*
|
|
38
|
+
* @private
|
|
39
|
+
*
|
|
40
|
+
* @param {string} path
|
|
41
|
+
* @param {string} method
|
|
42
|
+
* @param {object} data
|
|
43
|
+
*
|
|
44
|
+
* @returns {object}
|
|
45
|
+
*/
|
|
46
|
+
sendRequest(method, url, data = null) {
|
|
47
|
+
return data ? axios[method](url, data, this.config) : axios[method](url, this.config);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get app by appKey
|
|
52
|
+
*
|
|
53
|
+
* @public
|
|
54
|
+
*
|
|
55
|
+
* @param {string} appKey
|
|
56
|
+
*
|
|
57
|
+
* @returns {object}
|
|
58
|
+
*/
|
|
59
|
+
async getAppByKey(appKey) {
|
|
60
|
+
const url = this.getUrl(`/v2/apps?per_page=2&search=${appKey}`);
|
|
61
|
+
const response = await this.sendRequest('get', url);
|
|
62
|
+
|
|
63
|
+
const app = response.data.data[0];
|
|
64
|
+
if (!app || app.attributes.app_key != appKey) {
|
|
65
|
+
throw new Error(`App with key - ${appKey}, not exist`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return app;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Get app datasets
|
|
73
|
+
*
|
|
74
|
+
* @public
|
|
75
|
+
*
|
|
76
|
+
* @param {string} appId
|
|
77
|
+
*
|
|
78
|
+
* @returns {object}
|
|
79
|
+
*/
|
|
80
|
+
async getAppDatasetsOperationWrite(appId) {
|
|
81
|
+
const url = this.getUrl(
|
|
82
|
+
`/v2/apps/${appId}/app_datasets?dataset_operation=write&fields[]=app_dataset.dataset_name`
|
|
83
|
+
);
|
|
84
|
+
const response = await this.sendRequest('get', url);
|
|
85
|
+
|
|
86
|
+
if (!response.data.data.length) {
|
|
87
|
+
throw new Error(`App with ID - ${appId}, has not any datasets`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return response.data.data;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Get asset details
|
|
95
|
+
*
|
|
96
|
+
* @public
|
|
97
|
+
*
|
|
98
|
+
* @param {string} assetId
|
|
99
|
+
*
|
|
100
|
+
* @returns {object[]}
|
|
101
|
+
*/
|
|
102
|
+
async getAssetById(assetId) {
|
|
103
|
+
const url = this.getUrl(`/v2/assets/${assetId}`);
|
|
104
|
+
const response = await this.sendRequest('get', url);
|
|
105
|
+
|
|
106
|
+
return response.data.data;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get asset streams
|
|
112
|
+
*
|
|
113
|
+
* @public
|
|
114
|
+
*
|
|
115
|
+
* @param {string} assetId
|
|
116
|
+
*
|
|
117
|
+
* @returns {object[]}
|
|
118
|
+
*/
|
|
119
|
+
async getStreamByAssetId(assetId) {
|
|
120
|
+
const url = this.getUrl(`/v1/app_streams?asset_id=${assetId}&segment=drilling&status=complete`);
|
|
121
|
+
const response = await this.sendRequest('get', url);
|
|
122
|
+
|
|
123
|
+
if (!response.data[0]) {
|
|
124
|
+
throw new Error(`Could not found streams for asset ID - ${assetId}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return response.data;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get wells data by asset id
|
|
132
|
+
*
|
|
133
|
+
* @public
|
|
134
|
+
*
|
|
135
|
+
* @param {string} assetId
|
|
136
|
+
*
|
|
137
|
+
* @returns {object[]}
|
|
138
|
+
*/
|
|
139
|
+
async getWellByAssetId(assetId) {
|
|
140
|
+
const data = {
|
|
141
|
+
assets: [assetId],
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const url = this.getUrl(`/v2/assets/resolve`);
|
|
145
|
+
const response = await this.sendRequest('post', url, data);
|
|
146
|
+
|
|
147
|
+
if (!response.data.wells.length) {
|
|
148
|
+
throw new Error(`Could not found wells by asset ID - ${assetId}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return response.data.wells;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Queue app run for create new task
|
|
156
|
+
*
|
|
157
|
+
* @public
|
|
158
|
+
*
|
|
159
|
+
* @param {string} appId
|
|
160
|
+
* @param {string} version
|
|
161
|
+
* @param {string} wellId
|
|
162
|
+
* @param {string[]} appDatasetsNames
|
|
163
|
+
* @param {string} streamId
|
|
164
|
+
*
|
|
165
|
+
* @returns {object}
|
|
166
|
+
*/
|
|
167
|
+
async queueAppRun(appId, version, wellId, appDatasetsNames, streamId) {
|
|
168
|
+
const data = {
|
|
169
|
+
app_run: {
|
|
170
|
+
app_stream_id: streamId,
|
|
171
|
+
well_id: wellId,
|
|
172
|
+
app_version: version,
|
|
173
|
+
datasets: appDatasetsNames,
|
|
174
|
+
settings: {
|
|
175
|
+
end: 0,
|
|
176
|
+
interval: null,
|
|
177
|
+
invokes: null,
|
|
178
|
+
records_per_event: 300,
|
|
179
|
+
start: 0,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const url = this.getUrl(`/v2/apps/${appId}/app_runs`);
|
|
185
|
+
const response = await this.sendRequest('post', url, data);
|
|
186
|
+
return response.data.data;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Get all active(status - pending or in_progress or running) runs by app ID
|
|
191
|
+
*
|
|
192
|
+
* @public
|
|
193
|
+
*
|
|
194
|
+
* @param {string} appId
|
|
195
|
+
*
|
|
196
|
+
* @returns {object[]}
|
|
197
|
+
*/
|
|
198
|
+
async getAppRuns(appId) {
|
|
199
|
+
const url = this.getUrl(`/v2/apps/${appId}/app_runs?page=1&per_page=500&status[]=pending&status[]=in_progress&status[]=running`);
|
|
200
|
+
const response = await this.sendRequest('get', url);
|
|
201
|
+
|
|
202
|
+
return response.data.data;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Upload packages
|
|
207
|
+
*
|
|
208
|
+
* @public
|
|
209
|
+
*
|
|
210
|
+
* @param {string} appKey
|
|
211
|
+
* @param {object} formData
|
|
212
|
+
*
|
|
213
|
+
* @returns
|
|
214
|
+
*/
|
|
215
|
+
async uploadPackages(appKey, formData) {
|
|
216
|
+
const url = this.getUrl(`/v2/apps/${appKey}/packages/upload`);
|
|
217
|
+
this.config.headers = {
|
|
218
|
+
...this.config.headers,
|
|
219
|
+
...formData.getHeaders(),
|
|
220
|
+
};
|
|
221
|
+
return this.sendRequest('post', url, formData)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
module.exports = { Api };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const { PREPARE_FLOW } = require('./prepare');
|
|
2
|
+
const { RERUN_STEPS } = require('./steps/rerun');
|
|
3
|
+
const { GET_RELEASE_CONFIG_STEP } = require('./steps/release-get-config');
|
|
4
|
+
|
|
5
|
+
const RERUN_FLOW = {
|
|
6
|
+
name: 'rerun',
|
|
7
|
+
steps: [PREPARE_FLOW, GET_RELEASE_CONFIG_STEP, ...RERUN_STEPS],
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
module.exports = { RERUN_FLOW };
|
|
@@ -1,31 +1,19 @@
|
|
|
1
1
|
const { RELEASE } = require('../../constants/messages');
|
|
2
|
-
const axios = require('axios');
|
|
3
2
|
const FormData = require('form-data');
|
|
4
3
|
const { resolve } = require('path');
|
|
5
4
|
const { createReadStream } = require('fs');
|
|
6
5
|
const { StepError } = require('../lib/step-error');
|
|
6
|
+
const { Api } = require('../lib/api');
|
|
7
7
|
const { get } = require('lodash/fp');
|
|
8
8
|
|
|
9
9
|
const UPLOAD_ZIP_TO_CORVA_STEP = {
|
|
10
10
|
message: RELEASE.uploadApp,
|
|
11
11
|
fn: async ({ zipFileName, appKey, CORVA_API_ENV, AUTH_TOKEN, API_KEY, dirName }) => {
|
|
12
12
|
const form = new FormData();
|
|
13
|
-
|
|
14
13
|
form.append('package', createReadStream(resolve(dirName, zipFileName)), 'archive.zip');
|
|
15
14
|
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
}.corva.ai`;
|
|
19
|
-
const uploadURL = `${baseURL}/v2/apps/${appKey}/packages/upload`;
|
|
20
|
-
|
|
21
|
-
await axios
|
|
22
|
-
.post(uploadURL, form, {
|
|
23
|
-
headers: {
|
|
24
|
-
...form.getHeaders(),
|
|
25
|
-
Authorization: AUTH_TOKEN ? `Bearer ${AUTH_TOKEN}` : `API ${API_KEY}`,
|
|
26
|
-
},
|
|
27
|
-
})
|
|
28
|
-
.catch((e) => {
|
|
15
|
+
const api = new Api(CORVA_API_ENV, API_KEY, AUTH_TOKEN);
|
|
16
|
+
api.uploadPackages(appKey, form).catch((e) => {
|
|
29
17
|
throw new StepError(
|
|
30
18
|
`${get('response.data.message', e) || ''} \nPOST: ${uploadURL} failed.`,
|
|
31
19
|
{ cause: e }
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
|
|
4
|
+
const CREATE_TASK_STEP = {
|
|
5
|
+
message: 'Creating tasks...',
|
|
6
|
+
fn: async ({
|
|
7
|
+
appId,
|
|
8
|
+
assets,
|
|
9
|
+
appDatasetsNames,
|
|
10
|
+
mappedAssetsToStreams,
|
|
11
|
+
mappedAssetsToWells,
|
|
12
|
+
api,
|
|
13
|
+
version,
|
|
14
|
+
CORVA_API_ENV,
|
|
15
|
+
}) => {
|
|
16
|
+
if (!assets.length) {
|
|
17
|
+
process.stdout.write(
|
|
18
|
+
`\n\n${chalk.yellow.bold('There is not a asset ID to create new task')}`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
for (const assetId of assets) {
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const result = await api.queueAppRun(
|
|
26
|
+
appId,
|
|
27
|
+
version,
|
|
28
|
+
mappedAssetsToWells.get(parseInt(assetId)),
|
|
29
|
+
appDatasetsNames,
|
|
30
|
+
mappedAssetsToStreams.get(parseInt(assetId))
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
process.stdout.write(
|
|
34
|
+
`\n Created new task with ID ${chalk.yellow(result.id)} for asset ID - ${chalk.green(
|
|
35
|
+
assetId
|
|
36
|
+
)}`
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
process.stdout.write(
|
|
40
|
+
`\n Task link - https://app${CORVA_API_ENV === 'production' ? '.' : `.${CORVA_API_ENV}.`}corva.ai/dev-center/apps/${appId}/runner`
|
|
41
|
+
);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
process.stdout.write(
|
|
44
|
+
`\n\n${chalk.red.underline.bold(`Could not rerun app for asset ID - ${assetId}, an error occured: ${e.message}`)}\n\n`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
module.exports = { CREATE_TASK_STEP };
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
const _ = require('lodash');
|
|
2
|
+
const chalk = require('chalk');
|
|
3
|
+
const inquirer = require('inquirer');
|
|
4
|
+
const { Api } = require('../lib/api');
|
|
5
|
+
const { StepError } = require('../lib/step-error');
|
|
6
|
+
|
|
7
|
+
const MAX_ASSET_IDS_COUNT = 10;
|
|
8
|
+
|
|
9
|
+
const PREPARE_DATA_STEP = {
|
|
10
|
+
message: 'Preparing data for tasks...',
|
|
11
|
+
fn: async (context) => {
|
|
12
|
+
const { manifest, patterns, API_KEY, CORVA_API_ENV, pkg } = context;
|
|
13
|
+
let { assets } = patterns;
|
|
14
|
+
|
|
15
|
+
assets = _.uniq(assets);
|
|
16
|
+
|
|
17
|
+
if (assets.length > MAX_ASSET_IDS_COUNT) {
|
|
18
|
+
throw new StepError(`Please no more than ${MAX_ASSET_IDS_COUNT} asset ids!`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!['scheduler', 'stream'].includes(manifest.manifest.application.type)) {
|
|
22
|
+
throw new StepError('Rerun command supports only "scheduler" or "stream" apps');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!await promptAreYouSure()) {
|
|
26
|
+
throw new StepError('Command stopped');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const api = new Api(CORVA_API_ENV, API_KEY);
|
|
30
|
+
|
|
31
|
+
const app = await api.getAppByKey(manifest.manifest.application.key.toLowerCase());
|
|
32
|
+
const appDatasets = await api.getAppDatasetsOperationWrite(app.id);
|
|
33
|
+
const appDatasetsNames = appDatasets.map((dataset) => dataset.attributes.dataset_name);
|
|
34
|
+
|
|
35
|
+
const existingAppRuns = await getExistAppRuns(app.id, assets, appDatasetsNames, api);
|
|
36
|
+
if (existingAppRuns.size) {
|
|
37
|
+
for (let appRunAssetId of existingAppRuns.keys()) {
|
|
38
|
+
// remove asset ID if similar rerun already exists
|
|
39
|
+
assets = assets.filter((assetId) => appRunAssetId != assetId);
|
|
40
|
+
|
|
41
|
+
const run = existingAppRuns.get(appRunAssetId);
|
|
42
|
+
|
|
43
|
+
process.stdout.write(
|
|
44
|
+
`\n\n${chalk.yellow.bold(`Similar rerun with ID ${run.id}, for asset ID ${appRunAssetId} - already exist. Will be skipped!`)}`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const {mappedAssetsToWells, mappedAssetsToStreams, assetsToDelete} = await prepareWellAndStreamData(assets, api);
|
|
50
|
+
|
|
51
|
+
if (assetsToDelete.length) {
|
|
52
|
+
// remove asset ID if could not found stream or well
|
|
53
|
+
assets = assets.filter((assetId) => !assetsToDelete.includes(assetId));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
appId: app.id,
|
|
58
|
+
assets,
|
|
59
|
+
appDatasetsNames,
|
|
60
|
+
mappedAssetsToStreams,
|
|
61
|
+
mappedAssetsToWells,
|
|
62
|
+
api,
|
|
63
|
+
CORVA_API_ENV,
|
|
64
|
+
version: pkg.version,
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* CLI prompt question - Are you sure you want to do this?
|
|
71
|
+
*
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
74
|
+
const promptAreYouSure = async () => {
|
|
75
|
+
const answers = await inquirer
|
|
76
|
+
.prompt([
|
|
77
|
+
{
|
|
78
|
+
message:
|
|
79
|
+
'\n This command will create additional load on the server, which may take a long time. \n Are you sure you want to do this?',
|
|
80
|
+
name: 'option',
|
|
81
|
+
type: 'list',
|
|
82
|
+
choices: [
|
|
83
|
+
{ value: true, name: 'Yes' },
|
|
84
|
+
{ value: false, name: 'No' },
|
|
85
|
+
],
|
|
86
|
+
default: false,
|
|
87
|
+
},
|
|
88
|
+
]);
|
|
89
|
+
return answers.option;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* CLI prompt question - Please choose the stream
|
|
94
|
+
*
|
|
95
|
+
* @param {object[]} streams
|
|
96
|
+
*
|
|
97
|
+
* @returns
|
|
98
|
+
*/
|
|
99
|
+
const getStreamWithPrompt = async (streams) => {
|
|
100
|
+
let stream = {};
|
|
101
|
+
|
|
102
|
+
const choices = streams.map((stream) => {
|
|
103
|
+
return {
|
|
104
|
+
value: stream,
|
|
105
|
+
name: stream.name
|
|
106
|
+
};
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
await inquirer
|
|
110
|
+
.prompt([
|
|
111
|
+
{
|
|
112
|
+
message:
|
|
113
|
+
'\n Please choose stream?',
|
|
114
|
+
name: 'option',
|
|
115
|
+
type: 'list',
|
|
116
|
+
choices,
|
|
117
|
+
},
|
|
118
|
+
])
|
|
119
|
+
.then((answers) => {
|
|
120
|
+
stream = answers.option;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return stream;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* CLI prompt question - Please choose the well
|
|
128
|
+
*
|
|
129
|
+
* @param {object[]} wells
|
|
130
|
+
* @param {object} api
|
|
131
|
+
*
|
|
132
|
+
* @returns
|
|
133
|
+
*/
|
|
134
|
+
const getWellWithPrompt = async (wells, api) => {
|
|
135
|
+
let well = {};
|
|
136
|
+
|
|
137
|
+
const choices = await Promise.all(wells.map( async (well) => {
|
|
138
|
+
const assetDetails = await api.getAssetById(well.data.attributes.asset_id);
|
|
139
|
+
well.data.name = assetDetails.attributes.name;
|
|
140
|
+
return {
|
|
141
|
+
value: well.data,
|
|
142
|
+
name: well.data.name,
|
|
143
|
+
};
|
|
144
|
+
}));
|
|
145
|
+
|
|
146
|
+
await inquirer
|
|
147
|
+
.prompt([
|
|
148
|
+
{
|
|
149
|
+
message:
|
|
150
|
+
'\n Please choose the well?',
|
|
151
|
+
name: 'option',
|
|
152
|
+
type: 'list',
|
|
153
|
+
choices,
|
|
154
|
+
},
|
|
155
|
+
])
|
|
156
|
+
.then((answers) => {
|
|
157
|
+
well = answers.option;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return well;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Get stream and well data for assets
|
|
166
|
+
*
|
|
167
|
+
* @param {string[]} assets
|
|
168
|
+
* @param {object} api
|
|
169
|
+
*
|
|
170
|
+
* @returns {object}
|
|
171
|
+
*/
|
|
172
|
+
const prepareWellAndStreamData = async (assets, api) => {
|
|
173
|
+
const mappedAssetsToWells = new Map();
|
|
174
|
+
const mappedAssetsToStreams = new Map();
|
|
175
|
+
const assetsToDelete = [];
|
|
176
|
+
|
|
177
|
+
for (const assetId of assets) {
|
|
178
|
+
try {
|
|
179
|
+
process.stdout.write(
|
|
180
|
+
`\n\n${chalk.black.underline.bold(`Process asset ID - ${chalk.green(assetId)}`)}`
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
process.stdout.write(
|
|
184
|
+
'\n Loading wells...'
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
const wells = await api.getWellByAssetId(assetId);
|
|
188
|
+
const well = await getWellWithPrompt(wells, api);
|
|
189
|
+
mappedAssetsToWells.set(well.attributes.asset_id, well.id);
|
|
190
|
+
|
|
191
|
+
process.stdout.write(
|
|
192
|
+
'\n Loading streams...'
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const streams = await api.getStreamByAssetId(assetId);
|
|
196
|
+
const stream = await getStreamWithPrompt(streams);
|
|
197
|
+
mappedAssetsToStreams.set(stream.asset_id, stream.id);
|
|
198
|
+
} catch (e) {
|
|
199
|
+
process.stdout.write(
|
|
200
|
+
`\n\n${chalk.red.underline.bold(`Skipped the asset ID - ${assetId}, an error occured: ${e.message}`)}`
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
assetsToDelete.push(assetId);
|
|
204
|
+
|
|
205
|
+
if (mappedAssetsToWells.has(assetId)) mappedAssetsToWells.delete(assetId);
|
|
206
|
+
if (mappedAssetsToStreams.has(assetId)) mappedAssetsToStreams.delete(assetId);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {mappedAssetsToWells, mappedAssetsToStreams, assetsToDelete};
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Check if current runs already exist
|
|
215
|
+
*
|
|
216
|
+
* @param {string} appId
|
|
217
|
+
* @param {string[]} assets
|
|
218
|
+
* @param {string[]} appDatasetsNames
|
|
219
|
+
* @param {object} api
|
|
220
|
+
*
|
|
221
|
+
* @returns {map}
|
|
222
|
+
*/
|
|
223
|
+
const getExistAppRuns = async (appId, assets, appDatasetsNames, api) => {
|
|
224
|
+
const appRuns = new Map();
|
|
225
|
+
const existingAppRuns = await api.getAppRuns(appId);
|
|
226
|
+
|
|
227
|
+
// go through all assets ids
|
|
228
|
+
for (const assetId of assets) {
|
|
229
|
+
const runsForCurrentAsset = existingAppRuns.filter((run) => run.attributes.well_asset_id == assetId);
|
|
230
|
+
// go through all existing runs for current asset ID
|
|
231
|
+
for (const run of runsForCurrentAsset) {
|
|
232
|
+
const currentAppDatasetsNames = run.attributes.app_run_datasets.map((dataset) => dataset.name);
|
|
233
|
+
// if datasets names matched for the same asset ID - then run already exist
|
|
234
|
+
if (appDatasetsNames.sort().toString() === currentAppDatasetsNames.sort().toString()) {
|
|
235
|
+
appRuns.set(assetId, run);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return appRuns;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
module.exports = { PREPARE_DATA_STEP };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { saveJson } = require('../lib/json');
|
|
2
|
+
const logger = require('../../helpers/logger');
|
|
2
3
|
|
|
3
4
|
const debug = require('debug')('cca:flow:zip:prepare');
|
|
4
5
|
|
|
@@ -10,7 +11,7 @@ const PREPARE_FILES_BEFORE_ZIP_STEP = {
|
|
|
10
11
|
await saveJson(dirName, item.name, item.content);
|
|
11
12
|
|
|
12
13
|
if (item.message) {
|
|
13
|
-
|
|
14
|
+
logger.write(item.message);
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
},
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class Logger {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.isSilent = process.argv.includes('--silent');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
write(str) {
|
|
7
|
+
if (this.isSilent) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
process.stdout.write(str);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
writeDirectlyToConsole(str) {
|
|
15
|
+
process.stdout.write(str);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
log(str) {
|
|
19
|
+
if (this.isSilent) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(str);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = new Logger();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const execSync = require('child_process').execSync;
|
|
2
|
+
const logger = require('./logger');
|
|
2
3
|
|
|
3
4
|
function isInGitRepository(appPath) {
|
|
4
5
|
try {
|
|
@@ -20,7 +21,7 @@ function tryGitInit(appPath) {
|
|
|
20
21
|
execSync('git --version', { stdio: 'ignore', cwd: appPath });
|
|
21
22
|
|
|
22
23
|
execSync('git init', { stdio: 'ignore', cwd: appPath });
|
|
23
|
-
|
|
24
|
+
logger.log('Initialized git repo in app');
|
|
24
25
|
return true;
|
|
25
26
|
} catch (e) {
|
|
26
27
|
console.warn('Git repo not initialized', e);
|
|
@@ -35,7 +36,7 @@ function tryGitCommit(appPath) {
|
|
|
35
36
|
stdio: 'ignore',
|
|
36
37
|
cwd: appPath,
|
|
37
38
|
});
|
|
38
|
-
|
|
39
|
+
logger.log('Added first git commit');
|
|
39
40
|
return true;
|
|
40
41
|
} catch (e) {
|
|
41
42
|
// We couldn't commit in already initialized git repo,
|
package/lib/index.js
CHANGED
|
@@ -30,11 +30,13 @@ const { runFlow } = require('./flow');
|
|
|
30
30
|
const { ERROR_ICON } = require('./constants/messages');
|
|
31
31
|
const { StepError } = require('./flows/lib/step-error');
|
|
32
32
|
const { RELEASE_FLOW } = require('./flows/release');
|
|
33
|
+
const { RERUN_FLOW } = require('./flows/rerun');
|
|
33
34
|
const { ZIP_FLOW } = require('./flows/zip');
|
|
34
35
|
const { bumpVersionOptionDeprecated, bumpVersionOption } = require('./bump-version.option');
|
|
35
36
|
const { Manifest } = require('./flows/lib/manifest');
|
|
36
37
|
const { resolveAppRuntime } = require('./helpers/resolve-app-runtime');
|
|
37
38
|
const { existsSync } = require('fs');
|
|
39
|
+
const logger = require('./helpers/logger');
|
|
38
40
|
|
|
39
41
|
const writejsonOptions = {
|
|
40
42
|
spaces: 2,
|
|
@@ -48,11 +50,11 @@ function startingMessage() {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
function checkNodeVersion() {
|
|
51
|
-
|
|
53
|
+
logger.write('Checking node version...');
|
|
52
54
|
|
|
53
55
|
const unsupportedNodeVersion = !semver.satisfies(process.version, '>=16');
|
|
54
56
|
if (unsupportedNodeVersion) {
|
|
55
|
-
|
|
57
|
+
logger.log(
|
|
56
58
|
chalk.red(
|
|
57
59
|
`\nYou are using Node ${process.version}.\n\n` +
|
|
58
60
|
`Please update to Node 16 or higher for a better, fully supported experience.\n`
|
|
@@ -61,7 +63,7 @@ function checkNodeVersion() {
|
|
|
61
63
|
// Fall back to latest supported react-scripts on Node 4
|
|
62
64
|
return process.exit(1);
|
|
63
65
|
}
|
|
64
|
-
|
|
66
|
+
logger.write(' ✅ \n');
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
function checkOptions(opts) {
|
|
@@ -179,9 +181,14 @@ async function initialChecks() {
|
|
|
179
181
|
.argument('[patterns...]', 'Additional patterns to zip', [])
|
|
180
182
|
.addOption(bumpVersionOption)
|
|
181
183
|
.addOption(new Option('--ignored-files [ignoredFiles...]', 'Patterns to skip zip', []))
|
|
184
|
+
.addOption(new Option('--silent [boolean]', 'Only log result of the operation', []))
|
|
182
185
|
.action(async (dirName, patterns, options) => {
|
|
183
186
|
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
184
187
|
|
|
188
|
+
if (options.silent) {
|
|
189
|
+
console.log(dirName);
|
|
190
|
+
}
|
|
191
|
+
|
|
185
192
|
return runFlow(ZIP_FLOW, { dirName, patterns, options });
|
|
186
193
|
});
|
|
187
194
|
|
|
@@ -192,12 +199,26 @@ async function initialChecks() {
|
|
|
192
199
|
.argument('[patterns...]', 'Additional patterns to zip', [])
|
|
193
200
|
.addOption(bumpVersionOption)
|
|
194
201
|
.addOption(new Option('--ignored-files [ignoredFiles...]', 'Patterns to skip zip', []))
|
|
202
|
+
.addOption(new Option('--silent [boolean]', 'Only log result of the operation', []))
|
|
195
203
|
.action(async (dirName, patterns, options) => {
|
|
196
204
|
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
197
205
|
|
|
206
|
+
if (options.silent) {
|
|
207
|
+
console.log(dirName);
|
|
208
|
+
}
|
|
209
|
+
|
|
198
210
|
return runFlow(RELEASE_FLOW, { dirName, patterns, options });
|
|
199
211
|
});
|
|
200
212
|
|
|
213
|
+
program
|
|
214
|
+
.command('rerun')
|
|
215
|
+
.description('Rerun app')
|
|
216
|
+
.argument('<project-directory>', 'Project directory to work with')
|
|
217
|
+
.addOption(new Option('--assets [assets...]', 'Assets ids list', []))
|
|
218
|
+
.action(async (dirName, patterns, options) => {
|
|
219
|
+
return runFlow(RERUN_FLOW, { dirName, patterns, options });
|
|
220
|
+
});
|
|
221
|
+
|
|
201
222
|
try {
|
|
202
223
|
await program.parseAsync(process.argv);
|
|
203
224
|
} catch (e) {
|
|
@@ -214,7 +235,7 @@ const handleError = (e) => {
|
|
|
214
235
|
return;
|
|
215
236
|
}
|
|
216
237
|
|
|
217
|
-
|
|
238
|
+
logger.write(ERROR_ICON);
|
|
218
239
|
|
|
219
240
|
if (!(e instanceof StepError)) {
|
|
220
241
|
console.error(chalk.red(e));
|
|
@@ -235,16 +256,16 @@ const handleCommanderError = (e) => {
|
|
|
235
256
|
const commandName = program.args[0] || program._defaultCommandName;
|
|
236
257
|
|
|
237
258
|
console.error('Please specify the project directory:');
|
|
238
|
-
|
|
259
|
+
logger.log(
|
|
239
260
|
` ${chalk.cyan(program.name())} ${commandName} ${chalk.green('<project-directory>')}`
|
|
240
261
|
);
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
262
|
+
logger.log();
|
|
263
|
+
logger.log('For example:');
|
|
264
|
+
logger.log(
|
|
244
265
|
` ${chalk.cyan(program.name())} ${commandName} ${chalk.green('my-react-app')}`
|
|
245
266
|
);
|
|
246
|
-
|
|
247
|
-
|
|
267
|
+
logger.log();
|
|
268
|
+
logger.log(
|
|
248
269
|
`Run ${chalk.cyan(`${program.name()} help ${commandName}`)} to see all options.`
|
|
249
270
|
);
|
|
250
271
|
} else {
|
|
@@ -278,7 +299,7 @@ async function initPackage(projectName, opts) {
|
|
|
278
299
|
|
|
279
300
|
const root = path.resolve(projectName);
|
|
280
301
|
|
|
281
|
-
|
|
302
|
+
logger.log(`Creating a new Corva app in ${chalk.green(root)}.`);
|
|
282
303
|
|
|
283
304
|
if (fs.existsSync(root)) {
|
|
284
305
|
throw new Error(`Directory already exists: ${root}`);
|
|
@@ -290,7 +311,7 @@ async function initPackage(projectName, opts) {
|
|
|
290
311
|
await configureApp(root, manifest, runtime);
|
|
291
312
|
await installApp(root, manifest, runtime);
|
|
292
313
|
|
|
293
|
-
|
|
314
|
+
logger.log();
|
|
294
315
|
}
|
|
295
316
|
|
|
296
317
|
async function createApp(dirName, opts) {
|
|
@@ -298,7 +319,7 @@ async function createApp(dirName, opts) {
|
|
|
298
319
|
|
|
299
320
|
if (isValid) {
|
|
300
321
|
Object.keys(values).forEach((key) => {
|
|
301
|
-
|
|
322
|
+
logger.log(`${key} : ${values[key]}`);
|
|
302
323
|
});
|
|
303
324
|
|
|
304
325
|
return initPackage(dirName, opts);
|
|
@@ -318,8 +339,8 @@ async function createApp(dirName, opts) {
|
|
|
318
339
|
* @param {*} runtime
|
|
319
340
|
*/
|
|
320
341
|
async function addTemplate(root, manifest, runtime) {
|
|
321
|
-
|
|
322
|
-
|
|
342
|
+
logger.log(chalk.green('Copying app template...'));
|
|
343
|
+
logger.log();
|
|
323
344
|
|
|
324
345
|
const templateFolder = path.resolve(
|
|
325
346
|
__dirname,
|
|
@@ -344,7 +365,7 @@ async function addTemplate(root, manifest, runtime) {
|
|
|
344
365
|
// That's why we manually rename gitignore to .gitignore after copying template
|
|
345
366
|
fs.renameSync(path.join(root, 'gitignore'), path.join(root, '.gitignore'));
|
|
346
367
|
|
|
347
|
-
|
|
368
|
+
logger.log(chalk.green('Done: copying app template!'));
|
|
348
369
|
}
|
|
349
370
|
|
|
350
371
|
/**
|
|
@@ -461,7 +482,7 @@ async function installApp(root, manifest, runtime) {
|
|
|
461
482
|
const args = ['install'];
|
|
462
483
|
const opts = { stdio: ['inherit', 'inherit', 'pipe'], cwd: root }
|
|
463
484
|
|
|
464
|
-
|
|
485
|
+
logger.log(chalk.yellow(`Installing template dependencies using ${runtime.packageManager}...`));
|
|
465
486
|
const proc = manifest.isJs() && existsSync(`${os.homedir()}/.nvm/nvm.sh`) ?
|
|
466
487
|
spawn.sync(`\\. ${os.homedir()}/.nvm/nvm.sh && nvm i && ${command} ${args.join(' ')}`, { shell: true, ...opts }) :
|
|
467
488
|
spawn.sync(command, args, opts);
|
|
@@ -481,20 +502,20 @@ async function installApp(root, manifest, runtime) {
|
|
|
481
502
|
return;
|
|
482
503
|
}
|
|
483
504
|
|
|
484
|
-
|
|
505
|
+
logger.log(chalk.green('Successfull project install'));
|
|
485
506
|
|
|
486
507
|
if (versioning.tryGitInit(root)) {
|
|
487
|
-
|
|
488
|
-
|
|
508
|
+
logger.log();
|
|
509
|
+
logger.log('Initialized a git repository.');
|
|
489
510
|
|
|
490
511
|
if (versioning.tryGitCommit(root)) {
|
|
491
|
-
|
|
492
|
-
|
|
512
|
+
logger.log();
|
|
513
|
+
logger.log('Created git commit.');
|
|
493
514
|
}
|
|
494
515
|
}
|
|
495
516
|
|
|
496
|
-
|
|
497
|
-
|
|
517
|
+
logger.log();
|
|
518
|
+
logger.log(`Success! Created ${manifest.name} at ${root}`);
|
|
498
519
|
|
|
499
520
|
helpCommands(manifest, runtime);
|
|
500
521
|
}
|
|
@@ -506,20 +527,20 @@ async function helpCommands(manifest, { packageManager: displayedCommand }) {
|
|
|
506
527
|
|
|
507
528
|
const useYarn = displayedCommand === 'yarn';
|
|
508
529
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
530
|
+
logger.log('Inside that directory, you can run several commands:');
|
|
531
|
+
logger.log();
|
|
532
|
+
logger.log(chalk.cyan(` ${displayedCommand} start`));
|
|
533
|
+
logger.log(' Starts the development server.');
|
|
534
|
+
logger.log();
|
|
535
|
+
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`));
|
|
536
|
+
logger.log(' Bundles the app into static files for production.');
|
|
537
|
+
logger.log();
|
|
538
|
+
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}zip`));
|
|
539
|
+
logger.log(' Bundles the app into ZIP file in app root directory');
|
|
540
|
+
logger.log();
|
|
541
|
+
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}release`));
|
|
542
|
+
logger.log(' Uploads the app ZIP to Corva');
|
|
543
|
+
logger.log();
|
|
523
544
|
}
|
|
524
545
|
|
|
525
546
|
initialChecks();
|
|
@@ -3,6 +3,7 @@ const chalk = require('chalk');
|
|
|
3
3
|
const semver = require('semver');
|
|
4
4
|
const inquirer = require('inquirer');
|
|
5
5
|
|
|
6
|
+
const logger = require('../../helpers/logger');
|
|
6
7
|
const packageJson = require('../../../package.json');
|
|
7
8
|
const npm = new NpmApi();
|
|
8
9
|
|
|
@@ -37,7 +38,7 @@ async function ensureLatestVersion() {
|
|
|
37
38
|
|
|
38
39
|
// NOTE: Show user-friendly warning if version is outdated
|
|
39
40
|
async function warnIfOutdated() {
|
|
40
|
-
|
|
41
|
+
logger.write('Checking for updates...\n');
|
|
41
42
|
|
|
42
43
|
const currentVersion = getCurrentVersion();
|
|
43
44
|
const latestVersion = await getLatestVersion();
|
|
@@ -55,7 +56,7 @@ async function warnIfOutdated() {
|
|
|
55
56
|
`);
|
|
56
57
|
console.log(warning(asterisks));
|
|
57
58
|
} else {
|
|
58
|
-
|
|
59
|
+
logger.write(' ✅ \n');
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
62
|
|