@hubspot/cli 5.2.1-beta.0 → 5.2.1-beta.10

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.
Files changed (141) hide show
  1. package/bin/cli.js +7 -2
  2. package/bin/hs +1 -1
  3. package/commands/accounts/clean.js +8 -8
  4. package/commands/accounts/info.js +3 -3
  5. package/commands/accounts/list.js +14 -13
  6. package/commands/accounts/remove.js +1 -1
  7. package/commands/accounts/rename.js +3 -3
  8. package/commands/accounts/use.js +1 -1
  9. package/commands/accounts.js +3 -3
  10. package/commands/auth.js +7 -9
  11. package/commands/cms/convertFields.js +1 -1
  12. package/commands/cms/lighthouseScore.js +4 -4
  13. package/commands/cms/reactModules.js +1 -1
  14. package/commands/cms.js +3 -3
  15. package/commands/config/set/allowUsageTracking.js +1 -2
  16. package/commands/config/set/defaultMode.js +1 -1
  17. package/commands/config/set/httpTimeout.js +1 -1
  18. package/commands/config/set.js +1 -1
  19. package/commands/config.js +3 -3
  20. package/commands/create/api-sample.js +1 -1
  21. package/commands/create/module.js +1 -1
  22. package/commands/create/template.js +1 -1
  23. package/commands/create.js +1 -1
  24. package/commands/customObject/create.js +1 -1
  25. package/commands/customObject/schema/create.js +2 -3
  26. package/commands/customObject/schema/delete.js +1 -2
  27. package/commands/customObject/schema/fetch-all.js +1 -2
  28. package/commands/customObject/schema/fetch.js +1 -2
  29. package/commands/customObject/schema/list.js +1 -1
  30. package/commands/customObject/schema/update.js +2 -3
  31. package/commands/customObject/schema.js +1 -1
  32. package/commands/customObject.js +3 -3
  33. package/commands/feedback.js +4 -6
  34. package/commands/fetch.js +6 -6
  35. package/commands/filemanager/fetch.js +4 -4
  36. package/commands/filemanager/upload.js +4 -4
  37. package/commands/filemanager.js +4 -4
  38. package/commands/functions/deploy.js +7 -23
  39. package/commands/functions/list.js +4 -4
  40. package/commands/functions/server.js +4 -4
  41. package/commands/functions.js +3 -3
  42. package/commands/hubdb/clear.js +4 -4
  43. package/commands/hubdb/create.js +4 -4
  44. package/commands/hubdb/delete.js +4 -4
  45. package/commands/hubdb/fetch.js +4 -4
  46. package/commands/hubdb.js +3 -3
  47. package/commands/init.js +6 -8
  48. package/commands/lint.js +3 -3
  49. package/commands/list.js +4 -4
  50. package/commands/logs.js +4 -4
  51. package/commands/module/marketplace-validate.js +5 -5
  52. package/commands/module.js +3 -3
  53. package/commands/mv.js +4 -4
  54. package/commands/open.js +4 -4
  55. package/commands/project/add.js +1 -1
  56. package/commands/project/create.js +4 -4
  57. package/commands/project/deploy.js +4 -4
  58. package/commands/project/dev.js +139 -264
  59. package/commands/project/download.js +11 -7
  60. package/commands/project/listBuilds.js +4 -4
  61. package/commands/project/logs.js +4 -4
  62. package/commands/project/migrateApp.js +227 -0
  63. package/commands/project/open.js +12 -8
  64. package/commands/project/upload.js +15 -7
  65. package/commands/project/watch.js +4 -4
  66. package/commands/project.js +5 -3
  67. package/commands/remove.js +4 -4
  68. package/commands/sandbox/create.js +16 -16
  69. package/commands/sandbox/delete.js +5 -5
  70. package/commands/sandbox/sync.js +11 -9
  71. package/commands/sandbox.js +3 -3
  72. package/commands/secrets/addSecret.js +4 -4
  73. package/commands/secrets/deleteSecret.js +4 -4
  74. package/commands/secrets/listSecrets.js +4 -4
  75. package/commands/secrets/updateSecret.js +4 -4
  76. package/commands/secrets.js +3 -3
  77. package/commands/theme/generate-selectors.js +1 -1
  78. package/commands/theme/marketplace-validate.js +5 -5
  79. package/commands/theme/preview.js +52 -17
  80. package/commands/theme.js +1 -1
  81. package/commands/upload.js +5 -5
  82. package/commands/watch.js +61 -18
  83. package/jest.config.js +1 -0
  84. package/lang/en.lyaml +1426 -1336
  85. package/lib/DevServerManager.js +3 -2
  86. package/lib/LocalDevManager.js +194 -38
  87. package/lib/__tests__/{commonOpts.js → commonOpts.test.js} +3 -0
  88. package/lib/__tests__/downloadProjectPrompt.test.js +31 -0
  89. package/lib/__tests__/projects.test.js +13 -17
  90. package/lib/__tests__/{serverlessLogs.js → serverlessLogs.test.js} +1 -0
  91. package/lib/accountTypes.js +34 -0
  92. package/lib/buildAccount.js +197 -0
  93. package/lib/commonOpts.js +1 -1
  94. package/lib/constants.js +10 -0
  95. package/lib/developerTestAccounts.js +98 -4
  96. package/lib/errorHandlers/apiErrors.js +1 -1
  97. package/lib/errorHandlers/overrideErrors.js +1 -1
  98. package/lib/errorHandlers/standardErrors.js +1 -1
  99. package/lib/generate-selectors.js +1 -1
  100. package/lib/localDev.js +451 -0
  101. package/lib/marketplace-validate.js +11 -3
  102. package/lib/polling.js +26 -0
  103. package/lib/process.js +1 -1
  104. package/lib/projectStructure.js +12 -2
  105. package/lib/projects.js +99 -10
  106. package/lib/projectsWatch.js +1 -1
  107. package/lib/prompts/accountNamePrompt.js +81 -0
  108. package/lib/prompts/accountsPrompt.js +1 -1
  109. package/lib/prompts/activeInstallConfirmationPrompt.js +20 -0
  110. package/lib/prompts/buildIdPrompt.js +1 -1
  111. package/lib/prompts/cleanUploadPrompt.js +1 -1
  112. package/lib/prompts/cmsFieldPrompt.js +1 -1
  113. package/lib/prompts/createApiSamplePrompt.js +1 -1
  114. package/lib/prompts/createFunctionPrompt.js +1 -1
  115. package/lib/prompts/createModulePrompt.js +1 -1
  116. package/lib/prompts/createProjectPrompt.js +32 -10
  117. package/lib/prompts/createTemplatePrompt.js +1 -1
  118. package/lib/prompts/downloadProjectPrompt.js +5 -6
  119. package/lib/prompts/feedbackPrompt.js +1 -1
  120. package/lib/prompts/folderOverwritePrompt.js +1 -1
  121. package/lib/prompts/installPublicAppPrompt.js +42 -0
  122. package/lib/prompts/personalAccessKeyPrompt.js +3 -3
  123. package/lib/prompts/previewPrompt.js +19 -1
  124. package/lib/prompts/projectAddPrompt.js +1 -1
  125. package/lib/prompts/projectDevTargetAccountPrompt.js +127 -14
  126. package/lib/prompts/projectNamePrompt.js +2 -2
  127. package/lib/prompts/projectsLogsPrompt.js +1 -1
  128. package/lib/prompts/sandboxesPrompt.js +14 -43
  129. package/lib/prompts/secretPrompt.js +1 -1
  130. package/lib/prompts/selectPublicAppPrompt.js +69 -0
  131. package/lib/prompts/setAsDefaultAccountPrompt.js +1 -1
  132. package/lib/prompts/uploadPrompt.js +1 -1
  133. package/lib/sandboxSync.js +2 -2
  134. package/lib/sandboxes.js +168 -30
  135. package/lib/ui/git.js +1 -1
  136. package/lib/ui/index.js +5 -14
  137. package/lib/ui/serverlessFunctionLogs.js +1 -1
  138. package/package.json +6 -6
  139. package/lib/prompts/enterAccountNamePrompt.js +0 -33
  140. package/lib/sandboxCreate.js +0 -319
  141. /package/lib/__tests__/{validation.js → validation.test.js} +0 -0
