@hubspot/cli 5.2.1-beta.0 → 5.2.1-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/bin/cli.js +6 -1
- package/commands/accounts/clean.js +3 -3
- package/commands/accounts/list.js +17 -5
- package/commands/init.js +1 -1
- package/commands/project/dev.js +108 -259
- package/commands/project/upload.js +3 -0
- package/commands/sandbox/sync.js +5 -3
- package/commands/theme/preview.js +48 -14
- package/lang/en.lyaml +55 -17
- package/lib/LocalDevManager.js +31 -33
- package/lib/accountTypes.js +34 -0
- package/lib/developerTestAccountCreate.js +187 -0
- package/lib/developerTestAccounts.js +50 -4
- package/lib/localDev.js +444 -0
- package/lib/projectStructure.js +12 -2
- package/lib/projects.js +36 -0
- package/lib/prompts/developerTestAccountNamePrompt.js +29 -0
- package/lib/prompts/previewPrompt.js +18 -0
- package/lib/prompts/projectDevTargetAccountPrompt.js +126 -13
- package/lib/prompts/sandboxesPrompt.js +1 -1
- package/lib/sandboxSync.js +1 -1
- package/lib/sandboxes.js +1 -16
- package/lib/ui/index.js +10 -2
- package/package.json +4 -4
package/lib/localDev.js
ADDED
|
@@ -0,0 +1,444 @@
|
|
|
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 } = require('@hubspot/local-dev-lib/config');
|
|
12
|
+
const { createProject } = require('@hubspot/local-dev-lib/api/projects');
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
confirmDefaultAccountPrompt,
|
|
16
|
+
selectSandboxTargetAccountPrompt,
|
|
17
|
+
selectDeveloperTestTargetAccountPrompt,
|
|
18
|
+
confirmUseExistingDeveloperTestAccountPrompt,
|
|
19
|
+
} = require('./prompts/projectDevTargetAccountPrompt');
|
|
20
|
+
const { sandboxNamePrompt } = require('./prompts/sandboxesPrompt');
|
|
21
|
+
const {
|
|
22
|
+
developerTestAccountNamePrompt,
|
|
23
|
+
} = require('./prompts/developerTestAccountNamePrompt');
|
|
24
|
+
const { confirmPrompt } = require('./prompts/promptUtils');
|
|
25
|
+
const {
|
|
26
|
+
validateSandboxUsageLimits,
|
|
27
|
+
getSandboxTypeAsString,
|
|
28
|
+
getAvailableSyncTypes,
|
|
29
|
+
} = require('./sandboxes');
|
|
30
|
+
const { buildSandbox } = require('./sandboxCreate');
|
|
31
|
+
const { syncSandbox } = require('./sandboxSync');
|
|
32
|
+
const {
|
|
33
|
+
validateDevTestAccountUsageLimits,
|
|
34
|
+
} = require('./developerTestAccounts');
|
|
35
|
+
const {
|
|
36
|
+
buildDeveloperTestAccount,
|
|
37
|
+
saveDevTestAccountToConfig,
|
|
38
|
+
} = require('./developerTestAccountCreate');
|
|
39
|
+
const { logErrorInstance } = require('./errorHandlers/standardErrors');
|
|
40
|
+
const { uiCommandReference, uiLine, uiAccountDescription } = require('./ui');
|
|
41
|
+
const SpinniesManager = require('./ui/SpinniesManager');
|
|
42
|
+
const { i18n } = require('./lang');
|
|
43
|
+
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
44
|
+
const { trackCommandMetadataUsage } = require('./usageTracking');
|
|
45
|
+
const {
|
|
46
|
+
isAppDeveloperAccount,
|
|
47
|
+
isDeveloperTestAccount,
|
|
48
|
+
} = require('./accountTypes');
|
|
49
|
+
const {
|
|
50
|
+
handleProjectUpload,
|
|
51
|
+
pollProjectBuildAndDeploy,
|
|
52
|
+
} = require('./projects');
|
|
53
|
+
const {
|
|
54
|
+
PROJECT_ERROR_TYPES,
|
|
55
|
+
PROJECT_BUILD_TEXT,
|
|
56
|
+
PROJECT_DEPLOY_TEXT,
|
|
57
|
+
} = require('./constants');
|
|
58
|
+
const {
|
|
59
|
+
logApiErrorInstance,
|
|
60
|
+
ApiErrorContext,
|
|
61
|
+
} = require('./errorHandlers/apiErrors');
|
|
62
|
+
const {
|
|
63
|
+
PERSONAL_ACCESS_KEY_AUTH_METHOD,
|
|
64
|
+
} = require('@hubspot/local-dev-lib/constants/auth');
|
|
65
|
+
|
|
66
|
+
const i18nKey = 'cli.lib.localDev';
|
|
67
|
+
|
|
68
|
+
// If the user passed in the --account flag, confirm they want to use that account as
|
|
69
|
+
// their target account, otherwise exit
|
|
70
|
+
const confirmDefaultAccountIsTarget = async accountConfig => {
|
|
71
|
+
logger.log();
|
|
72
|
+
const useDefaultAccount = await confirmDefaultAccountPrompt(
|
|
73
|
+
accountConfig.name,
|
|
74
|
+
isDeveloperTestAccount(accountConfig)
|
|
75
|
+
? HUBSPOT_ACCOUNT_TYPE_STRINGS[HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST]
|
|
76
|
+
: `${getSandboxTypeAsString(accountConfig.accountType)} sandbox`
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (!useDefaultAccount) {
|
|
80
|
+
logger.log(
|
|
81
|
+
i18n(
|
|
82
|
+
`${i18nKey}.confirmDefaultAccountIsTarget.declineDefaultAccountExplanation`,
|
|
83
|
+
{
|
|
84
|
+
useCommand: uiCommandReference('hs accounts use'),
|
|
85
|
+
devCommand: uiCommandReference('hs project dev'),
|
|
86
|
+
}
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// Confirm the default account is a developer account if developing public apps
|
|
94
|
+
const checkIfAppDeveloperAccount = accountConfig => {
|
|
95
|
+
if (!isAppDeveloperAccount(accountConfig)) {
|
|
96
|
+
logger.error(i18n(`${i18nKey}.checkIfAppDevloperAccount`));
|
|
97
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Confirm the default account is a developer account if developing public apps
|
|
102
|
+
const checkIfDeveloperTestAccount = accountConfig => {
|
|
103
|
+
if (!isDeveloperTestAccount(accountConfig)) {
|
|
104
|
+
logger.error(i18n(`${i18nKey}.checkIfDeveloperTestAccount`));
|
|
105
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// If the user isn't using the recommended account type, prompt them to use or create one
|
|
110
|
+
const suggestRecommendedNestedAccount = async (
|
|
111
|
+
accounts,
|
|
112
|
+
accountConfig,
|
|
113
|
+
hasPublicApps
|
|
114
|
+
) => {
|
|
115
|
+
logger.log();
|
|
116
|
+
uiLine();
|
|
117
|
+
if (hasPublicApps) {
|
|
118
|
+
logger.warn(
|
|
119
|
+
i18n(
|
|
120
|
+
`${i18nKey}.suggestRecommendedNestedAccount.publicAppNonDeveloperTestAccountWarning`
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
} else if (isAppDeveloperAccount(accountConfig)) {
|
|
124
|
+
logger.warn(
|
|
125
|
+
i18n(
|
|
126
|
+
`${i18nKey}.suggestRecommendedNestedAccount.publicAppNonDeveloperTestAccountWarning`
|
|
127
|
+
)
|
|
128
|
+
);
|
|
129
|
+
} else {
|
|
130
|
+
logger.warn(
|
|
131
|
+
i18n(`${i18nKey}.suggestRecommendedNestedAccount.nonSandboxWarning`)
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
uiLine();
|
|
135
|
+
logger.log();
|
|
136
|
+
|
|
137
|
+
const targetAccountPrompt = isAppDeveloperAccount(accountConfig)
|
|
138
|
+
? selectDeveloperTestTargetAccountPrompt
|
|
139
|
+
: selectSandboxTargetAccountPrompt;
|
|
140
|
+
|
|
141
|
+
return targetAccountPrompt(accounts, accountConfig, hasPublicApps);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
// Create a new sandbox and return its accountId
|
|
145
|
+
const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
|
|
146
|
+
try {
|
|
147
|
+
await validateSandboxUsageLimits(
|
|
148
|
+
accountConfig,
|
|
149
|
+
HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
150
|
+
env
|
|
151
|
+
);
|
|
152
|
+
} catch (err) {
|
|
153
|
+
if (isMissingScopeError(err)) {
|
|
154
|
+
logger.error(
|
|
155
|
+
i18n('cli.lib.sandbox.create.failure.scopes.message', {
|
|
156
|
+
accountName: accountConfig.name || accountId,
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
const websiteOrigin = getHubSpotWebsiteOrigin(env);
|
|
160
|
+
const url = `${websiteOrigin}/personal-access-key/${accountId}`;
|
|
161
|
+
logger.info(
|
|
162
|
+
i18n('cli.lib.sandbox.create.failure.scopes.instructions', {
|
|
163
|
+
accountName: accountConfig.name || accountId,
|
|
164
|
+
url,
|
|
165
|
+
})
|
|
166
|
+
);
|
|
167
|
+
} else {
|
|
168
|
+
logErrorInstance(err);
|
|
169
|
+
}
|
|
170
|
+
process.exit(EXIT_CODES.ERROR);
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const { name } = await sandboxNamePrompt(
|
|
174
|
+
HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
trackCommandMetadataUsage(
|
|
178
|
+
'sandbox-create',
|
|
179
|
+
{ step: 'project-dev' },
|
|
180
|
+
accountId
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const { result } = await buildSandbox({
|
|
184
|
+
name,
|
|
185
|
+
type: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
186
|
+
accountConfig,
|
|
187
|
+
env,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const targetAccountId = result.sandbox.sandboxHubId;
|
|
191
|
+
|
|
192
|
+
const sandboxAccountConfig = getAccountConfig(result.sandbox.sandboxHubId);
|
|
193
|
+
const syncTasks = await getAvailableSyncTypes(
|
|
194
|
+
accountConfig,
|
|
195
|
+
sandboxAccountConfig
|
|
196
|
+
);
|
|
197
|
+
await syncSandbox({
|
|
198
|
+
accountConfig: sandboxAccountConfig,
|
|
199
|
+
parentAccountConfig: accountConfig,
|
|
200
|
+
env,
|
|
201
|
+
syncTasks,
|
|
202
|
+
allowEarlyTermination: false, // Don't let user terminate early in this flow
|
|
203
|
+
skipPolling: true, // Skip polling, sync will run and complete in the background
|
|
204
|
+
});
|
|
205
|
+
return targetAccountId;
|
|
206
|
+
} catch (err) {
|
|
207
|
+
logErrorInstance(err);
|
|
208
|
+
process.exit(EXIT_CODES.ERROR);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
// Create a developer test account and return its accountId
|
|
213
|
+
const createDeveloperTestAccountForLocalDev = async (
|
|
214
|
+
accountId,
|
|
215
|
+
accountConfig,
|
|
216
|
+
env
|
|
217
|
+
) => {
|
|
218
|
+
let currentPortalCount = 0;
|
|
219
|
+
let maxTestPortals = 10;
|
|
220
|
+
try {
|
|
221
|
+
const validateResult = await validateDevTestAccountUsageLimits(
|
|
222
|
+
accountConfig
|
|
223
|
+
);
|
|
224
|
+
if (validateResult) {
|
|
225
|
+
currentPortalCount = validateResult.results
|
|
226
|
+
? validateResult.results.length
|
|
227
|
+
: 0;
|
|
228
|
+
maxTestPortals = validateResult.maxTestPortals;
|
|
229
|
+
}
|
|
230
|
+
} catch (err) {
|
|
231
|
+
if (isMissingScopeError(err)) {
|
|
232
|
+
logger.error(
|
|
233
|
+
i18n('cli.lib.developerTestAccount.create.failure.scopes.message', {
|
|
234
|
+
accountName: accountConfig.name || accountId,
|
|
235
|
+
})
|
|
236
|
+
);
|
|
237
|
+
const websiteOrigin = getHubSpotWebsiteOrigin(env);
|
|
238
|
+
const url = `${websiteOrigin}/personal-access-key/${accountId}`;
|
|
239
|
+
logger.info(
|
|
240
|
+
i18n(
|
|
241
|
+
'cli.lib.developerTestAccount.create.failure.scopes.instructions',
|
|
242
|
+
{
|
|
243
|
+
accountName: accountConfig.name || accountId,
|
|
244
|
+
url,
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
);
|
|
248
|
+
} else {
|
|
249
|
+
logErrorInstance(err);
|
|
250
|
+
}
|
|
251
|
+
process.exit(EXIT_CODES.ERROR);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const { name } = await developerTestAccountNamePrompt(currentPortalCount);
|
|
256
|
+
trackCommandMetadataUsage(
|
|
257
|
+
'developer-test-account-create',
|
|
258
|
+
{ step: 'project-dev' },
|
|
259
|
+
accountId
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
const { result } = await buildDeveloperTestAccount({
|
|
263
|
+
name,
|
|
264
|
+
accountConfig,
|
|
265
|
+
env,
|
|
266
|
+
maxTestPortals,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
return result.id;
|
|
270
|
+
} catch (err) {
|
|
271
|
+
logErrorInstance(err);
|
|
272
|
+
process.exit(EXIT_CODES.ERROR);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
// Prompt user to confirm usage of an existing developer test account that is not currently in the config
|
|
277
|
+
const useExistingDevTestAccount = async (env, account) => {
|
|
278
|
+
const useExistingDevTestAcct = await confirmUseExistingDeveloperTestAccountPrompt(
|
|
279
|
+
account
|
|
280
|
+
);
|
|
281
|
+
if (!useExistingDevTestAcct) {
|
|
282
|
+
logger.log('');
|
|
283
|
+
logger.log(
|
|
284
|
+
i18n(
|
|
285
|
+
`${i18nKey}.confirmDefaultAccountIsTarget.declineDefaultAccountExplanation`,
|
|
286
|
+
{
|
|
287
|
+
useCommand: uiCommandReference('hs accounts use'),
|
|
288
|
+
devCommand: uiCommandReference('hs project dev'),
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
);
|
|
292
|
+
logger.log('');
|
|
293
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
294
|
+
}
|
|
295
|
+
const devTestAcctConfigName = await saveDevTestAccountToConfig(env, account);
|
|
296
|
+
logger.success(
|
|
297
|
+
i18n(`cli.lib.developerTestAccount.create.success.configFileUpdated`, {
|
|
298
|
+
accountName: devTestAcctConfigName,
|
|
299
|
+
authType: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
|
|
300
|
+
})
|
|
301
|
+
);
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// Prompt the user to create a new project if one doesn't exist on their target account
|
|
305
|
+
const createNewProjectForLocalDev = async (
|
|
306
|
+
projectConfig,
|
|
307
|
+
targetAccountId,
|
|
308
|
+
shouldCreateWithoutConfirmation,
|
|
309
|
+
hasPublicApps
|
|
310
|
+
) => {
|
|
311
|
+
// Create the project without prompting if this is a newly created sandbox
|
|
312
|
+
let shouldCreateProject = shouldCreateWithoutConfirmation;
|
|
313
|
+
|
|
314
|
+
if (!shouldCreateProject) {
|
|
315
|
+
const explanationString = i18n(
|
|
316
|
+
hasPublicApps
|
|
317
|
+
? `${i18nKey}.createNewProjectForLocalDev.publicAppProjectMustExistExplanation`
|
|
318
|
+
: `${i18nKey}.createNewProjectForLocalDev.projectMustExistExplanation`,
|
|
319
|
+
{
|
|
320
|
+
accountIdentifier: uiAccountDescription(targetAccountId),
|
|
321
|
+
projectName: projectConfig.name,
|
|
322
|
+
}
|
|
323
|
+
);
|
|
324
|
+
logger.log();
|
|
325
|
+
uiLine();
|
|
326
|
+
logger.warn(explanationString);
|
|
327
|
+
uiLine();
|
|
328
|
+
|
|
329
|
+
shouldCreateProject = await confirmPrompt(
|
|
330
|
+
i18n(`${i18nKey}.createNewProjectForLocalDev.createProject`, {
|
|
331
|
+
accountIdentifier: uiAccountDescription(targetAccountId),
|
|
332
|
+
projectName: projectConfig.name,
|
|
333
|
+
})
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (shouldCreateProject) {
|
|
338
|
+
SpinniesManager.add('createProject', {
|
|
339
|
+
text: i18n(`${i18nKey}.createNewProjectForLocalDev.creatingProject`, {
|
|
340
|
+
accountIdentifier: uiAccountDescription(targetAccountId),
|
|
341
|
+
projectName: projectConfig.name,
|
|
342
|
+
}),
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
try {
|
|
346
|
+
await createProject(targetAccountId, projectConfig.name);
|
|
347
|
+
SpinniesManager.succeed('createProject', {
|
|
348
|
+
text: i18n(`${i18nKey}.createNewProjectForLocalDev.createdProject`, {
|
|
349
|
+
accountIdentifier: uiAccountDescription(targetAccountId),
|
|
350
|
+
projectName: projectConfig.name,
|
|
351
|
+
}),
|
|
352
|
+
succeedColor: 'white',
|
|
353
|
+
});
|
|
354
|
+
} catch (err) {
|
|
355
|
+
SpinniesManager.fail('createProject');
|
|
356
|
+
logger.log(
|
|
357
|
+
i18n(`${i18nKey}.createNewProjectForLocalDev.failedToCreateProject`)
|
|
358
|
+
);
|
|
359
|
+
process.exit(EXIT_CODES.ERROR);
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
// We cannot continue if the project does not exist in the target account
|
|
363
|
+
logger.log();
|
|
364
|
+
logger.log(
|
|
365
|
+
i18n(`${i18nKey}.createNewProjectForLocalDev.choseNotToCreateProject`)
|
|
366
|
+
);
|
|
367
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// Create an initial build if the project was newly created in the account
|
|
372
|
+
// Return the newly deployed build
|
|
373
|
+
const createInitialBuildForNewProject = async (
|
|
374
|
+
projectConfig,
|
|
375
|
+
projectDir,
|
|
376
|
+
targetAccountId
|
|
377
|
+
) => {
|
|
378
|
+
const initialUploadResult = await handleProjectUpload(
|
|
379
|
+
targetAccountId,
|
|
380
|
+
projectConfig,
|
|
381
|
+
projectDir,
|
|
382
|
+
(...args) => pollProjectBuildAndDeploy(...args, true),
|
|
383
|
+
i18n(`${i18nKey}.createInitialBuildForNewProject.initialUploadMessage`)
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
if (initialUploadResult.uploadError) {
|
|
387
|
+
if (
|
|
388
|
+
isSpecifiedError(initialUploadResult.uploadError, {
|
|
389
|
+
subCategory: PROJECT_ERROR_TYPES.PROJECT_LOCKED,
|
|
390
|
+
})
|
|
391
|
+
) {
|
|
392
|
+
logger.log();
|
|
393
|
+
logger.error(
|
|
394
|
+
i18n(`${i18nKey}.createInitialBuildForNewProject.projectLockedError`)
|
|
395
|
+
);
|
|
396
|
+
logger.log();
|
|
397
|
+
} else {
|
|
398
|
+
logApiErrorInstance(
|
|
399
|
+
initialUploadResult.uploadError,
|
|
400
|
+
new ApiErrorContext({
|
|
401
|
+
accountId: targetAccountId,
|
|
402
|
+
projectName: projectConfig.name,
|
|
403
|
+
})
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
process.exit(EXIT_CODES.ERROR);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (!initialUploadResult.succeeded) {
|
|
410
|
+
let subTasks = [];
|
|
411
|
+
|
|
412
|
+
if (initialUploadResult.buildResult.status === 'FAILURE') {
|
|
413
|
+
subTasks =
|
|
414
|
+
initialUploadResult.buildResult[PROJECT_BUILD_TEXT.SUBTASK_KEY];
|
|
415
|
+
} else if (initialUploadResult.deployResult.status === 'FAILURE') {
|
|
416
|
+
subTasks =
|
|
417
|
+
initialUploadResult.deployResult[PROJECT_DEPLOY_TEXT.SUBTASK_KEY];
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
const failedSubTasks = subTasks.filter(task => task.status === 'FAILURE');
|
|
421
|
+
|
|
422
|
+
logger.log();
|
|
423
|
+
failedSubTasks.forEach(failedSubTask => {
|
|
424
|
+
console.error(failedSubTask.errorMessage);
|
|
425
|
+
});
|
|
426
|
+
logger.log();
|
|
427
|
+
|
|
428
|
+
process.exit(EXIT_CODES.ERROR);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return initialUploadResult.buildResult;
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
module.exports = {
|
|
435
|
+
confirmDefaultAccountIsTarget,
|
|
436
|
+
checkIfAppDeveloperAccount,
|
|
437
|
+
checkIfDeveloperTestAccount,
|
|
438
|
+
suggestRecommendedNestedAccount,
|
|
439
|
+
createSandboxForLocalDev,
|
|
440
|
+
createDeveloperTestAccountForLocalDev,
|
|
441
|
+
useExistingDevTestAccount,
|
|
442
|
+
createNewProjectForLocalDev,
|
|
443
|
+
createInitialBuildForNewProject,
|
|
444
|
+
};
|
package/lib/projectStructure.js
CHANGED
|
@@ -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
|
|
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
|
};
|
package/lib/projects.js
CHANGED
|
@@ -26,6 +26,8 @@ const {
|
|
|
26
26
|
getDeployStructure,
|
|
27
27
|
fetchProject,
|
|
28
28
|
uploadProject,
|
|
29
|
+
fetchBuildWarnLogs,
|
|
30
|
+
fetchDeployWarnLogs,
|
|
29
31
|
} = require('@hubspot/local-dev-lib/api/projects');
|
|
30
32
|
const {
|
|
31
33
|
isSpecifiedError,
|
|
@@ -445,7 +447,10 @@ const pollProjectBuildAndDeploy = async (
|
|
|
445
447
|
}
|
|
446
448
|
)
|
|
447
449
|
);
|
|
450
|
+
|
|
451
|
+
displayWarnLogs(accountId, projectConfig.name, buildId);
|
|
448
452
|
}
|
|
453
|
+
|
|
449
454
|
const deployStatus = await pollDeployStatus(
|
|
450
455
|
accountId,
|
|
451
456
|
projectConfig.name,
|
|
@@ -473,6 +478,14 @@ const pollProjectBuildAndDeploy = async (
|
|
|
473
478
|
logger.error(e);
|
|
474
479
|
}
|
|
475
480
|
|
|
481
|
+
if (result && result.deployResult) {
|
|
482
|
+
displayWarnLogs(
|
|
483
|
+
accountId,
|
|
484
|
+
projectConfig.name,
|
|
485
|
+
result.deployResult.deployId,
|
|
486
|
+
true
|
|
487
|
+
);
|
|
488
|
+
}
|
|
476
489
|
return result;
|
|
477
490
|
};
|
|
478
491
|
|
|
@@ -871,6 +884,28 @@ const createProjectComponent = async (
|
|
|
871
884
|
);
|
|
872
885
|
};
|
|
873
886
|
|
|
887
|
+
const displayWarnLogs = async (
|
|
888
|
+
accountId,
|
|
889
|
+
projectName,
|
|
890
|
+
taskId,
|
|
891
|
+
isDeploy = false
|
|
892
|
+
) => {
|
|
893
|
+
let result;
|
|
894
|
+
|
|
895
|
+
if (isDeploy) {
|
|
896
|
+
result = await fetchDeployWarnLogs(accountId, projectName, taskId);
|
|
897
|
+
} else {
|
|
898
|
+
result = await fetchBuildWarnLogs(accountId, projectName, taskId);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (result && result.logs.length) {
|
|
902
|
+
result.logs.forEach(log => {
|
|
903
|
+
logger.warn(log.message);
|
|
904
|
+
logger.log('');
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
|
|
874
909
|
module.exports = {
|
|
875
910
|
writeProjectConfig,
|
|
876
911
|
getProjectConfig,
|
|
@@ -887,4 +922,5 @@ module.exports = {
|
|
|
887
922
|
ensureProjectExists,
|
|
888
923
|
logFeedbackMessage,
|
|
889
924
|
createProjectComponent,
|
|
925
|
+
displayWarnLogs,
|
|
890
926
|
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const { promptUser } = require('./promptUtils');
|
|
2
|
+
const { i18n } = require('../lang');
|
|
3
|
+
const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
|
|
4
|
+
|
|
5
|
+
const i18nKey = 'cli.lib.prompts.developerTestAccountPrompt';
|
|
6
|
+
|
|
7
|
+
const developerTestAccountNamePrompt = currentPortalCount => {
|
|
8
|
+
return promptUser([
|
|
9
|
+
{
|
|
10
|
+
name: 'name',
|
|
11
|
+
message: i18n(`${i18nKey}.name.message`),
|
|
12
|
+
validate(val) {
|
|
13
|
+
if (typeof val !== 'string') {
|
|
14
|
+
return i18n(`${i18nKey}.name.errors.invalidName`);
|
|
15
|
+
} else if (!val.length) {
|
|
16
|
+
return i18n(`${i18nKey}.name.errors.nameRequired`);
|
|
17
|
+
}
|
|
18
|
+
return accountNameExistsInConfig(val)
|
|
19
|
+
? i18n(`${i18nKey}.name.errors.accountNameExists`, { name: val })
|
|
20
|
+
: true;
|
|
21
|
+
},
|
|
22
|
+
default: `Developer test account ${currentPortalCount + 1}`,
|
|
23
|
+
},
|
|
24
|
+
]);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
developerTestAccountNamePrompt,
|
|
29
|
+
};
|
|
@@ -34,6 +34,24 @@ const previewPrompt = (promptOptions = {}) => {
|
|
|
34
34
|
]);
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
+
const previewProjectPrompt = async themeComponents => {
|
|
38
|
+
return promptUser([
|
|
39
|
+
{
|
|
40
|
+
name: 'themeComponentPath',
|
|
41
|
+
message: i18n(`${i18nKey}.themeProjectSelect`),
|
|
42
|
+
type: 'list',
|
|
43
|
+
choices: themeComponents.map(t => {
|
|
44
|
+
const themeName = path.basename(t.path);
|
|
45
|
+
return {
|
|
46
|
+
name: themeName,
|
|
47
|
+
value: t.path,
|
|
48
|
+
};
|
|
49
|
+
}),
|
|
50
|
+
},
|
|
51
|
+
]);
|
|
52
|
+
};
|
|
53
|
+
|
|
37
54
|
module.exports = {
|
|
38
55
|
previewPrompt,
|
|
56
|
+
previewProjectPrompt,
|
|
39
57
|
};
|