@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 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
- process.stdout.write(
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
- process.stdout.write(`${indent}Done!` + SUCCESS_ICON);
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
- process.stdout.write(message);
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
- process.stdout.write(SUCCESS_ICON);
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 baseURL = `https://api${
17
- CORVA_API_ENV === 'production' ? '' : `.${CORVA_API_ENV}`
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 };
@@ -0,0 +1,8 @@
1
+ const { PREPARE_DATA_STEP } = require('./rerun-prepare-data');
2
+ const { CREATE_TASK_STEP } = require('./rerun-create-task');
3
+
4
+ const RERUN_STEPS = [PREPARE_DATA_STEP, CREATE_TASK_STEP];
5
+
6
+ module.exports = {
7
+ RERUN_STEPS,
8
+ };
@@ -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
- process.stdout.write(item.message);
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
- console.log('Initialized git repo in app');
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
- console.log('Added first git commit');
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
- process.stdout.write('Checking node version...');
53
+ logger.write('Checking node version...');
52
54
 
53
55
  const unsupportedNodeVersion = !semver.satisfies(process.version, '>=16');
54
56
  if (unsupportedNodeVersion) {
55
- console.log(
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
- process.stdout.write(' ✅ \n');
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
- process.stdout.write(ERROR_ICON);
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
- console.log(
259
+ logger.log(
239
260
  ` ${chalk.cyan(program.name())} ${commandName} ${chalk.green('<project-directory>')}`
240
261
  );
241
- console.log();
242
- console.log('For example:');
243
- console.log(
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
- console.log();
247
- console.log(
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
- console.log(`Creating a new Corva app in ${chalk.green(root)}.`);
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
- console.log();
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
- console.log(`${key} : ${values[key]}`);
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
- console.log(chalk.green('Copying app template...'));
322
- console.log();
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
- console.log(chalk.green('Done: copying app template!'));
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
- console.log(chalk.yellow(`Installing template dependencies using ${runtime.packageManager}...`));
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
- console.log(chalk.green('Successfull project install'));
505
+ logger.log(chalk.green('Successfull project install'));
485
506
 
486
507
  if (versioning.tryGitInit(root)) {
487
- console.log();
488
- console.log('Initialized a git repository.');
508
+ logger.log();
509
+ logger.log('Initialized a git repository.');
489
510
 
490
511
  if (versioning.tryGitCommit(root)) {
491
- console.log();
492
- console.log('Created git commit.');
512
+ logger.log();
513
+ logger.log('Created git commit.');
493
514
  }
494
515
  }
495
516
 
496
- console.log();
497
- console.log(`Success! Created ${manifest.name} at ${root}`);
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
- console.log('Inside that directory, you can run several commands:');
510
- console.log();
511
- console.log(chalk.cyan(` ${displayedCommand} start`));
512
- console.log(' Starts the development server.');
513
- console.log();
514
- console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`));
515
- console.log(' Bundles the app into static files for production.');
516
- console.log();
517
- console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}zip`));
518
- console.log(' Bundles the app into ZIP file in app root directory');
519
- console.log();
520
- console.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}release`));
521
- console.log(' Uploads the app ZIP to Corva');
522
- console.log();
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
- process.stdout.write('Checking for updates...\n');
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
- process.stdout.write(' ✅ \n');
59
+ logger.write(' ✅ \n');
59
60
  }
60
61
  }
61
62
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corva/create-app",
3
- "version": "0.39.0-rc.0",
3
+ "version": "0.40.0-1",
4
4
  "private": false,
5
5
  "description": "Create app to use it in CORVA.AI",
6
6
  "keywords": [