@@ -0,0 +1,451 @@
1
+ const { logger } = require('@hubspot/local-dev-lib/logger');
2
+ const {
3
+ HUBSPOT_ACCOUNT_TYPES,
4
+ HUBSPOT_ACCOUNT_TYPE_STRINGS,
5
+ } = require('@hubspot/local-dev-lib/constants/config');
6
+ const {
7
+ isMissingScopeError,
8
+ isSpecifiedError,
9
+ } = require('@hubspot/local-dev-lib/errors/apiErrors');
10
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
11
+ const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
12
+ const { createProject } = require('@hubspot/local-dev-lib/api/projects');
13
+ const {
14
+ ENVIRONMENTS,
15
+ } = require('@hubspot/local-dev-lib/constants/environments');
16
+ const {
17
+ confirmDefaultAccountPrompt,
18
+ selectSandboxTargetAccountPrompt,
19
+ selectDeveloperTestTargetAccountPrompt,
20
+ confirmUseExistingDeveloperTestAccountPrompt,
21
+ } = require('./prompts/projectDevTargetAccountPrompt');
22
+ const { confirmPrompt } = require('./prompts/promptUtils');
23
+ const {
24
+ validateSandboxUsageLimits,
25
+ getAvailableSyncTypes,
26
+ } = require('./sandboxes');
27
+ const { syncSandbox } = require('./sandboxSync');
28
+ const {
29
+ validateDevTestAccountUsageLimits,
30
+ } = require('./developerTestAccounts');
31
+ const { logErrorInstance } = require('./errorHandlers/standardErrors');
32
+ const { uiCommandReference, uiLine, uiAccountDescription } = require('./ui');
33
+ const SpinniesManager = require('./ui/SpinniesManager');
34
+ const { i18n } = require('./lang');
35
+ const { EXIT_CODES } = require('./enums/exitCodes');
36
+ const { trackCommandMetadataUsage } = require('./usageTracking');
37
+ const {
38
+ isAppDeveloperAccount,
39
+ isDeveloperTestAccount,
40
+ } = require('./accountTypes');
41
+ const {
42
+ handleProjectUpload,
43
+ pollProjectBuildAndDeploy,
44
+ } = require('./projects');
45
+ const {
46
+ PROJECT_ERROR_TYPES,
47
+ PROJECT_BUILD_TEXT,
48
+ PROJECT_DEPLOY_TEXT,
49
+ } = require('./constants');
50
+ const {
51
+ logApiErrorInstance,
52
+ ApiErrorContext,
53
+ } = require('./errorHandlers/apiErrors');
54
+ const {
55
+ PERSONAL_ACCESS_KEY_AUTH_METHOD,
56
+ } = require('@hubspot/local-dev-lib/constants/auth');
57
+ const { buildNewAccount, saveAccountToConfig } = require('./buildAccount');
58
+ const { hubspotAccountNamePrompt } = require('./prompts/accountNamePrompt');
59
+
60
+ const i18nKey = 'lib.localDev';
61
+
62
+ // If the user passed in the --account flag, confirm they want to use that account as
63
+ // their target account, otherwise exit
64
+ const confirmDefaultAccountIsTarget = async accountConfig => {
65
+ logger.log();
66
+ const useDefaultAccount = await confirmDefaultAccountPrompt(
67
+ accountConfig.name,
68
+ HUBSPOT_ACCOUNT_TYPE_STRINGS[accountConfig.accountType]
69
+ );
70
+
71
+ if (!useDefaultAccount) {
72
+ logger.log(
73
+ i18n(
74
+ `${i18nKey}.confirmDefaultAccountIsTarget.declineDefaultAccountExplanation`,
75
+ {
76
+ useCommand: uiCommandReference('hs accounts use'),
77
+ devCommand: uiCommandReference('hs project dev'),
78
+ }
79
+ )
80
+ );
81
+ process.exit(EXIT_CODES.SUCCESS);
82
+ }
83
+ };
84
+
85
+ // Confirm the default account is a developer account if developing public apps
86
+ const checkIfAppDeveloperAccount = accountConfig => {
87
+ if (!isAppDeveloperAccount(accountConfig)) {
88
+ logger.error(i18n(`${i18nKey}.checkIfAppDevloperAccount`));
89
+ process.exit(EXIT_CODES.SUCCESS);
90
+ }
91
+ };
92
+
93
+ // Confirm the default account is a developer account if developing public apps
94
+ const checkIfDeveloperTestAccount = accountConfig => {
95
+ if (!isDeveloperTestAccount(accountConfig)) {
96
+ logger.error(i18n(`${i18nKey}.checkIfDeveloperTestAccount`));
97
+ process.exit(EXIT_CODES.SUCCESS);
98
+ }
99
+ };
100
+
101
+ // If the user isn't using the recommended account type, prompt them to use or create one
102
+ const suggestRecommendedNestedAccount = async (
103
+ accounts,
104
+ accountConfig,
105
+ hasPublicApps
106
+ ) => {
107
+ logger.log();
108
+ uiLine();
109
+ if (hasPublicApps) {
110
+ logger.log(
111
+ i18n(
112
+ `${i18nKey}.suggestRecommendedNestedAccount.publicAppNonDeveloperTestAccountWarning`
113
+ )
114
+ );
115
+ } else if (isAppDeveloperAccount(accountConfig)) {
116
+ logger.error(
117
+ i18n(
118
+ `${i18nKey}.suggestRecommendedNestedAccount.privateAppInAppDeveloperAccountError`
119
+ )
120
+ );
121
+ process.exit(EXIT_CODES.ERROR);
122
+ } else {
123
+ logger.log(
124
+ i18n(`${i18nKey}.suggestRecommendedNestedAccount.nonSandboxWarning`)
125
+ );
126
+ }
127
+ uiLine();
128
+ logger.log();
129
+
130
+ const targetAccountPrompt = isAppDeveloperAccount(accountConfig)
131
+ ? selectDeveloperTestTargetAccountPrompt
132
+ : selectSandboxTargetAccountPrompt;
133
+
134
+ return targetAccountPrompt(accounts, accountConfig, hasPublicApps);
135
+ };
136
+
137
+ // Create a new sandbox and return its accountId
138
+ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
139
+ try {
140
+ await validateSandboxUsageLimits(
141
+ accountConfig,
142
+ HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
143
+ env
144
+ );
145
+ } catch (err) {
146
+ if (isMissingScopeError(err)) {
147
+ logger.error(
148
+ i18n('lib.sandbox.create.failure.scopes.message', {
149
+ accountName: accountConfig.name || accountId,
150
+ })
151
+ );
152
+ const websiteOrigin = getHubSpotWebsiteOrigin(env);
153
+ const url = `${websiteOrigin}/personal-access-key/${accountId}`;
154
+ logger.info(
155
+ i18n('lib.sandbox.create.failure.scopes.instructions', {
156
+ accountName: accountConfig.name || accountId,
157
+ url,
158
+ })
159
+ );
160
+ } else {
161
+ logErrorInstance(err);
162
+ }
163
+ process.exit(EXIT_CODES.ERROR);
164
+ }
165
+ try {
166
+ const { name } = await hubspotAccountNamePrompt({
167
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
168
+ });
169
+
170
+ trackCommandMetadataUsage(
171
+ 'sandbox-create',
172
+ { step: 'project-dev' },
173
+ accountId
174
+ );
175
+
176
+ const { result } = await buildNewAccount({
177
+ name,
178
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
179
+ accountConfig,
180
+ env,
181
+ });
182
+
183
+ const targetAccountId = result.sandbox.sandboxHubId;
184
+
185
+ const sandboxAccountConfig = getAccountConfig(result.sandbox.sandboxHubId);
186
+ const syncTasks = await getAvailableSyncTypes(
187
+ accountConfig,
188
+ sandboxAccountConfig
189
+ );
190
+ await syncSandbox({
191
+ accountConfig: sandboxAccountConfig,
192
+ parentAccountConfig: accountConfig,
193
+ env,
194
+ syncTasks,
195
+ allowEarlyTermination: false, // Don't let user terminate early in this flow
196
+ skipPolling: true, // Skip polling, sync will run and complete in the background
197
+ });
198
+ return targetAccountId;
199
+ } catch (err) {
200
+ logErrorInstance(err);
201
+ process.exit(EXIT_CODES.ERROR);
202
+ }
203
+ };
204
+
205
+ // Create a developer test account and return its accountId
206
+ const createDeveloperTestAccountForLocalDev = async (
207
+ accountId,
208
+ accountConfig,
209
+ env
210
+ ) => {
211
+ let currentPortalCount = 0;
212
+ let maxTestPortals = 10;
213
+ try {
214
+ const validateResult = await validateDevTestAccountUsageLimits(
215
+ accountConfig
216
+ );
217
+ if (validateResult) {
218
+ currentPortalCount = validateResult.results
219
+ ? validateResult.results.length
220
+ : 0;
221
+ maxTestPortals = validateResult.maxTestPortals;
222
+ }
223
+ } catch (err) {
224
+ if (isMissingScopeError(err)) {
225
+ logger.error(
226
+ i18n('lib.developerTestAccount.create.failure.scopes.message', {
227
+ accountName: accountConfig.name || accountId,
228
+ })
229
+ );
230
+ const websiteOrigin = getHubSpotWebsiteOrigin(env);
231
+ const url = `${websiteOrigin}/personal-access-key/${accountId}`;
232
+ logger.info(
233
+ i18n('lib.developerTestAccount.create.failure.scopes.instructions', {
234
+ accountName: accountConfig.name || accountId,
235
+ url,
236
+ })
237
+ );
238
+ } else {
239
+ logErrorInstance(err);
240
+ }
241
+ process.exit(EXIT_CODES.ERROR);
242
+ }
243
+
244
+ try {
245
+ const { name } = await hubspotAccountNamePrompt({
246
+ currentPortalCount,
247
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
248
+ });
249
+ trackCommandMetadataUsage(
250
+ 'developer-test-account-create',
251
+ { step: 'project-dev' },
252
+ accountId
253
+ );
254
+
255
+ const { result } = await buildNewAccount({
256
+ name,
257
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
258
+ accountConfig,
259
+ env,
260
+ portalLimit: maxTestPortals,
261
+ });
262
+
263
+ return result.id;
264
+ } catch (err) {
265
+ logErrorInstance(err);
266
+ process.exit(EXIT_CODES.ERROR);
267
+ }
268
+ };
269
+
270
+ // Prompt user to confirm usage of an existing developer test account that is not currently in the config
271
+ const useExistingDevTestAccount = async (env, account) => {
272
+ const useExistingDevTestAcct = await confirmUseExistingDeveloperTestAccountPrompt(
273
+ account
274
+ );
275
+ if (!useExistingDevTestAcct) {
276
+ logger.log('');
277
+ logger.log(
278
+ i18n(
279
+ `${i18nKey}.confirmDefaultAccountIsTarget.declineDefaultAccountExplanation`,
280
+ {
281
+ useCommand: uiCommandReference('hs accounts use'),
282
+ devCommand: uiCommandReference('hs project dev'),
283
+ }
284
+ )
285
+ );
286
+ logger.log('');
287
+ process.exit(EXIT_CODES.SUCCESS);
288
+ }
289
+ const devTestAcctConfigName = await saveAccountToConfig({
290
+ env,
291
+ accountName: account.accountName,
292
+ accountId: account.id,
293
+ });
294
+ logger.success(
295
+ i18n(`lib.developerTestAccount.create.success.configFileUpdated`, {
296
+ accountName: devTestAcctConfigName,
297
+ authType: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
298
+ })
299
+ );
300
+ };
301
+
302
+ // Prompt the user to create a new project if one doesn't exist on their target account
303
+ const createNewProjectForLocalDev = async (
304
+ projectConfig,
305
+ targetAccountId,
306
+ shouldCreateWithoutConfirmation,
307
+ hasPublicApps
308
+ ) => {
309
+ // Create the project without prompting if this is a newly created sandbox
310
+ let shouldCreateProject = shouldCreateWithoutConfirmation;
311
+
312
+ if (!shouldCreateProject) {
313
+ const explanationString = i18n(
314
+ hasPublicApps
315
+ ? `${i18nKey}.createNewProjectForLocalDev.publicAppProjectMustExistExplanation`
316
+ : `${i18nKey}.createNewProjectForLocalDev.projectMustExistExplanation`,
317
+ {
318
+ accountIdentifier: uiAccountDescription(targetAccountId),
319
+ projectName: projectConfig.name,
320
+ }
321
+ );
322
+ logger.log();
323
+ uiLine();
324
+ logger.log(explanationString);
325
+ uiLine();
326
+
327
+ shouldCreateProject = await confirmPrompt(
328
+ i18n(`${i18nKey}.createNewProjectForLocalDev.createProject`, {
329
+ accountIdentifier: uiAccountDescription(targetAccountId),
330
+ projectName: projectConfig.name,
331
+ })
332
+ );
333
+ }
334
+
335
+ if (shouldCreateProject) {
336
+ SpinniesManager.add('createProject', {
337
+ text: i18n(`${i18nKey}.createNewProjectForLocalDev.creatingProject`, {
338
+ accountIdentifier: uiAccountDescription(targetAccountId),
339
+ projectName: projectConfig.name,
340
+ }),
341
+ });
342
+
343
+ try {
344
+ const project = await createProject(targetAccountId, projectConfig.name);
345
+ SpinniesManager.succeed('createProject', {
346
+ text: i18n(`${i18nKey}.createNewProjectForLocalDev.createdProject`, {
347
+ accountIdentifier: uiAccountDescription(targetAccountId),
348
+ projectName: projectConfig.name,
349
+ }),
350
+ succeedColor: 'white',
351
+ });
352
+ return project;
353
+ } catch (err) {
354
+ SpinniesManager.fail('createProject');
355
+ logger.log(
356
+ i18n(`${i18nKey}.createNewProjectForLocalDev.failedToCreateProject`)
357
+ );
358
+ process.exit(EXIT_CODES.ERROR);
359
+ }
360
+ } else {
361
+ // We cannot continue if the project does not exist in the target account
362
+ logger.log();
363
+ logger.log(
364
+ i18n(`${i18nKey}.createNewProjectForLocalDev.choseNotToCreateProject`)
365
+ );
366
+ process.exit(EXIT_CODES.SUCCESS);
367
+ }
368
+ };
369
+
370
+ // Create an initial build if the project was newly created in the account
371
+ // Return the newly deployed build
372
+ const createInitialBuildForNewProject = async (
373
+ projectConfig,
374
+ projectDir,
375
+ targetAccountId
376
+ ) => {
377
+ const initialUploadResult = await handleProjectUpload(
378
+ targetAccountId,
379
+ projectConfig,
380
+ projectDir,
381
+ (...args) => pollProjectBuildAndDeploy(...args, true),
382
+ i18n(`${i18nKey}.createInitialBuildForNewProject.initialUploadMessage`)
383
+ );
384
+
385
+ if (initialUploadResult.uploadError) {
386
+ if (
387
+ isSpecifiedError(initialUploadResult.uploadError, {
388
+ subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED,
389
+ })
390
+ ) {
391
+ logger.log();
392
+ logger.error(
393
+ i18n(`${i18nKey}.createInitialBuildForNewProject.projectLockedError`)
394
+ );
395
+ logger.log();
396
+ } else {
397
+ logApiErrorInstance(
398
+ initialUploadResult.uploadError,
399
+ new ApiErrorContext({
400
+ accountId: targetAccountId,
401
+ projectName: projectConfig.name,
402
+ })
403
+ );
404
+ }
405
+ process.exit(EXIT_CODES.ERROR);
406
+ }
407
+
408
+ if (!initialUploadResult.succeeded) {
409
+ let subTasks = [];
410
+
411
+ if (initialUploadResult.buildResult.status === 'FAILURE') {
412
+ subTasks =
413
+ initialUploadResult.buildResult[PROJECT_BUILD_TEXT.SUBTASK_KEY];
414
+ } else if (initialUploadResult.deployResult.status === 'FAILURE') {
415
+ subTasks =
416
+ initialUploadResult.deployResult[PROJECT_DEPLOY_TEXT.SUBTASK_KEY];
417
+ }
418
+
419
+ const failedSubTasks = subTasks.filter(task => task.status === 'FAILURE');
420
+
421
+ logger.log();
422
+ failedSubTasks.forEach(failedSubTask => {
423
+ logger.error(failedSubTask.errorMessage);
424
+ });
425
+ logger.log();
426
+
427
+ process.exit(EXIT_CODES.ERROR);
428
+ }
429
+
430
+ return initialUploadResult.buildResult;
431
+ };
432
+
433
+ const getAccountHomeUrl = accountId => {
434
+ const baseUrl = getHubSpotWebsiteOrigin(
435
+ getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
436
+ );
437
+ return `${baseUrl}/home?portalId=${accountId}`;
438
+ };
439
+
440
+ module.exports = {
441
+ confirmDefaultAccountIsTarget,
442
+ checkIfAppDeveloperAccount,
443
+ checkIfDeveloperTestAccount,
444
+ suggestRecommendedNestedAccount,
445
+ createSandboxForLocalDev,
446
+ createDeveloperTestAccountForLocalDev,
447
+ useExistingDevTestAccount,
448
+ createNewProjectForLocalDev,
449
+ createInitialBuildForNewProject,
450
+ getAccountHomeUrl,
451
+ };
@@ -57,12 +57,20 @@ const fetchValidationResults = async (accountId, validationId) => {
57
57
  }
