@hubspot/cli 5.2.1-beta.8 → 5.2.1-beta.9

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/commands/auth.js CHANGED
@@ -31,9 +31,7 @@ const {
31
31
  personalAccessKeyPrompt,
32
32
  OAUTH_FLOW,
33
33
  } = require('../lib/prompts/personalAccessKeyPrompt');
34
- const {
35
- enterAccountNamePrompt,
36
- } = require('../lib/prompts/enterAccountNamePrompt');
34
+ const { cliAccountNamePrompt } = require('../lib/prompts/accountNamePrompt');
37
35
  const {
38
36
  setAsDefaultAccountPrompt,
39
37
  } = require('../lib/prompts/setAsDefaultAccountPrompt');
@@ -129,7 +127,7 @@ exports.handler = async options => {
129
127
  validName = updatedConfig.name;
130
128
 
131
129
  if (!validName) {
132
- const { name: namePrompt } = await enterAccountNamePrompt(defaultName);
130
+ const { name: namePrompt } = await cliAccountNamePrompt(defaultName);
133
131
  validName = namePrompt;
134
132
  }
135
133
 
package/commands/init.js CHANGED
@@ -40,9 +40,7 @@ const {
40
40
  OAUTH_FLOW,
41
41
  personalAccessKeyPrompt,
42
42
  } = require('../lib/prompts/personalAccessKeyPrompt');
43
- const {
44
- enterAccountNamePrompt,
45
- } = require('../lib/prompts/enterAccountNamePrompt');
43
+ const { cliAccountNamePrompt } = require('../lib/prompts/accountNamePrompt');
46
44
  const { logDebugInfo } = require('../lib/debugInfo');
47
45
  const { authenticateWithOauth } = require('../lib/oauth');
48
46
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
@@ -63,7 +61,7 @@ const personalAccessKeyConfigCreationFlow = async (env, account) => {
63
61
  try {
64
62
  const token = await getAccessToken(personalAccessKey, env);
65
63
  const defaultName = token.hubName ? toKebabCase(token.hubName) : null;
66
- const { name } = await enterAccountNamePrompt(defaultName);
64
+ const { name } = await cliAccountNamePrompt(defaultName);
67
65
 
68
66
  updatedConfig = updateConfigWithAccessToken(
69
67
  token,
@@ -48,7 +48,7 @@ exports.handler = async options => {
48
48
  accountId,
49
49
  validationId
50
50
  );
51
- processValidationErrors(validationResults);
51
+ processValidationErrors(i18nKey, validationResults);
52
52
  displayValidationResults(i18nKey, validationResults);
53
53
 
54
54
  process.exit();
@@ -32,6 +32,7 @@ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
32
32
  const { promptUser } = require('../../lib/prompts/promptUtils');
33
33
  const { isAppDeveloperAccount } = require('../../lib/accountTypes');
34
34
  const { ensureProjectExists } = require('../../lib/projects');
35
+ const { handleKeypress } = require('../../lib/process');
35
36
  const {
36
37
  migrateApp,
37
38
  checkMigrationStatus,
@@ -58,14 +59,15 @@ exports.handler = async options => {
58
59
  trackCommandUsage('migrate-app', {}, accountId);
59
60
 
60
61
  if (!isAppDeveloperAccount(accountConfig)) {
61
- logger.error(
62
- i18n(`${i18nKey}.errors.invalidAccountType`, {
63
- accountName,
64
- accountType: accountConfig.accountType,
62
+ uiLine();
63
+ logger.error(i18n(`${i18nKey}.errors.invalidAccountTypeTitle`));
64
+ logger.log(
65
+ i18n(`${i18nKey}.errors.invalidAccountTypeDescription`, {
65
66
  useCommand: uiCommandReference('hs accounts use'),
66
67
  authCommand: uiCommandReference('hs auth'),
67
68
  })
68
69
  );
70
+ uiLine();
69
71
  process.exit(EXIT_CODES.ERROR);
70
72
  }
71
73
 
@@ -75,12 +77,13 @@ exports.handler = async options => {
75
77
  : await selectPublicAppPrompt({
76
78
  accountId,
77
79
  accountName,
78
- options,
79
80
  migrateApp: true,
80
81
  });
81
82
 
82
83
  const publicApps = await fetchPublicAppOptions(accountId, accountName);
83
- if (!publicApps.find(a => a.id === appId)) {
84
+ const selectedApp = publicApps.find(a => a.id === appId);
85
+ const appName = selectedApp ? selectedApp.name : 'app';
86
+ if (!selectedApp) {
84
87
  logger.error(i18n(`${i18nKey}.errors.invalidAppId`, { appId }));
85
88
  process.exit(EXIT_CODES.ERROR);
86
89
  }
@@ -106,7 +109,7 @@ exports.handler = async options => {
106
109
 
107
110
  logger.log('');
108
111
  uiLine();
109
- logger.log(uiBetaTag(i18n(`${i18nKey}.warning.title`), false));
112
+ logger.log(uiBetaTag(i18n(`${i18nKey}.warning.title`, { appName }), false));
110
113
  logger.log(i18n(`${i18nKey}.warning.projectConversion`));
111
114
  logger.log(i18n(`${i18nKey}.warning.appConfig`));
112
115
  logger.log('');
@@ -122,6 +125,7 @@ exports.handler = async options => {
122
125
  type: 'confirm',
123
126
  message: i18n(`${i18nKey}.createAppPrompt`),
124
127
  });
128
+ process.stdin.resume();
125
129
 
126
130
  if (!shouldCreateApp) {
127
131
  process.exit(EXIT_CODES.SUCCESS);
@@ -134,6 +138,14 @@ exports.handler = async options => {
134
138
  text: i18n(`${i18nKey}.migrationStatus.inProgress`),
135
139
  });
136
140
 
141
+ handleKeypress(async key => {
142
+ if ((key.ctrl && key.name === 'c') || key.name === 'q') {
143
+ SpinniesManager.remove('migrateApp');
144
+ logger.log(i18n(`${i18nKey}.migrationInterrupted`));
145
+ process.exit(EXIT_CODES.SUCCESS);
146
+ }
147
+ });
148
+
137
149
  const migrateResponse = await migrateApp(accountId, appId, projectName);
138
150
  const { id } = migrateResponse;
139
151
  const pollResponse = await poll(checkMigrationStatus, accountId, id);
@@ -173,10 +185,11 @@ exports.handler = async options => {
173
185
  text: i18n(`${i18nKey}.migrationStatus.failure`),
174
186
  failColor: 'white',
175
187
  });
176
- logApiErrorInstance(
177
- error.error || error,
178
- new ApiErrorContext({ accountId })
179
- );
188
+ if (error.errors) {
189
+ error.errors.forEach(logApiErrorInstance);
190
+ } else {
191
+ logApiErrorInstance(error, new ApiErrorContext({ accountId }));
192
+ }
180
193
 
181
194
  process.exit(EXIT_CODES.ERROR);
182
195
  }
@@ -22,10 +22,7 @@ const {
22
22
  trackCommandUsage,
23
23
  trackCommandMetadataUsage,
24
24
  } = require('../../lib/usageTracking');
25
- const {
26
- sandboxTypePrompt,
27
- sandboxNamePrompt,
28
- } = require('../../lib/prompts/sandboxesPrompt');
25
+ const { sandboxTypePrompt } = require('../../lib/prompts/sandboxesPrompt');
29
26
  const { promptUser } = require('../../lib/prompts/promptUtils');
30
27
  const { syncSandbox } = require('../../lib/sandboxSync');
31
28
  const { logErrorInstance } = require('../../lib/errorHandlers/standardErrors');
@@ -38,6 +35,9 @@ const {
38
35
  HUBSPOT_ACCOUNT_TYPE_STRINGS,
39
36
  } = require('@hubspot/local-dev-lib/constants/config');
40
37
  const { buildNewAccount } = require('../../lib/buildAccount');
38
+ const {
39
+ hubspotAccountNamePrompt,
40
+ } = require('../../lib/prompts/accountNamePrompt');
41
41
 
42
42
  const i18nKey = 'commands.sandbox.subcommands.create';
43
43
 
@@ -112,7 +112,7 @@ exports.handler = async options => {
112
112
 
113
113
  if (!name) {
114
114
  if (!force) {
115
- namePrompt = await sandboxNamePrompt(sandboxType);
115
+ namePrompt = await hubspotAccountNamePrompt({ accountType: sandboxType });
116
116
  } else {
117
117
  logger.error(i18n(`${i18nKey}.failure.optionMissing.name`));
118
118
  process.exit(EXIT_CODES.ERROR);
@@ -48,7 +48,7 @@ exports.handler = async options => {
48
48
  accountId,
49
49
  validationId
50
50
  );
51
- processValidationErrors(validationResults);
51
+ processValidationErrors(i18nKey, validationResults);
52
52
  displayValidationResults(i18nKey, validationResults);
53
53
 
54
54
  process.exit();
package/lang/en.lyaml CHANGED
@@ -514,18 +514,18 @@ en:
514
514
  done: "Converting app configuration to public-app.json component definition ... DONE"
515
515
  failure: "Converting app configuration to public-app.json component definition ... FAILED"
516
516
  warning:
517
- title: "{{#bold}}Migrate an app to the projects framework?{{/bold}}"
518
- projectConversion: "{{#bold}}The selected app will be permanently converted to a project component.{{/bold}}"
517
+ title: "{{#bold}}Migrate {{appName}} to the projects framework?{{/bold}}"
518
+ projectConversion: "{{#bold}}The selected app will be converted to a project component.{{/bold}}"
519
519
  appConfig: "All supported app configuration will be moved to the {{#bold}}public-app.json{{/bold}} component definition file. Future updates to those features must be made through the project build and deploy pipeline, not the developer account UI."
520
520
  buildAndDeploy: "This will create a new project with a single app component and immediately build and deploy it to your developer account (build #1)."
521
521
  existingApps: "{{#bold}}This will not affect existing app users or installs.{{/bold}}"
522
522
  copyApp: "We strongly recommend making a copy of your app to test this process in a development app before replacing production."
523
- createAppPrompt: "Proceed with migrating this app to a project component?"
523
+ migrationInterrupted: "\nThe command is terminated, but app migration is still in progress. Please check your account to ensure that the project and associated app have been created successfully."
524
+ createAppPrompt: "Proceed with migrating this app to a project component (this process can't be aborted)?"
524
525
  projectDetailsLink: "View project details in your developer account"
525
526
  errors:
526
- invalidAccountType: "Public apps must be migrated within an app developer account. Your current account {{#bold}}{{ accountName }}{{/bold}} is a {{ accountType }}.
527
- \n- Run {{ useCommand }} to switch your default account to an app developer account.
528
- \n- Run {{ authCommand }} to connect an app developer account to the HubSpot CLI.\n"
527
+ invalidAccountTypeTitle: "{{#bold}}Developer account not targeted{{/bold}}"
528
+ invalidAccountTypeDescription: "Only public apps created in a developer account can be converted to a project component. Select a connected developer account with {{useCommand}} or {{authCommand}} and try again."
529
529
  projectAlreadyExists: "A project with name {{ projectName }} already exists. Please choose another name."
530
530
  invalidAppId: "[--appId] Could not find appId {{ appId }}. Please choose another public app."
531
531
  add:
@@ -959,7 +959,8 @@ en:
959
959
  learnMoreLocalDevServer: "Learn more about the projects local dev server"
960
960
  running: "Running {{#bold}}{{ projectName }}{{/bold}} locally on {{ accountIdentifier }}, waiting for changes ..."
961
961
  quitHelper: "Press {{#bold}}'q'{{/bold}} to stop the local dev server"
962
- viewInHubSpotLink: "View project in HubSpot"
962
+ viewProjectLink: "View project in HubSpot"
963
+ viewTestAccountLink: "View developer test account in HubSpot"
963
964
  exitingStart: "Stopping local dev server ..."
964
965
  exitingSucceed: "Successfully exited"
965
966
  exitingFail: "Failed to cleanup before exiting"
@@ -968,17 +969,15 @@ en:
968
969
  appLabel: "[App]"
969
970
  uiExtensionLabel: "[UI Extension]"
970
971
  missingComponents: "Couldn't find the following components in the deployed build for this project: {{#bold}}'{{ missingComponents }}'{{/bold}}. This may cause issues in local development."
971
- defaultWarning: "Changing project configuration requires a new project build."
972
- defaultPublicAppWarning: "Changing project configuration requires a new project build. This will affect existing users of your public app. If your app has users in production, we strongly recommend creating a copy of this app to test your changes before proceding."
972
+ defaultWarning: "{{#bold}}Changing project configuration requires a new project build.{{/bold}}"
973
+ defaultPublicAppWarning: "{{#bold}}Changing project configuration requires a new project build.{{/bold}}\n\nThis will affect your public app's {{#bold}}{{ installCount }} existing {{ installText }}{{/bold}}. If your app has users in production, we strongly recommend creating a copy of this app to test your changes before proceding."
973
974
  header: "{{ warning }} To reflect these changes and continue testing:"
974
975
  stopDev: " * Stop {{ command }}"
975
976
  runUpload: " * Run {{ command }}"
976
977
  restartDev: " * Re-run {{ command }}"
977
978
  pushToGithub: " * Commit and push your changes to GitHub"
978
979
  activeInstallWarning:
979
- installCount: "{{#bold}}The app {{ appName }} has {{ installCount }} production installs{{/bold}}"
980
- genericHeader: "{{#bold}}Local development can affect existing installs of your public app.{{/bold}}"
981
- genericExplanation: "Some changes made during local development may need to be synced to HubSpot, which will impact users with existing installs. You will always be asked to confirm these changes before uploading them. If your app has any production installs, we strongly recommend creating a copy of this app to develop on instead."
980
+ installCount: "{{#bold}}The app {{ appName }} has {{ installCount }} production {{ installText }}{{/bold}}"
982
981
  explanation: "Some changes made during local development may need to be synced to HubSpot, which will impact those existing installs. We strongly recommend creating a copy of this app to use instead."
983
982
  confirmation: "You will always be asked to confirm any permanent changes to your app’s configuration before uploading them."
984
983
  devServer:
@@ -1143,8 +1142,13 @@ en:
1143
1142
  setAsDefaultAccountMessage: "Set this account as the default?"
1144
1143
  setAsDefaultAccount: "Account \"{{ accountName }}\" set as the default account"
1145
1144
  keepingCurrentDefault: "Account \"{{ accountName }}\" will continue to be the default account"
1146
- enterAccountNamePrompt:
1145
+ accountNamePrompt:
1147
1146
  enterAccountName: "Enter a unique name to reference this account in the CLI:"
1147
+ enterDeveloperTestAccountName: "Name your developer test account:"
1148
+ enterStandardSandboxName: "Name your standard sandbox:"
1149
+ enterDevelopmentSandboxName: "Name your development sandbox:"
1150
+ sandboxDefaultName: "New {{ sandboxType }} sandbox"
1151
+ developerTestAccountDefaultName: "Developer test account {{ count }}"
1148
1152
  errors:
1149
1153
  invalidName: "You entered an invalid name. Please try again."
1150
1154
  nameRequired: "The name may not be blank. Please try again."
@@ -1217,13 +1221,7 @@ en:
1217
1221
  noAppsMessage: "The selected developer account {{#bold}}{{ accountName }}{{/bold}} doesn't have any apps that can be migrated to the projects framework."
1218
1222
  errorFetchingApps: "There was an error fetching public apps."
1219
1223
  invalidAppId: "[--appId] Could not find appId {{ appId }}. Please choose another public app."
1220
- developerTestAccountPrompt:
1221
- name:
1222
- message: "Name your developer test account"
1223
- errors:
1224
- invalidName: "You entered an invalid name. Please try again."
1225
- nameRequired: "The name may not be blank. Please try again."
1226
- accountNameExists: "Account with name \"{{ name }}\" already exists in the CLI config, please enter a different name."
1224
+ marketplaceApp: "Marketplace app"
1227
1225
  downloadProjectPrompt:
1228
1226
  selectProject: "Select a project to download:"
1229
1227
  errors:
@@ -1239,15 +1237,8 @@ en:
1239
1237
  errors:
1240
1238
  invalidValue: "You entered an invalid value. Please try again."
1241
1239
  sandboxesPrompt:
1242
- name:
1243
- message: "Name your sandbox"
1244
- developmentSandboxMessage: "Name your development sandbox"
1245
- errors:
1246
- invalidName: "You entered an invalid name. Please try again."
1247
- nameRequired: "The name may not be blank. Please try again."
1248
- accountNameExists: "Account with name \"{{ name }}\" already exists in the CLI config, please enter a different name."
1249
- selectAccountName: "Select the sandbox account you want to delete"
1250
- selectParentAccountName: "Select the account that the sandbox belongs to"
1240
+ selectAccountName: "Select the sandbox account you want to delete"
1241
+ selectParentAccountName: "Select the account that the sandbox belongs to"
1251
1242
  type:
1252
1243
  message: "What type of sandbox would you like to create?"
1253
1244
  developer: "Development sandbox (Isolated environment for developers)"
@@ -1289,6 +1280,8 @@ en:
1289
1280
  explanation: "Local development requires this app to be installed in the target test account"
1290
1281
  prompt: "Open hubspot.com to install this app?"
1291
1282
  decline: "To continue local development of this app, install it in your target test account and re-run {{#bold}}`hs project dev`{{/bold}}"
1283
+ activeInstallConfirmationPrompt:
1284
+ message: "Proceed with local development of this {{#bold}}production{{/bold}} app?"
1292
1285
  convertFields:
1293
1286
  positionals:
1294
1287
  src:
@@ -9,6 +9,7 @@ const {
9
9
  } = require('@hubspot/local-dev-lib/api/localDevAuth');
10
10
  const {
11
11
  fetchPublicAppsForPortal,
12
+ fetchPublicAppDeveloperTestAccountInstallData,
12
13
  } = require('@hubspot/local-dev-lib/api/appsDev');
13
14
  const {
14
15
  getAccountId,
@@ -19,6 +20,7 @@ const SpinniesManager = require('./ui/SpinniesManager');
19
20
  const DevServerManager = require('./DevServerManager');
20
21
  const { EXIT_CODES } = require('./enums/exitCodes');
21
22
  const { getProjectDetailUrl } = require('./projects');
23
+ const { getAccountHomeUrl } = require('./localDev');
22
24
  const {
23
25
  CONFIG_FILES,
24
26
  COMPONENT_TYPES,
@@ -34,6 +36,9 @@ const {
34
36
  } = require('./ui');
35
37
  const { logErrorInstance } = require('./errorHandlers/standardErrors');
36
38
  const { installPublicAppPrompt } = require('./prompts/installPublicAppPrompt');
39
+ const {
40
+ activeInstallConfirmationPrompt,
41
+ } = require('./prompts/activeInstallConfirmationPrompt');
37
42
 
38
43
  const WATCH_EVENTS = {
39
44
  add: 'add',
@@ -62,6 +67,7 @@ class LocalDevManager {
62
67
  this.activeApp = null;
63
68
  this.activePublicAppData = null;
64
69
  this.env = options.env;
70
+ this.publicAppActiveInstalls = null;
65
71
 
66
72
  this.projectSourceDir = path.join(
67
73
  this.projectDir,
@@ -132,20 +138,44 @@ class LocalDevManager {
132
138
  ({ sourceId }) => sourceId === this.activeApp.config.uid
133
139
  );
134
140
 
141
+ const {
142
+ testPortalInstallCount,
143
+ } = await fetchPublicAppDeveloperTestAccountInstallData(
144
+ activePublicAppData.id,
145
+ this.targetProjectAccountId
146
+ );
147
+
135
148
  this.activePublicAppData = activePublicAppData;
149
+ this.publicAppActiveInstalls =
150
+ activePublicAppData.publicApplicationInstallCounts
151
+ .uniquePortalInstallCount - testPortalInstallCount;
136
152
  }
137
153
 
138
154
  async checkActivePublicAppInstalls() {
139
- // TODO: Add check for installs once we have that info
140
- if (!this.activePublicAppData) {
155
+ if (
156
+ !this.activePublicAppData ||
157
+ !this.publicAppActiveInstalls ||
158
+ this.publicAppActiveInstalls < 1
159
+ ) {
141
160
  return;
142
161
  }
143
162
  uiLine();
144
- // TODO: Replace with final copy
145
163
 
146
- logger.warn(i18n(`${i18nKey}.activeInstallWarning.genericHeader`));
147
- logger.log(i18n(`${i18nKey}.activeInstallWarning.genericExplanation`));
164
+ logger.warn(
165
+ i18n(`${i18nKey}.activeInstallWarning.installCount`, {
166
+ appName: this.activePublicAppData.name,
167
+ installCount: this.publicAppActiveInstalls,
168
+ installText:
169
+ this.publicAppActiveInstalls === 1 ? 'install' : 'installs',
170
+ })
171
+ );
172
+ logger.log(i18n(`${i18nKey}.activeInstallWarning.explanation`));
148
173
  uiLine();
174
+ const proceed = await activeInstallConfirmationPrompt();
175
+
176
+ if (!proceed) {
177
+ process.exit(EXIT_CODES.SUCCESS);
178
+ }
149
179
  }
150
180
 
151
181
  async start() {
@@ -193,13 +223,23 @@ class LocalDevManager {
193
223
  );
194
224
  logger.log(
195
225
  uiLink(
196
- i18n(`${i18nKey}.viewInHubSpotLink`),
226
+ i18n(`${i18nKey}.viewProjectLink`),
197
227
  getProjectDetailUrl(
198
228
  this.projectConfig.name,
199
229
  this.targetProjectAccountId
200
230
  )
201
231
  )
202
232
  );
233
+
234
+ if (this.activeApp.type === COMPONENT_TYPES.publicApp) {
235
+ logger.log(
236
+ uiLink(
237
+ i18n(`${i18nKey}.viewTestAccountLink`),
238
+ getAccountHomeUrl(this.targetAccountId)
239
+ )
240
+ );
241
+ }
242
+
203
243
  logger.log();
204
244
  logger.log(i18n(`${i18nKey}.quitHelper`));
205
245
  uiLine();
@@ -294,8 +334,13 @@ class LocalDevManager {
294
334
  let warning = reason;
295
335
  if (!reason) {
296
336
  warning =
297
- this.activeApp.type === COMPONENT_TYPES.publicApp
298
- ? i18n(`${i18nKey}.uploadWarning.defaultPublicAppWarning`)
337
+ this.activeApp.type === COMPONENT_TYPES.publicApp &&
338
+ this.publicAppActiveInstalls > 0
339
+ ? i18n(`${i18nKey}.uploadWarning.defaultPublicAppWarning`, {
340
+ installCount: this.publicAppActiveInstalls,
341
+ installText:
342
+ this.publicAppActiveInstalls === 1 ? 'install' : 'installs',
343
+ })
299
344
  : i18n(`${i18nKey}.uploadWarning.defaultWarning`);
300
345
  }
301
346
 
@@ -13,7 +13,7 @@ const {
13
13
  } = require('@hubspot/local-dev-lib/config');
14
14
  const { logger } = require('@hubspot/local-dev-lib/logger');
15
15
  const { i18n } = require('./lang');
16
- const { enterAccountNamePrompt } = require('./prompts/enterAccountNamePrompt');
16
+ const { cliAccountNamePrompt } = require('./prompts/accountNamePrompt');
17
17
  const SpinniesManager = require('./ui/SpinniesManager');
18
18
  const {
19
19
  debugErrorAndContext,
@@ -65,11 +65,11 @@ async function saveAccountToConfig({
65
65
  if (!force) {
66
66
  logger.log('');
67
67
  logger.warn(
68
- i18n(`lib.prompts.enterAccountNamePrompt.errors.accountNameExists`, {
68
+ i18n(`lib.prompts.accountNamePrompt.errors.accountNameExists`, {
69
69
  name: nameForConfig,
70
70
  })
71
71
  );
72
- const { name: promptName } = await enterAccountNamePrompt(
72
+ const { name: promptName } = await cliAccountNamePrompt(
73
73
  nameForConfig + `_${accountId}`
74
74
  );
75
75
  validName = promptName;
package/lib/localDev.js CHANGED
@@ -8,18 +8,17 @@ const {
8
8
  isSpecifiedError,
9
9
  } = require('@hubspot/local-dev-lib/errors/apiErrors');
10
10
  const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
11
- const { getAccountConfig } = require('@hubspot/local-dev-lib/config');
11
+ const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
12
12
  const { createProject } = require('@hubspot/local-dev-lib/api/projects');
13
+ const {
14
+ ENVIRONMENTS,
15
+ } = require('@hubspot/local-dev-lib/constants/environments');
13
16
  const {
14
17
  confirmDefaultAccountPrompt,
15
18
  selectSandboxTargetAccountPrompt,
16
19
  selectDeveloperTestTargetAccountPrompt,
17
20
  confirmUseExistingDeveloperTestAccountPrompt,
18
21
  } = require('./prompts/projectDevTargetAccountPrompt');
19
- const { sandboxNamePrompt } = require('./prompts/sandboxesPrompt');
20
- const {
21
- developerTestAccountNamePrompt,
22
- } = require('./prompts/developerTestAccountNamePrompt');
23
22
  const { confirmPrompt } = require('./prompts/promptUtils');
24
23
  const {
25
24
  validateSandboxUsageLimits,
@@ -56,6 +55,7 @@ const {
56
55
  PERSONAL_ACCESS_KEY_AUTH_METHOD,
57
56
  } = require('@hubspot/local-dev-lib/constants/auth');
58
57
  const { buildNewAccount, saveAccountToConfig } = require('./buildAccount');
58
+ const { hubspotAccountNamePrompt } = require('./prompts/accountNamePrompt');
59
59
 
60
60
  const i18nKey = 'lib.localDev';
61
61
 
@@ -163,9 +163,9 @@ const createSandboxForLocalDev = async (accountId, accountConfig, env) => {
163
163
  process.exit(EXIT_CODES.ERROR);
164
164
  }
165
165
  try {
166
- const { name } = await sandboxNamePrompt(
167
- HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX
168
- );
166
+ const { name } = await hubspotAccountNamePrompt({
167
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
168
+ });
169
169
 
170
170
  trackCommandMetadataUsage(
171
171
  'sandbox-create',
@@ -242,7 +242,10 @@ const createDeveloperTestAccountForLocalDev = async (
242
242
  }
243
243
 
244
244
  try {
245
- const { name } = await developerTestAccountNamePrompt(currentPortalCount);
245
+ const { name } = await hubspotAccountNamePrompt({
246
+ currentPortalCount,
247
+ accountType: HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST,
248
+ });
246
249
  trackCommandMetadataUsage(
247
250
  'developer-test-account-create',
248
251
  { step: 'project-dev' },
@@ -427,6 +430,13 @@ const createInitialBuildForNewProject = async (
427
430
  return initialUploadResult.buildResult;
428
431
  };
429
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
+
430
440
  module.exports = {
431
441
  confirmDefaultAccountIsTarget,
432
442
  checkIfAppDeveloperAccount,
@@ -437,4 +447,5 @@ module.exports = {
437
447
  useExistingDevTestAccount,
438
448
  createNewProjectForLocalDev,
439
449
  createInitialBuildForNewProject,
450
+ getAccountHomeUrl,
440
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
  }
@@ -0,0 +1,81 @@
1
+ const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
2
+ const { STRING_WITH_NO_SPACES_REGEX } = require('../regex');
3
+ const { promptUser } = require('./promptUtils');
4
+ const { i18n } = require('../lang');
5
+ const {
6
+ HUBSPOT_ACCOUNT_TYPES,
7
+ } = require('@hubspot/local-dev-lib/constants/config');
8
+
9
+ const i18nKey = 'lib.prompts.accountNamePrompt';
10
+
11
+ const getCliAccountNamePromptConfig = defaultName => ({
12
+ name: 'name',
13
+ message: i18n(`${i18nKey}.enterAccountName`),
14
+ default: defaultName,
15
+ validate(val) {
16
+ if (typeof val !== 'string') {
17
+ return i18n(`${i18nKey}.errors.invalidName`);
18
+ } else if (!val.length) {
19
+ return i18n(`${i18nKey}.errors.nameRequired`);
20
+ } else if (!STRING_WITH_NO_SPACES_REGEX.test(val)) {
21
+ return i18n(`${i18nKey}.errors.spacesInName`);
22
+ }
23
+ return accountNameExistsInConfig(val)
24
+ ? i18n(`${i18nKey}.errors.accountNameExists`, { name: val })
25
+ : true;
26
+ },
27
+ });
28
+
29
+ const cliAccountNamePrompt = defaultName => {
30
+ return promptUser(getCliAccountNamePromptConfig(defaultName));
31
+ };
32
+
33
+ const hubspotAccountNamePrompt = ({ accountType, currentPortalCount = 0 }) => {
34
+ const isDevelopmentSandbox =
35
+ accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX;
36
+ const isSandbox =
37
+ accountType === HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX ||
38
+ isDevelopmentSandbox;
39
+ const isDeveloperTestAccount =
40
+ accountType === HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST;
41
+
42
+ let promptMessageString;
43
+ let defaultName;
44
+ if (isSandbox) {
45
+ promptMessageString = isDevelopmentSandbox
46
+ ? i18n(`${i18nKey}.enterDevelopmentSandboxName`)
47
+ : i18n(`${i18nKey}.enterStandardSandboxName`);
48
+ defaultName = i18n(`${i18nKey}.sandboxDefaultName`, {
49
+ sandboxType: isDevelopmentSandbox ? 'development' : 'standard',
50
+ });
51
+ } else if (isDeveloperTestAccount) {
52
+ promptMessageString = i18n(`${i18nKey}.enterDeveloperTestAccountName`);
53
+ defaultName = i18n(`${i18nKey}.developerTestAccountDefaultName`, {
54
+ count: currentPortalCount + 1,
55
+ });
56
+ }
57
+
58
+ return promptUser([
59
+ {
60
+ name: 'name',
61
+ message: promptMessageString,
62
+ validate(val) {
63
+ if (typeof val !== 'string') {
64
+ return i18n(`${i18nKey}.errors.invalidName`);
65
+ } else if (!val.length) {
66
+ return i18n(`${i18nKey}.errors.nameRequired`);
67
+ }
68
+ return accountNameExistsInConfig(val)
69
+ ? i18n(`${i18nKey}.errors.accountNameExists`, { name: val })
70
+ : true;
71
+ },
72
+ default: defaultName,
73
+ },
74
+ ]);
75
+ };
76
+
77
+ module.exports = {
78
+ getCliAccountNamePromptConfig,
79
+ cliAccountNamePrompt,
80
+ hubspotAccountNamePrompt,
81
+ };
@@ -0,0 +1,20 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { i18n } = require('../lang');
3
+
4
+ const i18nKey = 'lib.prompts.activeInstallConfirmationPrompt';
5
+
6
+ const activeInstallConfirmationPrompt = async () => {
7
+ const { proceed } = await promptUser([
8
+ {
9
+ name: 'proceed',
10
+ message: i18n(`${i18nKey}.message`),
11
+ type: 'confirm',
12
+ default: false,
13
+ },
14
+ ]);
15
+ return proceed;
16
+ };
17
+
18
+ module.exports = {
19
+ activeInstallConfirmationPrompt,
20
+ };
@@ -7,7 +7,7 @@ const { deleteEmptyConfigFile } = require('@hubspot/local-dev-lib/config');
7
7
  const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
8
8
  const { logger } = require('@hubspot/local-dev-lib/logger');
9
9
  const { promptUser } = require('./promptUtils');
10
- const { accountNamePrompt } = require('./enterAccountNamePrompt');
10
+ const { getCliAccountNamePromptConfig } = require('./accountNamePrompt');
11
11
  const { i18n } = require('../lang');
12
12
  const { uiInfoSection } = require('../ui');
13
13
  const { EXIT_CODES } = require('../enums/exitCodes');
@@ -124,7 +124,7 @@ const SCOPES = {
124
124
  };
125
125
 
126
126
  const OAUTH_FLOW = [
127
- accountNamePrompt(),
127
+ getCliAccountNamePromptConfig(),
128
128
  ACCOUNT_ID,
129
129
  CLIENT_ID,
130
130
  CLIENT_SECRET,
@@ -1,6 +1,5 @@
1
1
  const { promptUser } = require('./promptUtils');
2
2
  const { i18n } = require('../lang');
3
- const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
4
3
  const { uiAccountDescription } = require('../ui');
5
4
  const {
6
5
  HUBSPOT_ACCOUNT_TYPES,
@@ -29,42 +28,6 @@ const mapNonSandboxAccountChoices = portals =>
29
28
  };
30
29
  });
31
30
 
32
- const sandboxNamePrompt = (type = HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX) => {
33
- const isDevelopmentSandbox =
34
- type === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX;
35
- const namePromptMessage = isDevelopmentSandbox
36
- ? `${i18nKey}.name.developmentSandboxMessage`
37
- : `${i18nKey}.name.message`;
38
- return promptUser([
39
- {
40
- name: 'name',
41
- message: i18n(namePromptMessage),
42
- validate(val) {
43
- if (typeof val !== 'string') {
44
- return i18n(`${i18nKey}.name.errors.invalidName`);
45
- } else if (!val.length) {
46
- return i18n(`${i18nKey}.name.errors.nameRequired`);
47
- }
48
- return accountNameExistsInConfig(val)
49
- ? i18n(`${i18nKey}.name.errors.accountNameExists`, { name: val })
50
- : true;
51
- },
52
- default: `New ${isDevelopmentSandbox ? 'development ' : ''}sandbox`,
53
- },
54
- ]);
55
- };
56
-
57
- const sandboxTypeChoices = [
58
- {
59
- name: i18n(`${i18nKey}.type.developer`),
60
- value: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
61
- },
62
- {
63
- name: i18n(`${i18nKey}.type.standard`),
64
- value: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
65
- },
66
- ];
67
-
68
31
  const sandboxTypePrompt = () => {
69
32
  return promptUser([
70
33
  {
@@ -72,7 +35,16 @@ const sandboxTypePrompt = () => {
72
35
  message: i18n(`${i18nKey}.type.message`),
73
36
  type: 'list',
74
37
  look: false,
75
- choices: sandboxTypeChoices,
38
+ choices: [
39
+ {
40
+ name: i18n(`${i18nKey}.type.developer`),
41
+ value: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
42
+ },
43
+ {
44
+ name: i18n(`${i18nKey}.type.standard`),
45
+ value: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
46
+ },
47
+ ],
76
48
  default: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
77
49
  },
78
50
  ]);
@@ -90,8 +62,8 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
90
62
  name: 'account',
91
63
  message: i18n(
92
64
  promptParentAccount
93
- ? `${i18nKey}.name.selectParentAccountName`
94
- : `${i18nKey}.name.selectAccountName`
65
+ ? `${i18nKey}.selectParentAccountName`
66
+ : `${i18nKey}.selectAccountName`
95
67
  ),
96
68
  type: 'list',
97
69
  look: false,
@@ -103,7 +75,6 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
103
75
  };
104
76
 
105
77
  module.exports = {
106
- sandboxNamePrompt,
107
78
  sandboxTypePrompt,
108
79
  deleteSandboxPrompt,
109
80
  };
@@ -35,7 +35,6 @@ const fetchPublicAppOptions = async (accountId, accountName) => {
35
35
  const selectPublicAppPrompt = async ({
36
36
  accountId,
37
37
  accountName,
38
- promptOptions = {},
39
38
  migrateApp = false,
40
39
  }) => {
41
40
  const publicApps = await fetchPublicAppOptions(accountId, accountName);
@@ -44,21 +43,17 @@ const selectPublicAppPrompt = async ({
44
43
  return promptUser([
45
44
  {
46
45
  name: 'appId',
47
- message: () => {
48
- return promptOptions.appId &&
49
- !publicApps.find(a => a.id === promptOptions.appId)
50
- ? i18n(`${i18nKey}.errors.invalidAppId`, {
51
- appId: promptOptions.appId,
52
- })
53
- : i18n(`${i18nKey}.${translationKey}`, {
54
- accountName,
55
- });
56
- },
57
- when:
58
- !promptOptions.appId ||
59
- !publicApps.find(a => a.id === promptOptions.appId),
46
+ message: i18n(`${i18nKey}.${translationKey}`, {
47
+ accountName,
48
+ }),
60
49
  type: 'list',
61
50
  choices: publicApps.map(app => {
51
+ if (app.listingInfo) {
52
+ return {
53
+ name: `${app.name} (${app.id})`,
54
+ disabled: i18n(`${i18nKey}.errors.marketplaceApp`),
55
+ };
56
+ }
62
57
  return {
63
58
  name: `${app.name} (${app.id})`,
64
59
  value: app.id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "5.2.1-beta.8",
3
+ "version": "5.2.1-beta.9",
4
4
  "description": "CLI for working with HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,8 +8,8 @@
8
8
  "url": "https://github.com/HubSpot/hubspot-cms-tools"
9
9
  },
10
10
  "dependencies": {
11
- "@hubspot/local-dev-lib": "1.4.0",
12
- "@hubspot/serverless-dev-runtime": "5.2.1-beta.7",
11
+ "@hubspot/local-dev-lib": "1.5.0",
12
+ "@hubspot/serverless-dev-runtime": "5.2.1-beta.9",
13
13
  "@hubspot/theme-preview-dev-server": "0.0.7",
14
14
  "@hubspot/ui-extensions-dev-server": "0.8.20",
15
15
  "archiver": "^5.3.0",
@@ -45,5 +45,5 @@
45
45
  "publishConfig": {
46
46
  "access": "public"
47
47
  },
48
- "gitHead": "d36fbe19c7daae6e47b6cb421f4c90ea935d9c9e"
48
+ "gitHead": "3b67796a5b4eee5dd8d78fd7407d5b44e81cd3da"
49
49
  }
@@ -1,29 +0,0 @@
1
- const { promptUser } = require('./promptUtils');
2
- const { i18n } = require('../lang');
3
- const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
4
-
5
- const i18nKey = '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
- };
@@ -1,33 +0,0 @@
1
- const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
2
- const { STRING_WITH_NO_SPACES_REGEX } = require('../regex');
3
- const { promptUser } = require('./promptUtils');
4
- const { i18n } = require('../lang');
5
-
6
- const i18nKey = 'lib.prompts.enterAccountNamePrompt';
7
-
8
- const accountNamePrompt = defaultName => ({
9
- name: 'name',
10
- message: i18n(`${i18nKey}.enterAccountName`),
11
- default: defaultName,
12
- validate(val) {
13
- if (typeof val !== 'string') {
14
- return i18n(`${i18nKey}.errors.invalidName`);
15
- } else if (!val.length) {
16
- return i18n(`${i18nKey}.errors.nameRequired`);
17
- } else if (!STRING_WITH_NO_SPACES_REGEX.test(val)) {
18
- return i18n(`${i18nKey}.errors.spacesInName`);
19
- }
20
- return accountNameExistsInConfig(val)
21
- ? i18n(`${i18nKey}.errors.accountNameExists`, { name: val })
22
- : true;
23
- },
24
- });
25
-
26
- const enterAccountNamePrompt = defaultName => {
27
- return promptUser(accountNamePrompt(defaultName));
28
- };
29
-
30
- module.exports = {
31
- accountNamePrompt,
32
- enterAccountNamePrompt,
33
- };