@hubspot/cli 4.1.8-beta.0 → 4.1.8-beta.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/README.md +2 -2
- package/commands/accounts/list.js +2 -2
- package/commands/create/api-sample.js +1 -1
- package/commands/project/add.js +56 -0
- package/commands/project/create.js +6 -3
- package/commands/project/deploy.js +32 -18
- package/commands/project/dev.js +278 -0
- package/commands/project/download.js +27 -16
- package/commands/project/upload.js +27 -75
- package/commands/project.js +4 -0
- package/commands/sandbox/create.js +133 -117
- package/commands/sandbox/delete.js +9 -9
- package/commands/sandbox/sync.js +30 -142
- package/lib/CliProgressMultibarManager.js +66 -0
- package/lib/LocalDevManager.js +526 -0
- package/lib/SpinniesManager.js +96 -0
- package/lib/projects.js +314 -126
- package/lib/prompts/accountsPrompt.js +2 -4
- package/lib/prompts/buildIdPrompt.js +35 -0
- package/lib/prompts/createProjectPrompt.js +43 -6
- package/lib/prompts/downloadProjectPrompt.js +44 -0
- package/lib/prompts/projectAddPrompt.js +57 -0
- package/lib/prompts/projectDevTargetAccountPrompt.js +105 -0
- package/lib/prompts/promptUtils.js +13 -0
- package/lib/prompts/sandboxesPrompt.js +50 -11
- package/lib/sandbox-create.js +199 -0
- package/lib/sandbox-sync.js +199 -0
- package/lib/sandboxes.js +296 -105
- package/lib/ui.js +15 -6
- package/package.json +6 -4
package/lib/projects.js
CHANGED
|
@@ -4,17 +4,14 @@ const archiver = require('archiver');
|
|
|
4
4
|
const tmp = require('tmp');
|
|
5
5
|
const chalk = require('chalk');
|
|
6
6
|
const findup = require('findup-sync');
|
|
7
|
-
const Spinnies = require('spinnies');
|
|
8
7
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
9
8
|
const { getEnv } = require('@hubspot/cli-lib/lib/config');
|
|
10
|
-
const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
|
|
11
9
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
12
10
|
const {
|
|
13
11
|
ENVIRONMENTS,
|
|
14
12
|
FEEDBACK_INTERVAL,
|
|
15
13
|
ERROR_TYPES,
|
|
16
14
|
POLLING_DELAY,
|
|
17
|
-
PROJECT_TEMPLATES,
|
|
18
15
|
PROJECT_BUILD_TEXT,
|
|
19
16
|
PROJECT_DEPLOY_TEXT,
|
|
20
17
|
PROJECT_CONFIG_FILE,
|
|
@@ -36,10 +33,17 @@ const {
|
|
|
36
33
|
} = require('@hubspot/cli-lib/errorHandlers');
|
|
37
34
|
const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');
|
|
38
35
|
const { getCwd, getAbsoluteFilePath } = require('@hubspot/cli-lib/path');
|
|
36
|
+
const { downloadGitHubRepoContents } = require('@hubspot/cli-lib/github');
|
|
39
37
|
const { promptUser } = require('./prompts/promptUtils');
|
|
40
38
|
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
41
39
|
const { uiLine, uiLink, uiAccountDescription } = require('../lib/ui');
|
|
42
40
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
41
|
+
const SpinniesManager = require('./SpinniesManager');
|
|
42
|
+
const {
|
|
43
|
+
isSpecifiedError,
|
|
44
|
+
} = require('@hubspot/cli-lib/errorHandlers/apiErrors');
|
|
45
|
+
|
|
46
|
+
const i18nKey = 'cli.lib.projects';
|
|
43
47
|
|
|
44
48
|
const writeProjectConfig = (configPath, config) => {
|
|
45
49
|
try {
|
|
@@ -85,7 +89,12 @@ const getProjectConfig = async _dir => {
|
|
|
85
89
|
}
|
|
86
90
|
};
|
|
87
91
|
|
|
88
|
-
const createProjectConfig = async (
|
|
92
|
+
const createProjectConfig = async (
|
|
93
|
+
projectPath,
|
|
94
|
+
projectName,
|
|
95
|
+
template,
|
|
96
|
+
repoPath
|
|
97
|
+
) => {
|
|
89
98
|
const { projectConfig, projectDir } = await getProjectConfig(projectPath);
|
|
90
99
|
|
|
91
100
|
if (projectConfig) {
|
|
@@ -121,7 +130,7 @@ const createProjectConfig = async (projectPath, projectName, template) => {
|
|
|
121
130
|
}`
|
|
122
131
|
);
|
|
123
132
|
|
|
124
|
-
if (template === 'no-template') {
|
|
133
|
+
if (template.name === 'no-template') {
|
|
125
134
|
fs.ensureDirSync(path.join(projectPath, 'src'));
|
|
126
135
|
|
|
127
136
|
writeProjectConfig(projectConfigPath, {
|
|
@@ -129,12 +138,7 @@ const createProjectConfig = async (projectPath, projectName, template) => {
|
|
|
129
138
|
srcDir: 'src',
|
|
130
139
|
});
|
|
131
140
|
} else {
|
|
132
|
-
await
|
|
133
|
-
projectPath,
|
|
134
|
-
'project',
|
|
135
|
-
PROJECT_TEMPLATES.find(t => t.name === template).repo,
|
|
136
|
-
''
|
|
137
|
-
);
|
|
141
|
+
await downloadGitHubRepoContents(repoPath, template.path, projectPath);
|
|
138
142
|
const _config = JSON.parse(fs.readFileSync(projectConfigPath));
|
|
139
143
|
writeProjectConfig(projectConfigPath, {
|
|
140
144
|
..._config,
|
|
@@ -168,15 +172,61 @@ const validateProjectConfig = (projectConfig, projectDir) => {
|
|
|
168
172
|
}
|
|
169
173
|
};
|
|
170
174
|
|
|
175
|
+
const pollFetchProject = async (accountId, projectName) => {
|
|
176
|
+
// Temporary solution for gating slowness. Retry on 403 statusCode
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
let pollCount = 0;
|
|
179
|
+
const spinnies = SpinniesManager.init();
|
|
180
|
+
spinnies.add('pollFetchProject', {
|
|
181
|
+
text: 'Fetching project status',
|
|
182
|
+
});
|
|
183
|
+
const pollInterval = setInterval(async () => {
|
|
184
|
+
try {
|
|
185
|
+
const project = await fetchProject(accountId, projectName);
|
|
186
|
+
if (project) {
|
|
187
|
+
spinnies.remove('pollFetchProject');
|
|
188
|
+
clearInterval(pollInterval);
|
|
189
|
+
resolve(project);
|
|
190
|
+
}
|
|
191
|
+
} catch (err) {
|
|
192
|
+
if (
|
|
193
|
+
isSpecifiedError(err, {
|
|
194
|
+
statusCode: 403,
|
|
195
|
+
category: 'GATED',
|
|
196
|
+
subCategory: 'BuildPipelineErrorType.PORTAL_GATED',
|
|
197
|
+
})
|
|
198
|
+
) {
|
|
199
|
+
pollCount += 1;
|
|
200
|
+
} else if (pollCount >= 15) {
|
|
201
|
+
// Poll up to max 30s
|
|
202
|
+
spinnies.remove('pollFetchProject');
|
|
203
|
+
clearInterval(pollInterval);
|
|
204
|
+
reject(err);
|
|
205
|
+
} else {
|
|
206
|
+
spinnies.remove('pollFetchProject');
|
|
207
|
+
clearInterval(pollInterval);
|
|
208
|
+
reject(err);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}, POLLING_DELAY);
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
|
|
171
215
|
const ensureProjectExists = async (
|
|
172
216
|
accountId,
|
|
173
217
|
projectName,
|
|
174
|
-
{
|
|
218
|
+
{
|
|
219
|
+
forceCreate = false,
|
|
220
|
+
allowCreate = true,
|
|
221
|
+
noLogs = false,
|
|
222
|
+
withPolling = false,
|
|
223
|
+
} = {}
|
|
175
224
|
) => {
|
|
176
|
-
const i18nKey = 'cli.commands.project.lib.ensureProjectExists';
|
|
177
225
|
const accountIdentifier = uiAccountDescription(accountId);
|
|
178
226
|
try {
|
|
179
|
-
const project =
|
|
227
|
+
const project = withPolling
|
|
228
|
+
? await pollFetchProject(accountId, projectName)
|
|
229
|
+
: await fetchProject(accountId, projectName);
|
|
180
230
|
return !!project;
|
|
181
231
|
} catch (err) {
|
|
182
232
|
if (err.statusCode === 404) {
|
|
@@ -186,7 +236,7 @@ const ensureProjectExists = async (
|
|
|
186
236
|
const promptResult = await promptUser([
|
|
187
237
|
{
|
|
188
238
|
name: 'shouldCreateProject',
|
|
189
|
-
message: i18n(`${i18nKey}.createPrompt`, {
|
|
239
|
+
message: i18n(`${i18nKey}.ensureProjectExists.createPrompt`, {
|
|
190
240
|
projectName,
|
|
191
241
|
accountIdentifier,
|
|
192
242
|
}),
|
|
@@ -200,7 +250,10 @@ const ensureProjectExists = async (
|
|
|
200
250
|
try {
|
|
201
251
|
await createProject(accountId, projectName);
|
|
202
252
|
logger.success(
|
|
203
|
-
i18n(`${i18nKey}.createSuccess`, {
|
|
253
|
+
i18n(`${i18nKey}.ensureProjectExists.createSuccess`, {
|
|
254
|
+
projectName,
|
|
255
|
+
accountIdentifier,
|
|
256
|
+
})
|
|
204
257
|
);
|
|
205
258
|
return true;
|
|
206
259
|
} catch (err) {
|
|
@@ -209,9 +262,10 @@ const ensureProjectExists = async (
|
|
|
209
262
|
} else {
|
|
210
263
|
if (!noLogs) {
|
|
211
264
|
logger.log(
|
|
212
|
-
|
|
213
|
-
projectName
|
|
214
|
-
|
|
265
|
+
i18n(`${i18nKey}.ensureProjectExists.notFound`, {
|
|
266
|
+
projectName,
|
|
267
|
+
accountIdentifier,
|
|
268
|
+
})
|
|
215
269
|
);
|
|
216
270
|
}
|
|
217
271
|
return false;
|
|
@@ -222,14 +276,17 @@ const ensureProjectExists = async (
|
|
|
222
276
|
}
|
|
223
277
|
};
|
|
224
278
|
|
|
225
|
-
const
|
|
226
|
-
if (!projectName) return;
|
|
227
|
-
|
|
279
|
+
const getProjectHomeUrl = accountId => {
|
|
228
280
|
const baseUrl = getHubSpotWebsiteOrigin(
|
|
229
281
|
getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
|
|
230
282
|
);
|
|
231
283
|
|
|
232
|
-
return `${baseUrl}/developer-projects/${accountId}
|
|
284
|
+
return `${baseUrl}/developer-projects/${accountId}`;
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
const getProjectDetailUrl = (projectName, accountId) => {
|
|
288
|
+
if (!projectName) return;
|
|
289
|
+
return `${getProjectHomeUrl(accountId)}/project/${projectName}`;
|
|
233
290
|
};
|
|
234
291
|
|
|
235
292
|
const getProjectBuildDetailUrl = (projectName, buildId, accountId) => {
|
|
@@ -244,23 +301,22 @@ const getProjectDeployDetailUrl = (projectName, deployId, accountId) => {
|
|
|
244
301
|
accountId
|
|
245
302
|
)}/activity/deploy/${deployId}`;
|
|
246
303
|
};
|
|
304
|
+
|
|
247
305
|
const uploadProjectFiles = async (
|
|
248
306
|
accountId,
|
|
249
307
|
projectName,
|
|
250
308
|
filePath,
|
|
251
309
|
uploadMessage
|
|
252
310
|
) => {
|
|
253
|
-
const
|
|
254
|
-
const spinnies = new Spinnies({
|
|
255
|
-
succeedColor: 'white',
|
|
256
|
-
});
|
|
311
|
+
const spinnies = SpinniesManager.init({});
|
|
257
312
|
const accountIdentifier = uiAccountDescription(accountId);
|
|
258
313
|
|
|
259
314
|
spinnies.add('upload', {
|
|
260
|
-
text: i18n(`${i18nKey}.
|
|
315
|
+
text: i18n(`${i18nKey}.uploadProjectFiles.add`, {
|
|
261
316
|
accountIdentifier,
|
|
262
317
|
projectName,
|
|
263
318
|
}),
|
|
319
|
+
succeedColor: 'white',
|
|
264
320
|
});
|
|
265
321
|
|
|
266
322
|
let buildId;
|
|
@@ -276,21 +332,21 @@ const uploadProjectFiles = async (
|
|
|
276
332
|
buildId = upload.buildId;
|
|
277
333
|
|
|
278
334
|
spinnies.succeed('upload', {
|
|
279
|
-
text: i18n(`${i18nKey}.
|
|
335
|
+
text: i18n(`${i18nKey}.uploadProjectFiles.succeed`, {
|
|
280
336
|
accountIdentifier,
|
|
281
337
|
projectName,
|
|
282
338
|
}),
|
|
283
339
|
});
|
|
284
340
|
|
|
285
341
|
logger.debug(
|
|
286
|
-
i18n(`${i18nKey}.
|
|
342
|
+
i18n(`${i18nKey}.uploadProjectFiles.buildCreated`, {
|
|
287
343
|
buildId,
|
|
288
344
|
projectName,
|
|
289
345
|
})
|
|
290
346
|
);
|
|
291
347
|
} catch (err) {
|
|
292
348
|
spinnies.fail('upload', {
|
|
293
|
-
text: i18n(`${i18nKey}.
|
|
349
|
+
text: i18n(`${i18nKey}.uploadProjectFiles.fail`, {
|
|
294
350
|
accountIdentifier,
|
|
295
351
|
projectName,
|
|
296
352
|
}),
|
|
@@ -304,7 +360,7 @@ const uploadProjectFiles = async (
|
|
|
304
360
|
})
|
|
305
361
|
);
|
|
306
362
|
if (err.error.subCategory === ERROR_TYPES.PROJECT_LOCKED) {
|
|
307
|
-
logger.log(i18n(`${i18nKey}.
|
|
363
|
+
logger.log(i18n(`${i18nKey}.uploadProjectFiles.projectLockedError`));
|
|
308
364
|
}
|
|
309
365
|
process.exit(EXIT_CODES.ERROR);
|
|
310
366
|
}
|
|
@@ -312,6 +368,84 @@ const uploadProjectFiles = async (
|
|
|
312
368
|
return { buildId };
|
|
313
369
|
};
|
|
314
370
|
|
|
371
|
+
const pollProjectBuildAndDeploy = async (
|
|
372
|
+
accountId,
|
|
373
|
+
projectConfig,
|
|
374
|
+
tempFile,
|
|
375
|
+
buildId,
|
|
376
|
+
silenceLogs = false
|
|
377
|
+
) => {
|
|
378
|
+
const {
|
|
379
|
+
autoDeployId,
|
|
380
|
+
isAutoDeployEnabled,
|
|
381
|
+
deployStatusTaskLocator,
|
|
382
|
+
status,
|
|
383
|
+
} = await pollBuildStatus(
|
|
384
|
+
accountId,
|
|
385
|
+
projectConfig.name,
|
|
386
|
+
buildId,
|
|
387
|
+
null,
|
|
388
|
+
silenceLogs
|
|
389
|
+
);
|
|
390
|
+
// autoDeployId of 0 indicates a skipped deploy
|
|
391
|
+
const isDeploying =
|
|
392
|
+
isAutoDeployEnabled && autoDeployId > 0 && deployStatusTaskLocator;
|
|
393
|
+
|
|
394
|
+
if (!silenceLogs) {
|
|
395
|
+
uiLine();
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const result = {
|
|
399
|
+
succeeded: true,
|
|
400
|
+
buildId,
|
|
401
|
+
buildSucceeded: true,
|
|
402
|
+
autodeployEnabled: isAutoDeployEnabled,
|
|
403
|
+
};
|
|
404
|
+
|
|
405
|
+
if (status === 'FAILURE') {
|
|
406
|
+
result.buildSucceeded = false;
|
|
407
|
+
result.succeeded = false;
|
|
408
|
+
return result;
|
|
409
|
+
} else if (isDeploying) {
|
|
410
|
+
if (!silenceLogs) {
|
|
411
|
+
logger.log(
|
|
412
|
+
i18n(
|
|
413
|
+
`${i18nKey}.pollProjectBuildAndDeploy.buildSucceededAutomaticallyDeploying`,
|
|
414
|
+
{
|
|
415
|
+
accountIdentifier: uiAccountDescription(accountId),
|
|
416
|
+
buildId,
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
const { status } = await pollDeployStatus(
|
|
422
|
+
accountId,
|
|
423
|
+
projectConfig.name,
|
|
424
|
+
deployStatusTaskLocator.id,
|
|
425
|
+
buildId,
|
|
426
|
+
silenceLogs
|
|
427
|
+
);
|
|
428
|
+
if (status === 'FAILURE') {
|
|
429
|
+
result.succeeded = false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
try {
|
|
434
|
+
if (tempFile) {
|
|
435
|
+
tempFile.removeCallback();
|
|
436
|
+
logger.debug(
|
|
437
|
+
i18n(`${i18nKey}.pollProjectBuildAndDeploy.cleanedUpTempFile`, {
|
|
438
|
+
path: tempFile.name,
|
|
439
|
+
})
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
} catch (e) {
|
|
443
|
+
logger.error(e);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return result;
|
|
447
|
+
};
|
|
448
|
+
|
|
315
449
|
const handleProjectUpload = async (
|
|
316
450
|
accountId,
|
|
317
451
|
projectConfig,
|
|
@@ -319,13 +453,14 @@ const handleProjectUpload = async (
|
|
|
319
453
|
callbackFunc,
|
|
320
454
|
uploadMessage
|
|
321
455
|
) => {
|
|
322
|
-
const i18nKey = 'cli.commands.project.subcommands.upload';
|
|
323
456
|
const srcDir = path.resolve(projectDir, projectConfig.srcDir);
|
|
324
457
|
|
|
325
458
|
const filenames = fs.readdirSync(srcDir);
|
|
326
459
|
if (!filenames || filenames.length === 0) {
|
|
327
460
|
logger.log(
|
|
328
|
-
i18n(`${i18nKey}.
|
|
461
|
+
i18n(`${i18nKey}.handleProjectUpload.emptySource`, {
|
|
462
|
+
srcDir: projectConfig.srcDir,
|
|
463
|
+
})
|
|
329
464
|
);
|
|
330
465
|
process.exit(EXIT_CODES.SUCCESS);
|
|
331
466
|
}
|
|
@@ -333,7 +468,7 @@ const handleProjectUpload = async (
|
|
|
333
468
|
const tempFile = tmp.fileSync({ postfix: '.zip' });
|
|
334
469
|
|
|
335
470
|
logger.debug(
|
|
336
|
-
i18n(`${i18nKey}.
|
|
471
|
+
i18n(`${i18nKey}.handleProjectUpload.compressing`, {
|
|
337
472
|
path: tempFile.name,
|
|
338
473
|
})
|
|
339
474
|
);
|
|
@@ -341,24 +476,34 @@ const handleProjectUpload = async (
|
|
|
341
476
|
const output = fs.createWriteStream(tempFile.name);
|
|
342
477
|
const archive = archiver('zip');
|
|
343
478
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
byteCount: archive.pointer(),
|
|
348
|
-
})
|
|
349
|
-
);
|
|
479
|
+
const result = new Promise(resolve =>
|
|
480
|
+
output.on('close', async function() {
|
|
481
|
+
let result = {};
|
|
350
482
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
);
|
|
483
|
+
logger.debug(
|
|
484
|
+
i18n(`${i18nKey}.handleProjectUpload.compressed`, {
|
|
485
|
+
byteCount: archive.pointer(),
|
|
486
|
+
})
|
|
487
|
+
);
|
|
357
488
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
489
|
+
const { buildId } = await uploadProjectFiles(
|
|
490
|
+
accountId,
|
|
491
|
+
projectConfig.name,
|
|
492
|
+
tempFile.name,
|
|
493
|
+
uploadMessage
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
if (callbackFunc) {
|
|
497
|
+
result = await callbackFunc(
|
|
498
|
+
accountId,
|
|
499
|
+
projectConfig,
|
|
500
|
+
tempFile,
|
|
501
|
+
buildId
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
resolve(result);
|
|
505
|
+
})
|
|
506
|
+
);
|
|
362
507
|
|
|
363
508
|
archive.pipe(output);
|
|
364
509
|
|
|
@@ -367,6 +512,8 @@ const handleProjectUpload = async (
|
|
|
367
512
|
);
|
|
368
513
|
|
|
369
514
|
archive.finalize();
|
|
515
|
+
|
|
516
|
+
return result;
|
|
370
517
|
};
|
|
371
518
|
|
|
372
519
|
const makePollTaskStatusFunc = ({
|
|
@@ -376,8 +523,6 @@ const makePollTaskStatusFunc = ({
|
|
|
376
523
|
statusStrings,
|
|
377
524
|
linkToHubSpot,
|
|
378
525
|
}) => {
|
|
379
|
-
const i18nKey = 'cli.commands.project.lib.makePollTaskStatusFunc';
|
|
380
|
-
|
|
381
526
|
const isTaskComplete = task => {
|
|
382
527
|
if (
|
|
383
528
|
!task[statusText.SUBTASK_KEY].length ||
|
|
@@ -389,23 +534,32 @@ const makePollTaskStatusFunc = ({
|
|
|
389
534
|
}
|
|
390
535
|
};
|
|
391
536
|
|
|
392
|
-
return async (
|
|
537
|
+
return async (
|
|
538
|
+
accountId,
|
|
539
|
+
taskName,
|
|
540
|
+
taskId,
|
|
541
|
+
deployedBuildId = null,
|
|
542
|
+
silenceLogs = false
|
|
543
|
+
) => {
|
|
393
544
|
const displayId = deployedBuildId || taskId;
|
|
394
545
|
|
|
395
|
-
if (linkToHubSpot) {
|
|
546
|
+
if (linkToHubSpot && !silenceLogs) {
|
|
396
547
|
logger.log(
|
|
397
548
|
`\n${linkToHubSpot(accountId, taskName, taskId, deployedBuildId)}\n`
|
|
398
549
|
);
|
|
399
550
|
}
|
|
400
551
|
|
|
401
|
-
const spinnies =
|
|
552
|
+
const spinnies = SpinniesManager.init();
|
|
553
|
+
|
|
554
|
+
const overallTaskSpinniesKey = `overallTaskStatus-${statusText.STATUS_TEXT}`;
|
|
555
|
+
|
|
556
|
+
spinnies.add(overallTaskSpinniesKey, {
|
|
557
|
+
text: 'Beginning',
|
|
402
558
|
succeedColor: 'white',
|
|
403
559
|
failColor: 'white',
|
|
404
560
|
failPrefix: chalk.bold('!'),
|
|
405
561
|
});
|
|
406
562
|
|
|
407
|
-
spinnies.add('overallTaskStatus', { text: 'Beginning' });
|
|
408
|
-
|
|
409
563
|
const [
|
|
410
564
|
initialTaskStatus,
|
|
411
565
|
{ topLevelComponentsWithChildren: taskStructure },
|
|
@@ -435,39 +589,45 @@ const makePollTaskStatusFunc = ({
|
|
|
435
589
|
});
|
|
436
590
|
|
|
437
591
|
const numComponents = structuredTasks.length;
|
|
438
|
-
const componentCountText =
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
592
|
+
const componentCountText = silenceLogs
|
|
593
|
+
? ''
|
|
594
|
+
: i18n(
|
|
595
|
+
numComponents === 1
|
|
596
|
+
? `${i18nKey}.makePollTaskStatusFunc.componentCountSingular`
|
|
597
|
+
: `${i18nKey}.makePollTaskStatusFunc.componentCount`,
|
|
598
|
+
{ numComponents }
|
|
599
|
+
) + '\n';
|
|
600
|
+
|
|
601
|
+
spinnies.update(overallTaskSpinniesKey, {
|
|
602
|
+
text: `${statusStrings.INITIALIZE(taskName)}\n${componentCountText}`,
|
|
447
603
|
});
|
|
448
604
|
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
605
|
+
if (!silenceLogs) {
|
|
606
|
+
const addTaskSpinner = (task, indent, newline) => {
|
|
607
|
+
const taskName = task[statusText.SUBTASK_NAME_KEY];
|
|
608
|
+
const taskType = task[statusText.TYPE_KEY];
|
|
609
|
+
const formattedTaskType = PROJECT_TASK_TYPES[taskType]
|
|
610
|
+
? `[${PROJECT_TASK_TYPES[taskType]}]`
|
|
611
|
+
: '';
|
|
612
|
+
const text = `${statusText.STATUS_TEXT} ${chalk.bold(
|
|
613
|
+
taskName
|
|
614
|
+
)} ${formattedTaskType} ...${newline ? '\n' : ''}`;
|
|
615
|
+
|
|
616
|
+
spinnies.add(task.id, {
|
|
617
|
+
text,
|
|
618
|
+
indent,
|
|
619
|
+
succeedColor: 'white',
|
|
620
|
+
failColor: 'white',
|
|
621
|
+
});
|
|
622
|
+
};
|
|
464
623
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
624
|
+
structuredTasks.forEach(task => {
|
|
625
|
+
addTaskSpinner(task, 2, !task.subtasks || task.subtasks.length === 0);
|
|
626
|
+
task.subtasks.forEach((subtask, i) =>
|
|
627
|
+
addTaskSpinner(subtask, 4, i === task.subtasks.length - 1)
|
|
628
|
+
);
|
|
629
|
+
});
|
|
630
|
+
}
|
|
471
631
|
|
|
472
632
|
return new Promise((resolve, reject) => {
|
|
473
633
|
const pollInterval = setInterval(async () => {
|
|
@@ -494,8 +654,8 @@ const makePollTaskStatusFunc = ({
|
|
|
494
654
|
) {
|
|
495
655
|
const taskStatusText =
|
|
496
656
|
subTask.status === statusText.STATES.SUCCESS
|
|
497
|
-
? i18n(`${i18nKey}.successStatusText`)
|
|
498
|
-
: i18n(`${i18nKey}.failedStatusText`);
|
|
657
|
+
? i18n(`${i18nKey}.makePollTaskStatusFunc.successStatusText`)
|
|
658
|
+
: i18n(`${i18nKey}.makePollTaskStatusFunc.failedStatusText`);
|
|
499
659
|
const hasNewline =
|
|
500
660
|
spinner.text.includes('\n') || Boolean(topLevelTask);
|
|
501
661
|
const updatedText = `${spinner.text.replace(
|
|
@@ -517,48 +677,49 @@ const makePollTaskStatusFunc = ({
|
|
|
517
677
|
|
|
518
678
|
if (isTaskComplete(taskStatus)) {
|
|
519
679
|
if (status === statusText.STATES.SUCCESS) {
|
|
520
|
-
spinnies.succeed(
|
|
680
|
+
spinnies.succeed(overallTaskSpinniesKey, {
|
|
521
681
|
text: statusStrings.SUCCESS(taskName),
|
|
522
682
|
});
|
|
523
683
|
} else if (status === statusText.STATES.FAILURE) {
|
|
524
|
-
spinnies.fail(
|
|
684
|
+
spinnies.fail(overallTaskSpinniesKey, {
|
|
525
685
|
text: statusStrings.FAIL(taskName),
|
|
526
686
|
});
|
|
527
687
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
`${statusStrings.SUBTASK_FAIL(
|
|
535
|
-
displayId,
|
|
536
|
-
failedSubtasks.length === 1
|
|
537
|
-
? failedSubtasks[0][statusText.SUBTASK_NAME_KEY]
|
|
538
|
-
: failedSubtasks.length + ' components'
|
|
539
|
-
)}\n`
|
|
540
|
-
);
|
|
541
|
-
logger.log('See below for a summary of errors.');
|
|
542
|
-
uiLine();
|
|
543
|
-
|
|
544
|
-
failedSubtasks.forEach(subTask => {
|
|
688
|
+
if (!silenceLogs) {
|
|
689
|
+
const failedSubtasks = subTaskStatus.filter(
|
|
690
|
+
subtask => subtask.status === 'FAILURE'
|
|
691
|
+
);
|
|
692
|
+
|
|
693
|
+
uiLine();
|
|
545
694
|
logger.log(
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
695
|
+
`${statusStrings.SUBTASK_FAIL(
|
|
696
|
+
displayId,
|
|
697
|
+
failedSubtasks.length === 1
|
|
698
|
+
? failedSubtasks[0][statusText.SUBTASK_NAME_KEY]
|
|
699
|
+
: failedSubtasks.length + ' components'
|
|
700
|
+
)}\n`
|
|
549
701
|
);
|
|
550
|
-
logger.
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
logger.log(
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
702
|
+
logger.log('See below for a summary of errors.');
|
|
703
|
+
uiLine();
|
|
704
|
+
|
|
705
|
+
failedSubtasks.forEach(subTask => {
|
|
706
|
+
logger.log(
|
|
707
|
+
`\n--- ${chalk.bold(
|
|
708
|
+
subTask[statusText.SUBTASK_NAME_KEY]
|
|
709
|
+
)} failed with the following error ---`
|
|
710
|
+
);
|
|
711
|
+
logger.error(subTask.errorMessage);
|
|
712
|
+
|
|
713
|
+
// Log nested errors
|
|
714
|
+
if (subTask.standardError && subTask.standardError.errors) {
|
|
715
|
+
logger.log();
|
|
716
|
+
subTask.standardError.errors.forEach(error => {
|
|
717
|
+
logger.log(error.message);
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
}
|
|
560
722
|
}
|
|
561
|
-
|
|
562
723
|
clearInterval(pollInterval);
|
|
563
724
|
resolve(taskStatus);
|
|
564
725
|
}
|
|
@@ -609,26 +770,53 @@ const pollDeployStatus = makePollTaskStatusFunc({
|
|
|
609
770
|
});
|
|
610
771
|
|
|
611
772
|
const logFeedbackMessage = buildId => {
|
|
612
|
-
const i18nKey = 'cli.commands.project.subcommands.upload';
|
|
613
773
|
if (buildId > 0 && buildId % FEEDBACK_INTERVAL === 0) {
|
|
614
774
|
uiLine();
|
|
615
|
-
logger.log(i18n(`${i18nKey}.
|
|
775
|
+
logger.log(i18n(`${i18nKey}.logFeedbackMessage.feedbackHeader`));
|
|
616
776
|
uiLine();
|
|
617
|
-
logger.log(i18n(`${i18nKey}.
|
|
777
|
+
logger.log(i18n(`${i18nKey}.logFeedbackMessage.feedbackMessage`));
|
|
618
778
|
}
|
|
619
779
|
};
|
|
620
780
|
|
|
781
|
+
const createProjectComponent = async (component, name) => {
|
|
782
|
+
const i18nKey = 'cli.commands.project.subcommands.add';
|
|
783
|
+
let componentName = name;
|
|
784
|
+
|
|
785
|
+
const configInfo = await getProjectConfig();
|
|
786
|
+
|
|
787
|
+
if (!configInfo.projectDir && !configInfo.projectConfig) {
|
|
788
|
+
logger.error(i18n(`${i18nKey}.error.locationInProject`));
|
|
789
|
+
process.exit(EXIT_CODES.ERROR);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const componentPath = path.join(
|
|
793
|
+
configInfo.projectDir,
|
|
794
|
+
configInfo.projectConfig.srcDir,
|
|
795
|
+
component.insertPath,
|
|
796
|
+
componentName
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
await downloadGitHubRepoContents(
|
|
800
|
+
'HubSpot/hubspot-project-components',
|
|
801
|
+
component.path,
|
|
802
|
+
componentPath
|
|
803
|
+
);
|
|
804
|
+
};
|
|
805
|
+
|
|
621
806
|
module.exports = {
|
|
622
807
|
writeProjectConfig,
|
|
623
808
|
getProjectConfig,
|
|
624
809
|
getIsInProject,
|
|
810
|
+
pollProjectBuildAndDeploy,
|
|
625
811
|
handleProjectUpload,
|
|
626
812
|
createProjectConfig,
|
|
627
813
|
validateProjectConfig,
|
|
814
|
+
getProjectHomeUrl,
|
|
628
815
|
getProjectDetailUrl,
|
|
629
816
|
getProjectBuildDetailUrl,
|
|
630
817
|
pollBuildStatus,
|
|
631
818
|
pollDeployStatus,
|
|
632
819
|
ensureProjectExists,
|
|
633
820
|
logFeedbackMessage,
|
|
821
|
+
createProjectComponent,
|
|
634
822
|
};
|