58
58
  };
59
59
 
60
- const processValidationErrors = validationResults => {
60
+ const processValidationErrors = (i18nKey, validationResults) => {
61
61
  if (validationResults.errors.length) {
62
- const { errors } = validationResults;
62
+ const { assetPath, errors } = validationResults;
63
63
 
64
64
  errors.forEach(err => {
65
- logger.error(`${err.context}`);
65
+ if (err.failureReasonType === 'DOWNLOAD_EMPTY') {
66
+ logger.error(
67
+ i18n(`${i18nKey}.errors.invalidPath`, {
68
+ path: assetPath,
69
+ })
70
+ );
71
+ } else {
72
+ logger.error(`${err.context}`);
73
+ }
66
74
  });
67
75
  process.exit(EXIT_CODES.ERROR);
68
76
  }
package/lib/polling.js ADDED
@@ -0,0 +1,26 @@
1
+ const { POLLING_DELAY, POLLING_STATUS } = require('./constants');
2
+
3
+ const poll = (callback, accountId, taskId) => {
4
+ return new Promise((resolve, reject) => {
5
+ const pollInterval = setInterval(async () => {
6
+ const pollResp = await callback(accountId, taskId);
7
+ const { status } = pollResp;
8
+
9
+ if (status === POLLING_STATUS.SUCCESS) {
10
+ clearInterval(pollInterval);
11
+ resolve(pollResp);
12
+ } else if (
13
+ status === POLLING_STATUS.ERROR ||
14
+ status === POLLING_STATUS.REVERTED ||
15
+ status === POLLING_STATUS.FAILURE
16
+ ) {
17
+ clearInterval(pollInterval);
18
+ reject(pollResp);
19
+ }
20
+ }, POLLING_DELAY);
21
+ });
22
+ };
23
+
24
+ module.exports = {
25
+ poll,
26
+ };
package/lib/process.js CHANGED
@@ -6,7 +6,7 @@ const {
6
6
  } = require('@hubspot/local-dev-lib/logger');
7
7
  const { i18n } = require('./lang');
8
8
 
9
- const i18nKey = 'cli.lib.process';
9
+ const i18nKey = 'lib.process';
10
10
 
11
11
  const handleExit = callback => {
12
12
  const terminationSignals = [
@@ -7,11 +7,13 @@ const { logErrorInstance } = require('./errorHandlers/standardErrors');
7
7
  const COMPONENT_TYPES = Object.freeze({
8
8
  privateApp: 'private-app',
9
9
  publicApp: 'public-app',
10
+ hublTheme: 'hubl-theme',
10
11
  });
11
12
 
12
13
  const CONFIG_FILES = {
13
14
  [COMPONENT_TYPES.privateApp]: 'app.json',
14
15
  [COMPONENT_TYPES.publicApp]: 'public-app.json',
16
+ [COMPONENT_TYPES.hublTheme]: 'theme.json',
15
17
  };
16
18
 
17
19
  function getTypeFromConfigFile(configFile) {
@@ -102,13 +104,14 @@ async function findProjectComponents(projectSourceDir) {
102
104
  if (Object.values(CONFIG_FILES).includes(base)) {
103
105
  const parsedAppConfig = loadConfigFile(projectFile);
104
106
 
105
- if (parsedAppConfig && parsedAppConfig.name) {
107
+ if (parsedAppConfig) {
106
108
  const isLegacy = getIsLegacyApp(parsedAppConfig, dir);
109
+ const isHublTheme = base === CONFIG_FILES[COMPONENT_TYPES.hublTheme];
107
110
 
108
111
  components.push({
109
112
  type: getTypeFromConfigFile(base),
110
113
  config: parsedAppConfig,
111
- runnable: !isLegacy,
114
+ runnable: !isLegacy && !isHublTheme,
112
115
  path: dir,
113
116
  });
114
117
  }
@@ -118,9 +121,16 @@ async function findProjectComponents(projectSourceDir) {
118
121
  return components;
119
122
  }
120
123
 
124
+ function getProjectComponentTypes(components) {
125
+ const projectContents = {};
126
+ components.forEach(({ type }) => (projectContents[type] = true));
127
+ return projectContents;
128
+ }
129
+
121
130
  module.exports = {
122
131
  CONFIG_FILES,
123
132
  COMPONENT_TYPES,
124
133
  findProjectComponents,
125
134
  getAppCardConfigs,
135
+ getProjectComponentTypes,
126
136
  };