@corva/create-app 0.49.0-0 → 0.49.0-2
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/lib/commands/attach.js +21 -0
- package/lib/commands/create.js +424 -0
- package/lib/commands/release.js +43 -0
- package/lib/commands/rerun.js +25 -0
- package/lib/commands/zip.js +32 -0
- package/lib/constants/cli.js +1 -0
- package/lib/flows/lib/manifest.js +1 -1
- package/lib/flows/steps/zip-file-list-resolve.js +1 -1
- package/lib/{scripts/utils/version.js → helpers/cli-version.js} +2 -2
- package/lib/helpers/commands.js +13 -0
- package/lib/helpers/versioning.js +19 -12
- package/lib/main.js +13 -555
- package/lib/options/api-key.js +6 -0
- package/lib/options/app-version.js +3 -0
- package/lib/options/bump-version.js +19 -0
- package/lib/options/env.js +3 -0
- package/lib/options/original-cwd.js +3 -0
- package/lib/options/silent.js +3 -0
- package/package.json +1 -1
- package/lib/bump-version.option.js +0 -40
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { runFlow } from '../flow.js';
|
|
3
|
+
import { ATTACH_FLOW } from '../flows/attach.js';
|
|
4
|
+
import { getRealWorkingDir } from '../helpers/commands.js';
|
|
5
|
+
import { apiKeyOption } from '../options/api-key.js';
|
|
6
|
+
import { appVersion } from '../options/app-version.js';
|
|
7
|
+
import { envOption } from '../options/env.js';
|
|
8
|
+
import { originalCwdOption } from '../options/original-cwd.js';
|
|
9
|
+
import { silentOption } from '../options/silent.js';
|
|
10
|
+
|
|
11
|
+
export const attachCommand = new Command('attach')
|
|
12
|
+
.description('Add app to live assets streams')
|
|
13
|
+
.argument('<project-directory>', 'Project directory to work with')
|
|
14
|
+
.addOption(apiKeyOption)
|
|
15
|
+
.addOption(envOption)
|
|
16
|
+
.addOption(silentOption)
|
|
17
|
+
.addOption(appVersion)
|
|
18
|
+
.addOption(originalCwdOption)
|
|
19
|
+
.action(async (dirName, options) => {
|
|
20
|
+
await runFlow(ATTACH_FLOW, { dirName: getRealWorkingDir(dirName, options), options });
|
|
21
|
+
});
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import { dirname, join, resolve } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { clear } from 'node:console';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
import { Command, Option } from 'commander';
|
|
8
|
+
import spawn from 'cross-spawn';
|
|
9
|
+
import chalk from 'chalk';
|
|
10
|
+
import fs from 'fs-extra';
|
|
11
|
+
import figlet from 'figlet';
|
|
12
|
+
import _ from 'lodash/fp.js';
|
|
13
|
+
import inquirer from 'inquirer';
|
|
14
|
+
|
|
15
|
+
import { originalCwdOption } from '../options/original-cwd.js';
|
|
16
|
+
import { ensureBumpVersion, ensureLatestVersion } from '../helpers/cli-version.js';
|
|
17
|
+
import { logger } from '../helpers/logger.js';
|
|
18
|
+
import { IS_WINDOWS, resolveAppRuntime } from '../helpers/resolve-app-runtime.js';
|
|
19
|
+
import { initVersioning } from '../helpers/versioning.js';
|
|
20
|
+
import { copyFolderRecursiveSync, putVariablesInEnvFile } from '../helpers/utils.js';
|
|
21
|
+
import { getDefaultsForPackageJson } from '../constants/package.js';
|
|
22
|
+
import { getRealWorkingDir } from '../helpers/commands.js';
|
|
23
|
+
import { Manifest } from '../flows/lib/manifest.js';
|
|
24
|
+
import { fillManifest } from '../helpers/manifest.js';
|
|
25
|
+
import { getManifestMandatoryKeys, manifestOptions } from '../constants/manifest.js';
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const __dirname = dirname(__filename);
|
|
29
|
+
|
|
30
|
+
const WRITE_TO_JSON_OPTS = {
|
|
31
|
+
spaces: 2,
|
|
32
|
+
EOL: os.EOL,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const createCommand = new Command('create')
|
|
36
|
+
.description('Create a new app')
|
|
37
|
+
.argument('[project-directory]', 'project directory to work with', process.argv[process.argv.length - 1])
|
|
38
|
+
.addOption(originalCwdOption)
|
|
39
|
+
.usage(`${chalk.green('<project-directory>')} [options]`);
|
|
40
|
+
|
|
41
|
+
manifestOptions().forEach((value) => {
|
|
42
|
+
const type = typeof value.default;
|
|
43
|
+
const cliType = type === 'undefined' ? (value.choices ? ' [string]' : '') : ` [${type}]`;
|
|
44
|
+
const alias = value.alias ? `-${value.alias}, ` : '';
|
|
45
|
+
const optionString = `${alias}--${value.name}${cliType}`;
|
|
46
|
+
|
|
47
|
+
const option = new Option(optionString, value.message);
|
|
48
|
+
|
|
49
|
+
if (value.choices) {
|
|
50
|
+
if (typeof value.choices === 'function') {
|
|
51
|
+
option.choices(value.choices());
|
|
52
|
+
} else {
|
|
53
|
+
option.choices(value.choices.map((choice) => `${typeof choice === 'object' ? choice.value : choice}`));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (type === 'number') {
|
|
58
|
+
option.argParser(Number);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (type !== 'undefined') {
|
|
62
|
+
option.default(value.default);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
createCommand.addOption(option);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
createCommand.version(fs.readJSONSync(join(__dirname, '../../package.json')).version);
|
|
69
|
+
|
|
70
|
+
createCommand.action(async (dirName, options) => {
|
|
71
|
+
if (options.zip || options.release) {
|
|
72
|
+
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
startingMessage();
|
|
76
|
+
|
|
77
|
+
// NOTE: Default action
|
|
78
|
+
await createApp(dirName, options);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
*
|
|
83
|
+
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
84
|
+
*/
|
|
85
|
+
const getEnvManagementLink = (manifest) => {
|
|
86
|
+
if (manifest.isJs()) {
|
|
87
|
+
if (IS_WINDOWS) {
|
|
88
|
+
return 'https://github.com/coreybutler/nvm-windows';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return 'https://github.com/nvm-sh/nvm';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (IS_WINDOWS) {
|
|
95
|
+
return 'https://github.com/pyenv-win/pyenv-win';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return 'https://github.com/pyenv/pyenv';
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
async function initPackage(projectName, opts) {
|
|
102
|
+
const manifest = new Manifest(fillManifest(opts));
|
|
103
|
+
const runtime = resolveAppRuntime(opts);
|
|
104
|
+
|
|
105
|
+
if (!(await runtime.isRuntimeAvailable())) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
`Runtime "${opts.runtime}" is not available locally. Please proceed to ${chalk.green(
|
|
108
|
+
getEnvManagementLink(manifest),
|
|
109
|
+
)} to install it locally.`,
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (manifest.isUi()) {
|
|
114
|
+
await ensureLatestVersion();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const root = getRealWorkingDir(projectName, opts);
|
|
118
|
+
|
|
119
|
+
logger.log(`Creating a new Corva app in ${chalk.green(root)}.`);
|
|
120
|
+
|
|
121
|
+
await fs.ensureDir(root);
|
|
122
|
+
|
|
123
|
+
if ((await fs.readdir(root)).length) {
|
|
124
|
+
const shouldCleanup = await inquirer
|
|
125
|
+
.prompt([
|
|
126
|
+
{
|
|
127
|
+
message: `Directory "${root}" is not empty. Clean it to proceed?`,
|
|
128
|
+
name: 'cleanup',
|
|
129
|
+
type: 'confirm',
|
|
130
|
+
},
|
|
131
|
+
])
|
|
132
|
+
.then(_.get('cleanup'));
|
|
133
|
+
|
|
134
|
+
if (shouldCleanup) {
|
|
135
|
+
await fs.emptyDir(root);
|
|
136
|
+
} else {
|
|
137
|
+
throw new Error(`Directory is not empty: ${root}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await fs.writeJSON(join(root, 'manifest.json'), manifest.manifest, WRITE_TO_JSON_OPTS);
|
|
142
|
+
|
|
143
|
+
await addTemplate(root, manifest, runtime);
|
|
144
|
+
await configureApp(root, manifest, runtime);
|
|
145
|
+
|
|
146
|
+
opts.dependenciesInstall && (await installDependencies(root, manifest, runtime));
|
|
147
|
+
|
|
148
|
+
opts.gitInit && (await initVersioning(root, manifest, runtime));
|
|
149
|
+
|
|
150
|
+
logger.log();
|
|
151
|
+
logger.log(`Success! Created ${chalk.green(manifest.name)} at ${chalk.yellow(root)}`);
|
|
152
|
+
|
|
153
|
+
helpCommands(manifest, runtime);
|
|
154
|
+
|
|
155
|
+
logger.log();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function createApp(dirName, opts) {
|
|
159
|
+
const { isValid, values } = checkOptions(opts);
|
|
160
|
+
|
|
161
|
+
if (isValid) {
|
|
162
|
+
Object.keys(values).forEach((key) => {
|
|
163
|
+
logger.log(`${key} : ${values[key]}`);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return initPackage(dirName, opts);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log('Please fill your app Metadata');
|
|
170
|
+
|
|
171
|
+
const answers = await inquirer.prompt(manifestOptions(dirName), opts);
|
|
172
|
+
|
|
173
|
+
return initPackage(dirName, answers);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
*
|
|
178
|
+
* @param {string} root
|
|
179
|
+
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
180
|
+
* @param {*} runtime
|
|
181
|
+
*/
|
|
182
|
+
async function addTemplate(root, manifest, runtime) {
|
|
183
|
+
logger.log(chalk.green('Copying app template...'));
|
|
184
|
+
logger.log();
|
|
185
|
+
|
|
186
|
+
const cliRoot = resolve(__dirname, '..', '..');
|
|
187
|
+
|
|
188
|
+
const templateFolder = join(cliRoot, 'templates', manifest.templateName, runtime.language);
|
|
189
|
+
|
|
190
|
+
copyFolderRecursiveSync(templateFolder, root);
|
|
191
|
+
|
|
192
|
+
if (manifest.isNode()) {
|
|
193
|
+
copyFolderRecursiveSync(join(cliRoot, 'common', 'node'), root);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (manifest.isPython()) {
|
|
197
|
+
copyFolderRecursiveSync(join(cliRoot, 'common', 'python'), root);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!manifest.isUi()) {
|
|
201
|
+
await putVariablesInEnvFile(root, manifest);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// We can't have .gitignore file in our templates.
|
|
205
|
+
// It's missing when @corva/create-app is installed.
|
|
206
|
+
// That's why we manually rename gitignore to .gitignore after copying template
|
|
207
|
+
const targetGitignore = join(root, '.gitignore');
|
|
208
|
+
|
|
209
|
+
fs.renameSync(join(root, 'gitignore'), targetGitignore);
|
|
210
|
+
|
|
211
|
+
if (runtime.language === 'typescript' && manifest.isNode()) {
|
|
212
|
+
await fs.appendFile(targetGitignore, '\n**/*.js\n');
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
logger.log(chalk.green('Done: copying app template!'));
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
*
|
|
220
|
+
* @param {string} root
|
|
221
|
+
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
222
|
+
* @param {*} runtime
|
|
223
|
+
*/
|
|
224
|
+
async function configureApp(root, manifest, runtime) {
|
|
225
|
+
if (manifest.isJs()) {
|
|
226
|
+
await addNvmRc(root, manifest, runtime);
|
|
227
|
+
await addPackageJSON(root, manifest, runtime);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (manifest.isNode()) {
|
|
231
|
+
await addTsConfigs(root, manifest, runtime);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (manifest.isPython()) {
|
|
235
|
+
await addPythonConfigs(root, manifest, runtime);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const addNvmRc = async (root, manifest, runtime) => {
|
|
240
|
+
await fs.outputFile(join(root, '.nvmrc'), `${runtime.semver}\n`);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
*
|
|
245
|
+
* @param {string} root
|
|
246
|
+
* @param {Manifest} manifest
|
|
247
|
+
* @param {import('./helpers/resolve-app-runtime.js').Runtime} runtime
|
|
248
|
+
*/
|
|
249
|
+
const addPythonConfigs = async (root, manifest, runtime) => {
|
|
250
|
+
await fs.writeFile(resolve(root, '.python-version'), `${runtime.semver}\n`);
|
|
251
|
+
await fs.writeFile(resolve(root, '.python-virtualenv'), `${manifest.unix_name}\n`);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
const addTsConfigs = (root, manifest, runtime) => {
|
|
255
|
+
if (runtime.language !== 'typescript') {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return Promise.all([
|
|
260
|
+
fs.writeJson(
|
|
261
|
+
resolve(root, 'tsconfig.json'),
|
|
262
|
+
{
|
|
263
|
+
extends: `@tsconfig/node${runtime.version}/tsconfig.json`,
|
|
264
|
+
compilerOptions: {
|
|
265
|
+
inlineSourceMap: true,
|
|
266
|
+
strict: false,
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
WRITE_TO_JSON_OPTS,
|
|
270
|
+
),
|
|
271
|
+
fs.writeJson(
|
|
272
|
+
resolve(root, 'tsconfig.build.json'),
|
|
273
|
+
{
|
|
274
|
+
extends: './tsconfig.json',
|
|
275
|
+
include: ['lib/**/*.ts', 'index.ts'],
|
|
276
|
+
exclude: ['node_modules', '**/*.spec.ts'],
|
|
277
|
+
},
|
|
278
|
+
WRITE_TO_JSON_OPTS,
|
|
279
|
+
),
|
|
280
|
+
]);
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
*
|
|
285
|
+
* @param {string} root
|
|
286
|
+
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
287
|
+
*/
|
|
288
|
+
function addPackageJSON(root, manifest, runtime) {
|
|
289
|
+
const {
|
|
290
|
+
version,
|
|
291
|
+
description,
|
|
292
|
+
scripts,
|
|
293
|
+
dependencies,
|
|
294
|
+
devDependencies,
|
|
295
|
+
main,
|
|
296
|
+
license = 'UNLICENSED',
|
|
297
|
+
private: isPrivate = true,
|
|
298
|
+
...rest
|
|
299
|
+
} = getDefaultsForPackageJson(manifest, runtime);
|
|
300
|
+
|
|
301
|
+
const packageJson = {
|
|
302
|
+
name: manifest.unix_name,
|
|
303
|
+
version,
|
|
304
|
+
description: manifest.description || description,
|
|
305
|
+
main,
|
|
306
|
+
private: isPrivate,
|
|
307
|
+
license,
|
|
308
|
+
engines: {
|
|
309
|
+
node: `^${runtime.version}`,
|
|
310
|
+
[runtime.packageManager]: '*',
|
|
311
|
+
},
|
|
312
|
+
scripts,
|
|
313
|
+
dependencies,
|
|
314
|
+
devDependencies,
|
|
315
|
+
...rest,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
return fs.writeJSON(join(root, 'package.json'), packageJson, WRITE_TO_JSON_OPTS);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* @param {string} root
|
|
323
|
+
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
324
|
+
* @param {import('./helpers/resolve-app-runtime.js').Runtime} runtime
|
|
325
|
+
* @returns {Promise<void>}
|
|
326
|
+
* @throws {Error}
|
|
327
|
+
* @throws {import('child_process').ExecException}
|
|
328
|
+
* @throws {import('child_process').SpawnSyncReturns<Buffer>}
|
|
329
|
+
*/
|
|
330
|
+
async function installDependencies(root, manifest, runtime) {
|
|
331
|
+
const command = manifest.isJs() ? runtime.packageManager : 'make';
|
|
332
|
+
|
|
333
|
+
if (IS_WINDOWS && !manifest.isJs()) {
|
|
334
|
+
logger.log();
|
|
335
|
+
logger.log(`⚠️ ${chalk.yellow('Please install project dependencies manually')}`);
|
|
336
|
+
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const args = ['install'];
|
|
341
|
+
const opts = { stdio: ['inherit', 'inherit', 'pipe'], cwd: root };
|
|
342
|
+
|
|
343
|
+
if (process.env.CI && command === 'yarn') {
|
|
344
|
+
args.push('--cache-folder=".yarn-cache"');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
logger.log(chalk.yellow(`Installing template dependencies using ${runtime.packageManager}...`));
|
|
348
|
+
|
|
349
|
+
const nvmOpts = {
|
|
350
|
+
shell: true,
|
|
351
|
+
...opts,
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const proc =
|
|
355
|
+
manifest.isJs() && existsSync(`${os.homedir()}/.nvm/nvm.sh`)
|
|
356
|
+
? spawn.sync(`\\. ${os.homedir()}/.nvm/nvm.sh && nvm i && ${command} ${args.join(' ')}`, nvmOpts)
|
|
357
|
+
: spawn.sync(command, args, opts);
|
|
358
|
+
|
|
359
|
+
if (proc.stderr) {
|
|
360
|
+
const error = proc.stderr
|
|
361
|
+
.toString('utf8')
|
|
362
|
+
.split('\n')
|
|
363
|
+
// NOTE: filter out warnings caused by @corva/ui peer dependencies
|
|
364
|
+
.filter((line) => !line.includes('@corva/ui'))
|
|
365
|
+
.join('\n');
|
|
366
|
+
|
|
367
|
+
console.log(error);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (proc.status !== 0) {
|
|
371
|
+
console.error(`\`${command} ${args.join(' ')}\` failed`);
|
|
372
|
+
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
logger.log(chalk.green('Successfull project install'));
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function helpCommands(manifest, { packageManager: displayedCommand }) {
|
|
380
|
+
if (!manifest.isUi()) {
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const useYarn = displayedCommand === 'yarn';
|
|
385
|
+
|
|
386
|
+
logger.log('Inside that directory, you can run several commands:');
|
|
387
|
+
logger.log();
|
|
388
|
+
logger.log(chalk.cyan(` ${displayedCommand} start`));
|
|
389
|
+
logger.log(' Starts the development server.');
|
|
390
|
+
logger.log();
|
|
391
|
+
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`));
|
|
392
|
+
logger.log(' Bundles the app into static files for production.');
|
|
393
|
+
logger.log();
|
|
394
|
+
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}zip`));
|
|
395
|
+
logger.log(' Bundles the app into ZIP file in app root directory');
|
|
396
|
+
logger.log();
|
|
397
|
+
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}release`));
|
|
398
|
+
logger.log(' Uploads the app ZIP to Corva');
|
|
399
|
+
logger.log();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function startingMessage() {
|
|
403
|
+
clear();
|
|
404
|
+
console.log(chalk.green(' Welcome to apps generator for:'));
|
|
405
|
+
console.log(chalk.cyan(figlet.textSync('CORVA.AI', { horizontalLayout: 'full' })));
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function checkOptions(opts) {
|
|
409
|
+
let isValid = true;
|
|
410
|
+
const values = {};
|
|
411
|
+
|
|
412
|
+
getManifestMandatoryKeys(opts).forEach((key) => {
|
|
413
|
+
if (!opts[key]) {
|
|
414
|
+
isValid = false;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
values[key] = opts[key];
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
return {
|
|
421
|
+
isValid,
|
|
422
|
+
values,
|
|
423
|
+
};
|
|
424
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
|
+
import { runFlow } from '../flow.js';
|
|
3
|
+
import { RELEASE_FLOW } from '../flows/release.js';
|
|
4
|
+
import { getRealWorkingDir } from '../helpers/commands.js';
|
|
5
|
+
import { apiKeyOption } from '../options/api-key.js';
|
|
6
|
+
import { bumpVersionOption } from '../options/bump-version.js';
|
|
7
|
+
import { envOption } from '../options/env.js';
|
|
8
|
+
import { originalCwdOption } from '../options/original-cwd.js';
|
|
9
|
+
import { silentOption } from '../options/silent.js';
|
|
10
|
+
import { ensureBumpVersion } from '../helpers/cli-version.js';
|
|
11
|
+
|
|
12
|
+
export const releaseCommand = new Command('release')
|
|
13
|
+
.description('Release app')
|
|
14
|
+
.argument('<project-directory>', 'Project directory to work with')
|
|
15
|
+
.argument('[patterns...]', 'Additional patterns to zip', [])
|
|
16
|
+
.addOption(bumpVersionOption)
|
|
17
|
+
.addOption(new Option('--ignored-files [string...]', 'Patterns to skip zip').default([]))
|
|
18
|
+
.addOption(silentOption)
|
|
19
|
+
.addOption(envOption)
|
|
20
|
+
.addOption(apiKeyOption)
|
|
21
|
+
.addOption(originalCwdOption)
|
|
22
|
+
.addOption(new Option('--notes [string]', 'Add custom notes to published app'))
|
|
23
|
+
.addOption(new Option('--label [string]', 'Put a label on the release').choices(['BETA', 'PROD']))
|
|
24
|
+
.addOption(new Option('--remove-on-fail [boolean]', 'Remove release if it fails during deployment').default(false))
|
|
25
|
+
.addOption(
|
|
26
|
+
new Option('--remove-on-success [boolean]', 'App package (.zip) will not be deleted after upload').default(true),
|
|
27
|
+
)
|
|
28
|
+
.addOption(
|
|
29
|
+
new Option(
|
|
30
|
+
'--remove-existing [boolean]',
|
|
31
|
+
'If package version is already taken - remove the previously published package and upload a new one',
|
|
32
|
+
).default(false),
|
|
33
|
+
)
|
|
34
|
+
// .addOption(new Option('--zip-file-name [string]', 'Prebuilt zip file name in dir'))
|
|
35
|
+
.action(async (dirName, patterns, options) => {
|
|
36
|
+
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
37
|
+
|
|
38
|
+
await runFlow(RELEASE_FLOW, {
|
|
39
|
+
dirName: getRealWorkingDir(dirName, options),
|
|
40
|
+
patterns,
|
|
41
|
+
options,
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
|
+
|
|
3
|
+
import { runFlow } from '../flow.js';
|
|
4
|
+
import { RERUN_FLOW } from '../flows/rerun.js';
|
|
5
|
+
import { getRealWorkingDir } from '../helpers/commands.js';
|
|
6
|
+
import { apiKeyOption } from '../options/api-key.js';
|
|
7
|
+
import { appVersion } from '../options/app-version.js';
|
|
8
|
+
import { envOption } from '../options/env.js';
|
|
9
|
+
import { originalCwdOption } from '../options/original-cwd.js';
|
|
10
|
+
import { silentOption } from '../options/silent.js';
|
|
11
|
+
|
|
12
|
+
export const rerunCommand = new Command('rerun')
|
|
13
|
+
.description('Rerun app')
|
|
14
|
+
.argument('<project-directory>', 'Project directory to work with')
|
|
15
|
+
.addOption(apiKeyOption)
|
|
16
|
+
.addOption(envOption)
|
|
17
|
+
.addOption(silentOption)
|
|
18
|
+
.addOption(appVersion)
|
|
19
|
+
.addOption(new Option('--assets [assets...]', 'Assets IDs list', []).conflicts('companyId'))
|
|
20
|
+
.addOption(new Option('--company-id [number]', 'Company ID', []))
|
|
21
|
+
.addOption(new Option('--interval [number]', 'Interval for scheduler apps (exp. 1200)'))
|
|
22
|
+
.addOption(originalCwdOption)
|
|
23
|
+
.action(async (dirName, options) => {
|
|
24
|
+
await runFlow(RERUN_FLOW, { dirName: getRealWorkingDir(dirName, options), options });
|
|
25
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command, Option } from 'commander';
|
|
2
|
+
import _ from 'lodash/fp.js';
|
|
3
|
+
|
|
4
|
+
import { runFlow } from '../flow.js';
|
|
5
|
+
import { ZIP_FLOW } from '../flows/zip.js';
|
|
6
|
+
import { getRealWorkingDir, silencer } from '../helpers/commands.js';
|
|
7
|
+
import { bumpVersionOption } from '../options/bump-version.js';
|
|
8
|
+
import { originalCwdOption } from '../options/original-cwd.js';
|
|
9
|
+
import { silentOption } from '../options/silent.js';
|
|
10
|
+
import { ensureBumpVersion } from '../helpers/cli-version.js';
|
|
11
|
+
|
|
12
|
+
export const zipCommand = new Command('zip')
|
|
13
|
+
.description('Bundle app')
|
|
14
|
+
.argument('<project-directory>', 'Project directory to work with')
|
|
15
|
+
.argument('[patterns...]', 'Additional patterns to zip', [])
|
|
16
|
+
.addOption(bumpVersionOption)
|
|
17
|
+
.addOption(new Option('--ignored-files [ignoredFiles...]', 'Patterns to skip zip', []))
|
|
18
|
+
.addOption(originalCwdOption)
|
|
19
|
+
.addOption(silentOption)
|
|
20
|
+
.action(
|
|
21
|
+
silencer(async (dirName, patterns, options) => {
|
|
22
|
+
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
23
|
+
|
|
24
|
+
console.log(getRealWorkingDir(dirName, options), options);
|
|
25
|
+
|
|
26
|
+
return runFlow(ZIP_FLOW, {
|
|
27
|
+
dirName: getRealWorkingDir(dirName, options),
|
|
28
|
+
patterns,
|
|
29
|
+
options,
|
|
30
|
+
}).then(_.get('zipFileName'));
|
|
31
|
+
}),
|
|
32
|
+
);
|
package/lib/constants/cli.js
CHANGED
|
@@ -11,7 +11,7 @@ const SCHEDULER_MAPPING = [SCHEDULER_TYPE_DATA_TIME, SCHEDULER_TYPE_DEPTH, SCHED
|
|
|
11
11
|
{},
|
|
12
12
|
);
|
|
13
13
|
|
|
14
|
-
const NODE_RUNTIMES = [APP_RUNTIMES.
|
|
14
|
+
const NODE_RUNTIMES = [APP_RUNTIMES.NODE16, APP_RUNTIMES.NODE18];
|
|
15
15
|
|
|
16
16
|
export class Manifest {
|
|
17
17
|
constructor(manifest) {
|
|
@@ -5,7 +5,7 @@ import _ from 'lodash/fp.js';
|
|
|
5
5
|
import { promises as fs } from 'node:fs';
|
|
6
6
|
import { resolve } from 'node:path';
|
|
7
7
|
import { promisify } from 'node:util';
|
|
8
|
-
import { getIncreasedVersion } from '../../
|
|
8
|
+
import { getIncreasedVersion } from '../../helpers/cli-version.js';
|
|
9
9
|
import { loadJson } from '../lib/json.js';
|
|
10
10
|
import { StepError } from '../lib/step-error.js';
|
|
11
11
|
|
|
@@ -5,7 +5,7 @@ import inquirer from 'inquirer';
|
|
|
5
5
|
import { join } from 'path';
|
|
6
6
|
import * as url from 'url';
|
|
7
7
|
|
|
8
|
-
import { logger } from '
|
|
8
|
+
import { logger } from './logger.js';
|
|
9
9
|
import fs from 'fs-extra';
|
|
10
10
|
|
|
11
11
|
const npm = new NpmApi();
|
|
@@ -16,7 +16,7 @@ const code = chalk.cyan;
|
|
|
16
16
|
const asterisks = '****************************************************************';
|
|
17
17
|
|
|
18
18
|
const getCurrentVersion = async () =>
|
|
19
|
-
(await fs.readJSON(join(url.fileURLToPath(new URL('.', import.meta.url)), '
|
|
19
|
+
(await fs.readJSON(join(url.fileURLToPath(new URL('.', import.meta.url)), '../../package.json'))).version;
|
|
20
20
|
const getLatestVersion = async () => npm.repo('@corva/create-app').prop('version');
|
|
21
21
|
|
|
22
22
|
// NOTE: Stop process and show error if version is outdated
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
|
|
3
|
+
export const silencer =
|
|
4
|
+
(handler) =>
|
|
5
|
+
async (...args) => {
|
|
6
|
+
const result = await handler(...args);
|
|
7
|
+
|
|
8
|
+
if (args[args.length - 2].silent && result) {
|
|
9
|
+
console.log(result);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const getRealWorkingDir = (relativePath, options) => resolve(options.originalCwd, relativePath);
|
|
@@ -6,6 +6,25 @@ import { logger } from './logger.js';
|
|
|
6
6
|
|
|
7
7
|
const debug = debugFn('cca:versioning');
|
|
8
8
|
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param {string} root
|
|
12
|
+
*/
|
|
13
|
+
export async function initVersioning(root) {
|
|
14
|
+
logger.log();
|
|
15
|
+
|
|
16
|
+
if (!tryGitInit(root)) {
|
|
17
|
+
logger.log('Already in a git repository. Skipping git initialization.');
|
|
18
|
+
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (tryGitCommit(root)) {
|
|
23
|
+
logger.log();
|
|
24
|
+
logger.log('Created git commit');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
export function isInGitRepository(appPath) {
|
|
10
29
|
try {
|
|
11
30
|
execSync('git rev-parse --is-inside-work-tree', {
|
|
@@ -73,15 +92,3 @@ export function tryGitCommit(appPath) {
|
|
|
73
92
|
return false;
|
|
74
93
|
}
|
|
75
94
|
}
|
|
76
|
-
|
|
77
|
-
export function shouldUseYarn(appPath) {
|
|
78
|
-
try {
|
|
79
|
-
execSync('yarnpkg --version', { stdio: 'ignore', cwd: appPath });
|
|
80
|
-
|
|
81
|
-
return true;
|
|
82
|
-
} catch (e) {
|
|
83
|
-
debug(e);
|
|
84
|
-
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
}
|
package/lib/main.js
CHANGED
|
@@ -1,71 +1,17 @@
|
|
|
1
|
-
/* eslint-disable no-undef */
|
|
2
1
|
import chalk from 'chalk';
|
|
3
|
-
import { Command, CommanderError
|
|
4
|
-
import figlet from 'figlet';
|
|
5
|
-
import fs from 'fs-extra';
|
|
6
|
-
import inquirer from 'inquirer';
|
|
7
|
-
import os from 'node:os';
|
|
8
|
-
import path, { resolve } from 'node:path';
|
|
2
|
+
import { Command, CommanderError } from 'commander';
|
|
9
3
|
import semver from 'semver';
|
|
10
4
|
|
|
11
|
-
import
|
|
12
|
-
import * as utils from './helpers/utils.js';
|
|
13
|
-
import * as versioning from './helpers/versioning.js';
|
|
14
|
-
import { ensureBumpVersion, ensureLatestVersion, warnIfOutdated } from './scripts/utils/version.js';
|
|
15
|
-
|
|
16
|
-
import * as manifestConstants from './constants/manifest.js';
|
|
17
|
-
import { getDefaultsForPackageJson } from './constants/package.js';
|
|
18
|
-
|
|
19
|
-
import spawn from 'cross-spawn';
|
|
20
|
-
import _ from 'lodash/fp.js';
|
|
21
|
-
import { clear } from 'node:console';
|
|
22
|
-
import { existsSync } from 'node:fs';
|
|
23
|
-
import { fileURLToPath } from 'node:url';
|
|
24
|
-
import packageJson from '../package.json' assert { type: 'json' };
|
|
25
|
-
import {
|
|
26
|
-
apiKeyOption,
|
|
27
|
-
appVersion,
|
|
28
|
-
bumpVersionOption,
|
|
29
|
-
envOption,
|
|
30
|
-
originalCwdOption,
|
|
31
|
-
silentOption,
|
|
32
|
-
} from './bump-version.option.js';
|
|
5
|
+
import { warnIfOutdated } from './helpers/cli-version.js';
|
|
33
6
|
import { ERROR_ICON } from './constants/messages.js';
|
|
34
|
-
import { runFlow } from './flow.js';
|
|
35
|
-
import { ATTACH_FLOW } from './flows/attach.js';
|
|
36
|
-
import { Manifest } from './flows/lib/manifest.js';
|
|
37
7
|
import { StepError } from './flows/lib/step-error.js';
|
|
38
|
-
import { RELEASE_FLOW } from './flows/release.js';
|
|
39
|
-
import { RERUN_FLOW } from './flows/rerun.js';
|
|
40
|
-
import { ZIP_FLOW } from './flows/zip.js';
|
|
41
8
|
import { logger } from './helpers/logger.js';
|
|
42
|
-
import { IS_WINDOWS, resolveAppRuntime } from './helpers/resolve-app-runtime.js';
|
|
43
|
-
|
|
44
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
45
|
-
const __dirname = path.dirname(__filename);
|
|
46
|
-
|
|
47
|
-
const writejsonOptions = {
|
|
48
|
-
spaces: 2,
|
|
49
|
-
EOL: os.EOL,
|
|
50
|
-
};
|
|
51
9
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (args[args.length - 2].silent && result) {
|
|
58
|
-
console.log(result);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const getRealWorkingDir = (relativePath, options) => resolve(options.originalCwd, relativePath);
|
|
63
|
-
|
|
64
|
-
function startingMessage() {
|
|
65
|
-
clear();
|
|
66
|
-
console.log(chalk.green(' Welcome to apps generator for:'));
|
|
67
|
-
console.log(chalk.cyan(figlet.textSync('CORVA.AI', { horizontalLayout: 'full' })));
|
|
68
|
-
}
|
|
10
|
+
import { zipCommand } from './commands/zip.js';
|
|
11
|
+
import { releaseCommand } from './commands/release.js';
|
|
12
|
+
import { rerunCommand } from './commands/rerun.js';
|
|
13
|
+
import { attachCommand } from './commands/attach.js';
|
|
14
|
+
import { createCommand } from './commands/create.js';
|
|
69
15
|
|
|
70
16
|
function checkNodeVersion() {
|
|
71
17
|
logger.write('Checking node version...');
|
|
@@ -87,30 +33,6 @@ function checkNodeVersion() {
|
|
|
87
33
|
logger.write(' ✅ \n');
|
|
88
34
|
}
|
|
89
35
|
|
|
90
|
-
function checkOptions(opts) {
|
|
91
|
-
let isValid = true;
|
|
92
|
-
const values = {};
|
|
93
|
-
|
|
94
|
-
manifestConstants.getManifestMandatoryKeys(opts).forEach((key) => {
|
|
95
|
-
if (!opts[key]) {
|
|
96
|
-
isValid = false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
values[key] = opts[key];
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
isValid,
|
|
104
|
-
values,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// eslint-disable-next-line no-unused-vars
|
|
109
|
-
const printDeprecationNotice = (param) =>
|
|
110
|
-
console.warn(
|
|
111
|
-
chalk.bgYellowBright`DEPRECATED OPTION: ${param}` + ` Use ${chalk.cyan(`create-corva-app ${param} .`)} instead`,
|
|
112
|
-
);
|
|
113
|
-
|
|
114
36
|
export async function run() {
|
|
115
37
|
const program = new Command('create-corva-app')
|
|
116
38
|
.hook('preAction', async () => {
|
|
@@ -119,140 +41,12 @@ export async function run() {
|
|
|
119
41
|
await warnIfOutdated();
|
|
120
42
|
})
|
|
121
43
|
.configureOutput({ writeErr: () => undefined })
|
|
122
|
-
.exitOverride()
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
.
|
|
126
|
-
.
|
|
127
|
-
.
|
|
128
|
-
.addOption(originalCwdOption)
|
|
129
|
-
.usage(`${chalk.green('<project-directory>')} [options]`);
|
|
130
|
-
|
|
131
|
-
manifestConstants.manifestOptions().forEach((value) => {
|
|
132
|
-
const type = typeof value.default;
|
|
133
|
-
const cliType = type === 'undefined' ? (value.choices ? ' [string]' : '') : ` [${type}]`;
|
|
134
|
-
const alias = value.alias ? `-${value.alias}, ` : '';
|
|
135
|
-
const optionString = `${alias}--${value.name}${cliType}`;
|
|
136
|
-
|
|
137
|
-
const option = new Option(optionString, value.message);
|
|
138
|
-
|
|
139
|
-
if (value.choices) {
|
|
140
|
-
if (typeof value.choices === 'function') {
|
|
141
|
-
option.choices(value.choices());
|
|
142
|
-
} else {
|
|
143
|
-
option.choices(value.choices.map((choice) => `${typeof choice === 'object' ? choice.value : choice}`));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (type === 'number') {
|
|
148
|
-
option.argParser(Number);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (type !== 'undefined') {
|
|
152
|
-
option.default(value.default);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
createCommand.addOption(option);
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
createCommand.version(packageJson.version);
|
|
159
|
-
|
|
160
|
-
createCommand.action(async (dirName, options) => {
|
|
161
|
-
if (options.zip || options.release) {
|
|
162
|
-
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
startingMessage();
|
|
166
|
-
|
|
167
|
-
// NOTE: Default action
|
|
168
|
-
await createApp(dirName, options);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
program
|
|
172
|
-
.command('zip')
|
|
173
|
-
.description('Bundle app')
|
|
174
|
-
.argument('<project-directory>', 'Project directory to work with')
|
|
175
|
-
.argument('[patterns...]', 'Additional patterns to zip', [])
|
|
176
|
-
.addOption(bumpVersionOption)
|
|
177
|
-
.addOption(new Option('--ignored-files [ignoredFiles...]', 'Patterns to skip zip', []))
|
|
178
|
-
.addOption(originalCwdOption)
|
|
179
|
-
.addOption(silentOption)
|
|
180
|
-
.action(
|
|
181
|
-
silencer(async (dirName, patterns, options) => {
|
|
182
|
-
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
183
|
-
|
|
184
|
-
console.log(getRealWorkingDir(dirName, options), options);
|
|
185
|
-
|
|
186
|
-
return runFlow(ZIP_FLOW, {
|
|
187
|
-
dirName: getRealWorkingDir(dirName, options),
|
|
188
|
-
patterns,
|
|
189
|
-
options,
|
|
190
|
-
}).then(_.get('zipFileName'));
|
|
191
|
-
}),
|
|
192
|
-
);
|
|
193
|
-
|
|
194
|
-
program
|
|
195
|
-
.command('release')
|
|
196
|
-
.description('Release app')
|
|
197
|
-
.argument('<project-directory>', 'Project directory to work with')
|
|
198
|
-
.argument('[patterns...]', 'Additional patterns to zip', [])
|
|
199
|
-
.addOption(bumpVersionOption)
|
|
200
|
-
.addOption(new Option('--ignored-files [string...]', 'Patterns to skip zip').default([]))
|
|
201
|
-
.addOption(silentOption)
|
|
202
|
-
.addOption(envOption)
|
|
203
|
-
.addOption(apiKeyOption)
|
|
204
|
-
.addOption(originalCwdOption)
|
|
205
|
-
.addOption(new Option('--notes [string]', 'Add custom notes to published app'))
|
|
206
|
-
.addOption(new Option('--label [string]', 'Put a label on the release').choices(['BETA', 'PROD']))
|
|
207
|
-
.addOption(new Option('--remove-on-fail [boolean]', 'Remove release if it fails during deployment').default(false))
|
|
208
|
-
.addOption(
|
|
209
|
-
new Option('--remove-on-success [boolean]', 'App package (.zip) will not be deleted after upload').default(true),
|
|
210
|
-
)
|
|
211
|
-
.addOption(
|
|
212
|
-
new Option(
|
|
213
|
-
'--remove-existing [boolean]',
|
|
214
|
-
'If package version is already taken - remove the previously published package and upload a new one',
|
|
215
|
-
).default(false),
|
|
216
|
-
)
|
|
217
|
-
// .addOption(new Option('--zip-file-name [string]', 'Prebuilt zip file name in dir'))
|
|
218
|
-
.action(async (dirName, patterns, options) => {
|
|
219
|
-
options.bumpVersion = await ensureBumpVersion(options.bumpVersion);
|
|
220
|
-
|
|
221
|
-
await runFlow(RELEASE_FLOW, {
|
|
222
|
-
dirName: getRealWorkingDir(dirName, options),
|
|
223
|
-
patterns,
|
|
224
|
-
options,
|
|
225
|
-
});
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
program
|
|
229
|
-
.command('rerun')
|
|
230
|
-
.description('Rerun app')
|
|
231
|
-
.argument('<project-directory>', 'Project directory to work with')
|
|
232
|
-
.addOption(apiKeyOption)
|
|
233
|
-
.addOption(envOption)
|
|
234
|
-
.addOption(silentOption)
|
|
235
|
-
.addOption(appVersion)
|
|
236
|
-
.addOption(new Option('--assets [assets...]', 'Assets IDs list', []).conflicts('companyId'))
|
|
237
|
-
.addOption(new Option('--company-id [number]', 'Company ID', []))
|
|
238
|
-
.addOption(new Option('--interval [number]', 'Interval for scheduler apps (exp. 1200)'))
|
|
239
|
-
.addOption(originalCwdOption)
|
|
240
|
-
.action(async (dirName, options) => {
|
|
241
|
-
await runFlow(RERUN_FLOW, { dirName: getRealWorkingDir(dirName, options), options });
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
program
|
|
245
|
-
.command('attach')
|
|
246
|
-
.description('Add app to live assets streams')
|
|
247
|
-
.argument('<project-directory>', 'Project directory to work with')
|
|
248
|
-
.addOption(apiKeyOption)
|
|
249
|
-
.addOption(envOption)
|
|
250
|
-
.addOption(silentOption)
|
|
251
|
-
.addOption(appVersion)
|
|
252
|
-
.addOption(originalCwdOption)
|
|
253
|
-
.action(async (dirName, options) => {
|
|
254
|
-
await runFlow(ATTACH_FLOW, { dirName: getRealWorkingDir(dirName, options), options });
|
|
255
|
-
});
|
|
44
|
+
.exitOverride()
|
|
45
|
+
.addCommand(createCommand, { isDefault: true })
|
|
46
|
+
.addCommand(zipCommand)
|
|
47
|
+
.addCommand(releaseCommand)
|
|
48
|
+
.addCommand(rerunCommand)
|
|
49
|
+
.addCommand(attachCommand);
|
|
256
50
|
|
|
257
51
|
try {
|
|
258
52
|
await program.parseAsync(process.argv);
|
|
@@ -313,339 +107,3 @@ const handleCommanderError = (program, e) => {
|
|
|
313
107
|
}
|
|
314
108
|
}
|
|
315
109
|
};
|
|
316
|
-
|
|
317
|
-
/**
|
|
318
|
-
*
|
|
319
|
-
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
320
|
-
*/
|
|
321
|
-
const getEnvManagementLink = (manifest) => {
|
|
322
|
-
if (manifest.isJs()) {
|
|
323
|
-
if (IS_WINDOWS) {
|
|
324
|
-
return 'https://github.com/coreybutler/nvm-windows';
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return 'https://github.com/nvm-sh/nvm';
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (IS_WINDOWS) {
|
|
331
|
-
return 'https://github.com/pyenv-win/pyenv-win';
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
return 'https://github.com/pyenv/pyenv';
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
async function initPackage(projectName, opts) {
|
|
338
|
-
const manifest = new Manifest(manifestHelpers.fillManifest(opts));
|
|
339
|
-
const runtime = resolveAppRuntime(opts);
|
|
340
|
-
|
|
341
|
-
if (!(await runtime.isRuntimeAvailable())) {
|
|
342
|
-
throw new Error(
|
|
343
|
-
`Runtime "${opts.runtime}" is not available locally. Please proceed to ${chalk.green(
|
|
344
|
-
getEnvManagementLink(manifest),
|
|
345
|
-
)} to install it locally.`,
|
|
346
|
-
);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (manifest.isUi()) {
|
|
350
|
-
await ensureLatestVersion();
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const root = getRealWorkingDir(projectName, opts);
|
|
354
|
-
|
|
355
|
-
logger.log(`Creating a new Corva app in ${chalk.green(root)}.`);
|
|
356
|
-
|
|
357
|
-
await fs.ensureDir(root);
|
|
358
|
-
|
|
359
|
-
if ((await fs.readdir(root)).length) {
|
|
360
|
-
const shouldCleanup = await inquirer
|
|
361
|
-
.prompt([
|
|
362
|
-
{
|
|
363
|
-
message: `Directory "${root}" is not empty. Clean it to proceed?`,
|
|
364
|
-
name: 'cleanup',
|
|
365
|
-
type: 'confirm',
|
|
366
|
-
},
|
|
367
|
-
])
|
|
368
|
-
.then(_.get('cleanup'));
|
|
369
|
-
|
|
370
|
-
if (shouldCleanup) {
|
|
371
|
-
await fs.emptyDir(root);
|
|
372
|
-
} else {
|
|
373
|
-
throw new Error(`Directory is not empty: ${root}`);
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
await fs.writeJSON(path.join(root, 'manifest.json'), manifest.manifest, writejsonOptions);
|
|
378
|
-
|
|
379
|
-
await addTemplate(root, manifest, runtime);
|
|
380
|
-
await configureApp(root, manifest, runtime);
|
|
381
|
-
|
|
382
|
-
opts.dependenciesInstall && (await installDependencies(root, manifest, runtime));
|
|
383
|
-
|
|
384
|
-
opts.gitInit && (await initVersioning(root, manifest, runtime));
|
|
385
|
-
|
|
386
|
-
logger.log();
|
|
387
|
-
logger.log(`Success! Created ${chalk.green(manifest.name)} at ${chalk.yellow(root)}`);
|
|
388
|
-
|
|
389
|
-
helpCommands(manifest, runtime);
|
|
390
|
-
|
|
391
|
-
logger.log();
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
async function createApp(dirName, opts) {
|
|
395
|
-
const { isValid, values } = checkOptions(opts);
|
|
396
|
-
|
|
397
|
-
if (isValid) {
|
|
398
|
-
Object.keys(values).forEach((key) => {
|
|
399
|
-
logger.log(`${key} : ${values[key]}`);
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
return initPackage(dirName, opts);
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
console.log('Please fill your app Metadata');
|
|
406
|
-
|
|
407
|
-
const answers = await inquirer.prompt(manifestConstants.manifestOptions(dirName), opts);
|
|
408
|
-
|
|
409
|
-
return initPackage(dirName, answers);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
*
|
|
414
|
-
* @param {string} root
|
|
415
|
-
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
416
|
-
* @param {*} runtime
|
|
417
|
-
*/
|
|
418
|
-
async function addTemplate(root, manifest, runtime) {
|
|
419
|
-
logger.log(chalk.green('Copying app template...'));
|
|
420
|
-
logger.log();
|
|
421
|
-
|
|
422
|
-
const templateFolder = path.resolve(__dirname, '..', 'templates', manifest.templateName, runtime.language);
|
|
423
|
-
|
|
424
|
-
utils.copyFolderRecursiveSync(templateFolder, root);
|
|
425
|
-
|
|
426
|
-
if (manifest.isNode()) {
|
|
427
|
-
utils.copyFolderRecursiveSync(path.resolve(__dirname, '..', 'common', 'node'), root);
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
if (manifest.isPython()) {
|
|
431
|
-
utils.copyFolderRecursiveSync(path.resolve(__dirname, '..', 'common', 'python'), root);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
if (!manifest.isUi()) {
|
|
435
|
-
await utils.putVariablesInEnvFile(root, manifest);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// We can't have .gitignore file in our templates.
|
|
439
|
-
// It's missing when @corva/create-app is installed.
|
|
440
|
-
// That's why we manually rename gitignore to .gitignore after copying template
|
|
441
|
-
const targetGitignore = path.join(root, '.gitignore');
|
|
442
|
-
|
|
443
|
-
fs.renameSync(path.join(root, 'gitignore'), targetGitignore);
|
|
444
|
-
|
|
445
|
-
if (runtime.language === 'typescript' && manifest.isNode()) {
|
|
446
|
-
await fs.appendFile(targetGitignore, '\n**/*.js\n');
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
logger.log(chalk.green('Done: copying app template!'));
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
/**
|
|
453
|
-
*
|
|
454
|
-
* @param {string} root
|
|
455
|
-
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
456
|
-
* @param {*} runtime
|
|
457
|
-
*/
|
|
458
|
-
async function configureApp(root, manifest, runtime) {
|
|
459
|
-
if (manifest.isJs()) {
|
|
460
|
-
await addNvmRc(root, manifest, runtime);
|
|
461
|
-
await addPackageJSON(root, manifest, runtime);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
if (manifest.isNode()) {
|
|
465
|
-
await addTsConfigs(root, manifest, runtime);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
if (manifest.isPython()) {
|
|
469
|
-
await addPythonConfigs(root, manifest, runtime);
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const addNvmRc = async (root, manifest, runtime) => {
|
|
474
|
-
await fs.outputFile(path.join(root, '.nvmrc'), `${runtime.semver}\n`);
|
|
475
|
-
};
|
|
476
|
-
|
|
477
|
-
/**
|
|
478
|
-
*
|
|
479
|
-
* @param {string} root
|
|
480
|
-
* @param {Manifest} manifest
|
|
481
|
-
* @param {import('./helpers/resolve-app-runtime.js').Runtime} runtime
|
|
482
|
-
*/
|
|
483
|
-
const addPythonConfigs = async (root, manifest, runtime) => {
|
|
484
|
-
await fs.writeFile(path.resolve(root, '.python-version'), `${runtime.semver}\n`);
|
|
485
|
-
await fs.writeFile(path.resolve(root, '.python-virtualenv'), `${manifest.unix_name}\n`);
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
const addTsConfigs = (root, manifest, runtime) => {
|
|
489
|
-
if (runtime.language !== 'typescript') {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
return Promise.all([
|
|
494
|
-
fs.writeJson(
|
|
495
|
-
path.resolve(root, 'tsconfig.json'),
|
|
496
|
-
{
|
|
497
|
-
extends: `@tsconfig/node${runtime.version}/tsconfig.json`,
|
|
498
|
-
compilerOptions: {
|
|
499
|
-
inlineSourceMap: true,
|
|
500
|
-
strict: false,
|
|
501
|
-
},
|
|
502
|
-
},
|
|
503
|
-
writejsonOptions,
|
|
504
|
-
),
|
|
505
|
-
fs.writeJson(
|
|
506
|
-
path.resolve(root, 'tsconfig.build.json'),
|
|
507
|
-
{
|
|
508
|
-
extends: './tsconfig.json',
|
|
509
|
-
include: ['lib/**/*.ts', 'index.ts'],
|
|
510
|
-
exclude: ['node_modules', '**/*.spec.ts'],
|
|
511
|
-
},
|
|
512
|
-
writejsonOptions,
|
|
513
|
-
),
|
|
514
|
-
]);
|
|
515
|
-
};
|
|
516
|
-
|
|
517
|
-
/**
|
|
518
|
-
*
|
|
519
|
-
* @param {string} root
|
|
520
|
-
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
521
|
-
*/
|
|
522
|
-
function addPackageJSON(root, manifest, runtime) {
|
|
523
|
-
const {
|
|
524
|
-
version,
|
|
525
|
-
description,
|
|
526
|
-
scripts,
|
|
527
|
-
dependencies,
|
|
528
|
-
devDependencies,
|
|
529
|
-
main,
|
|
530
|
-
license = 'UNLICENSED',
|
|
531
|
-
private: isPrivate = true,
|
|
532
|
-
...rest
|
|
533
|
-
} = getDefaultsForPackageJson(manifest, runtime);
|
|
534
|
-
|
|
535
|
-
const packageJson = {
|
|
536
|
-
name: manifest.unix_name,
|
|
537
|
-
version,
|
|
538
|
-
description: manifest.description || description,
|
|
539
|
-
main,
|
|
540
|
-
private: isPrivate,
|
|
541
|
-
license,
|
|
542
|
-
engines: {
|
|
543
|
-
node: `^${runtime.version}`,
|
|
544
|
-
[runtime.packageManager]: '*',
|
|
545
|
-
},
|
|
546
|
-
scripts,
|
|
547
|
-
dependencies,
|
|
548
|
-
devDependencies,
|
|
549
|
-
...rest,
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
return fs.writeJSON(path.join(root, 'package.json'), packageJson, writejsonOptions);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
/**
|
|
556
|
-
* @param {string} root
|
|
557
|
-
* @param {import('./flows/lib/manifest').Manifest} manifest
|
|
558
|
-
* @param {import('./helpers/resolve-app-runtime.js').Runtime} runtime
|
|
559
|
-
* @returns {Promise<void>}
|
|
560
|
-
* @throws {Error}
|
|
561
|
-
* @throws {import('child_process').ExecException}
|
|
562
|
-
* @throws {import('child_process').SpawnSyncReturns<Buffer>}
|
|
563
|
-
*/
|
|
564
|
-
async function installDependencies(root, manifest, runtime) {
|
|
565
|
-
const command = manifest.isJs() ? runtime.packageManager : 'make';
|
|
566
|
-
|
|
567
|
-
if (IS_WINDOWS && !manifest.isJs()) {
|
|
568
|
-
logger.log();
|
|
569
|
-
logger.log(`⚠️ ${chalk.yellow('Please install project dependencies manually')}`);
|
|
570
|
-
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
const args = ['install'];
|
|
575
|
-
const opts = { stdio: ['inherit', 'inherit', 'pipe'], cwd: root };
|
|
576
|
-
|
|
577
|
-
if (process.env.CI && command === 'yarn') {
|
|
578
|
-
args.push('--cache-folder=".yarn-cache"');
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
logger.log(chalk.yellow(`Installing template dependencies using ${runtime.packageManager}...`));
|
|
582
|
-
|
|
583
|
-
const proc =
|
|
584
|
-
manifest.isJs() && existsSync(`${os.homedir()}/.nvm/nvm.sh`)
|
|
585
|
-
? spawn.sync(`\\. ${os.homedir()}/.nvm/nvm.sh && nvm i && ${command} ${args.join(' ')}`, {
|
|
586
|
-
shell: true,
|
|
587
|
-
...opts,
|
|
588
|
-
})
|
|
589
|
-
: spawn.sync(command, args, opts);
|
|
590
|
-
|
|
591
|
-
if (proc.stderr) {
|
|
592
|
-
const error = proc.stderr
|
|
593
|
-
.toString('utf8')
|
|
594
|
-
.split('\n')
|
|
595
|
-
// NOTE: filter out warnings caused by @corva/ui peer dependencies
|
|
596
|
-
.filter((line) => !line.includes('@corva/ui'))
|
|
597
|
-
.join('\n');
|
|
598
|
-
|
|
599
|
-
console.log(error);
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
if (proc.status !== 0) {
|
|
603
|
-
console.error(`\`${command} ${args.join(' ')}\` failed`);
|
|
604
|
-
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
logger.log(chalk.green('Successfull project install'));
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
/**
|
|
612
|
-
*
|
|
613
|
-
* @param {string} root
|
|
614
|
-
*/
|
|
615
|
-
async function initVersioning(root) {
|
|
616
|
-
logger.log();
|
|
617
|
-
|
|
618
|
-
if (!versioning.tryGitInit(root)) {
|
|
619
|
-
logger.log('Already in a git repository. Skipping git initialization.');
|
|
620
|
-
|
|
621
|
-
return;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
if (versioning.tryGitCommit(root)) {
|
|
625
|
-
logger.log();
|
|
626
|
-
logger.log('Created git commit');
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
async function helpCommands(manifest, { packageManager: displayedCommand }) {
|
|
631
|
-
if (!manifest.isUi()) {
|
|
632
|
-
return;
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const useYarn = displayedCommand === 'yarn';
|
|
636
|
-
|
|
637
|
-
logger.log('Inside that directory, you can run several commands:');
|
|
638
|
-
logger.log();
|
|
639
|
-
logger.log(chalk.cyan(` ${displayedCommand} start`));
|
|
640
|
-
logger.log(' Starts the development server.');
|
|
641
|
-
logger.log();
|
|
642
|
-
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}build`));
|
|
643
|
-
logger.log(' Bundles the app into static files for production.');
|
|
644
|
-
logger.log();
|
|
645
|
-
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}zip`));
|
|
646
|
-
logger.log(' Bundles the app into ZIP file in app root directory');
|
|
647
|
-
logger.log();
|
|
648
|
-
logger.log(chalk.cyan(` ${displayedCommand} ${useYarn ? '' : 'run '}release`));
|
|
649
|
-
logger.log(' Uploads the app ZIP to Corva');
|
|
650
|
-
logger.log();
|
|
651
|
-
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { InvalidArgumentError, Option } from 'commander';
|
|
2
|
+
import semver from 'semver';
|
|
3
|
+
|
|
4
|
+
const flags = '--bump-version <string>';
|
|
5
|
+
const description = 'Bump version';
|
|
6
|
+
const choices = ['major', 'minor', 'patch', 'skip'];
|
|
7
|
+
|
|
8
|
+
function argParser(value, previous) {
|
|
9
|
+
// eslint-disable-next-line no-invalid-this
|
|
10
|
+
if (this.argChoices.includes(value) || semver.valid(value)) {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
throw new InvalidArgumentError(
|
|
15
|
+
// eslint-disable-next-line no-invalid-this
|
|
16
|
+
`Allowed choices are ${this.argChoices.map((choice) => `"${choice}"`).join(', ')} or a valid semver version.`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
export const bumpVersionOption = new Option(flags, description).choices(choices).argParser(argParser);
|
package/package.json
CHANGED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { InvalidArgumentError, Option } from 'commander';
|
|
3
|
-
import semver from 'semver';
|
|
4
|
-
|
|
5
|
-
const flags = '--bump-version <string>';
|
|
6
|
-
const description = 'Bump version';
|
|
7
|
-
const choices = ['major', 'minor', 'patch', 'skip'];
|
|
8
|
-
|
|
9
|
-
function argParser(value, previous) {
|
|
10
|
-
// eslint-disable-next-line no-invalid-this
|
|
11
|
-
if (this.argChoices.includes(value) || semver.valid(value)) {
|
|
12
|
-
return value;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
throw new InvalidArgumentError(
|
|
16
|
-
// eslint-disable-next-line no-invalid-this
|
|
17
|
-
`Allowed choices are ${this.argChoices.map((choice) => `"${choice}"`).join(', ')} or a valid semver version.`,
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
export const bumpVersionOption = new Option(flags, description).choices(choices).argParser(argParser);
|
|
21
|
-
|
|
22
|
-
export const apiKeyOption = new Option(
|
|
23
|
-
'--api-key [string]',
|
|
24
|
-
'Pre generated API key for authorization during app upload',
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
export const appVersion = new Option('--app-version [number]', 'App version (exp. 1, 2, 3)');
|
|
28
|
-
|
|
29
|
-
export const envOption = new Option('--env [string]', 'Environment to use').choices(['qa', 'staging', 'production']);
|
|
30
|
-
|
|
31
|
-
export const silentOption = new Option('--silent [boolean]', 'Only log result of the operation').default(false);
|
|
32
|
-
|
|
33
|
-
export const originalCwdOption = new Option('--original-cwd <string>').hideHelp();
|
|
34
|
-
|
|
35
|
-
export const bumpVersionOptionDeprecated = new Option(
|
|
36
|
-
flags,
|
|
37
|
-
chalk.bgYellow`DEPRECATED` + ` Use with ${chalk.cyan`zip`} or ${chalk.cyan`release`} command instead`,
|
|
38
|
-
)
|
|
39
|
-
.choices(choices)
|
|
40
|
-
.argParser(argParser);
|