@hubspot/cli 5.2.1-beta.3 → 5.2.1-beta.5

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.
@@ -1,7 +1,7 @@
1
1
  const open = require('open');
2
2
 
3
3
  const { i18n } = require('../lib/lang');
4
- const { FEEDBACK_OPTIONS, FEEDBACK_URLS } = require('../lib/constants');
4
+ const { FEEDBACK_URLS } = require('../lib/constants');
5
5
  const { logger } = require('@hubspot/local-dev-lib/logger');
6
6
 
7
7
  const {
@@ -22,10 +22,8 @@ exports.handler = async options => {
22
22
  const { shouldOpen } = await shouldOpenBrowserPrompt(type, usedTypeFlag);
23
23
 
24
24
  if (shouldOpen || usedTypeFlag) {
25
- const url =
26
- type === FEEDBACK_OPTIONS.BUG || bugFlag
27
- ? FEEDBACK_URLS.BUG
28
- : FEEDBACK_URLS.GENERAL;
25
+ // NOTE: for now, all feedback should go to the hubspot-cli repository
26
+ const url = FEEDBACK_URLS.BUG;
29
27
  open(url, { url: true });
30
28
  logger.success(i18n(`${i18nKey}.success`, { url }));
31
29
  }
@@ -79,8 +79,8 @@ exports.handler = async options => {
79
79
 
80
80
  const components = await findProjectComponents(projectDir);
81
81
  const componentTypes = getProjectComponentTypes(components);
82
- const hasPrivateApps = componentTypes[COMPONENT_TYPES.privateApp];
83
- const hasPublicApps = componentTypes[COMPONENT_TYPES.publicApp];
82
+ const hasPrivateApps = !!componentTypes[COMPONENT_TYPES.privateApp];
83
+ const hasPublicApps = !!componentTypes[COMPONENT_TYPES.publicApp];
84
84
 
85
85
  if (hasPrivateApps && hasPublicApps) {
86
86
  logger.error(i18n(`${i18nKey}.errors.invalidProjectComponents`));
@@ -178,7 +178,7 @@ exports.handler = async options => {
178
178
  targetProjectAccountId = accountId;
179
179
  }
180
180
 
181
- const { projectExists, project } = await ensureProjectExists(
181
+ let { projectExists, project } = await ensureProjectExists(
182
182
  targetProjectAccountId,
183
183
  projectConfig.name,
184
184
  {
@@ -199,7 +199,7 @@ exports.handler = async options => {
199
199
  project.sourceIntegration &&
200
200
  project.sourceIntegration.source === 'GITHUB';
201
201
  } else {
202
- await createNewProjectForLocalDev(
202
+ project = await createNewProjectForLocalDev(
203
203
  projectConfig,
204
204
  targetProjectAccountId,
205
205
  createNewSandbox,
@@ -221,7 +221,9 @@ exports.handler = async options => {
221
221
  parentAccountId: targetProjectAccountId,
222
222
  projectConfig,
223
223
  projectDir,
224
+ projectId: project.id,
224
225
  targetAccountId: targetTestingAccountId,
226
+ env,
225
227
  });
226
228
 
227
229
  await LocalDev.start();
@@ -93,7 +93,7 @@ exports.handler = async options => {
93
93
  uiLine();
94
94
  logFeedbackMessage(result.buildId);
95
95
 
96
- displayWarnLogs(accountId, projectConfig.name, result.buildId);
96
+ await displayWarnLogs(accountId, projectConfig.name, result.buildId);
97
97
  process.exit(EXIT_CODES.SUCCESS);
98
98
  }
99
99
  } catch (e) {
package/lang/en.lyaml CHANGED
@@ -648,7 +648,7 @@ en:
648
648
  bug:
649
649
  describe: "Open Github issues in your browser to report a bug."
650
650
  general:
651
- describe: "Open the projects feedback form in your browser."
651
+ describe: "Open Github issues in your browser to give feedback."
652
652
  remove:
653
653
  describe: "Delete a file or folder from HubSpot."
654
654
  deleted: "Deleted \"{{ path }}\" from account {{ accountId }}"
@@ -917,7 +917,7 @@ en:
917
917
  noCompatibleComponents: "Skipping call to {{ serverKey }} because there are no compatible components in the project."
918
918
  LocalDevManager:
919
919
  failedToInitialize: "Missing required arguments to initialize Local Dev"
920
- noDeployedBuild: "There is no deployed build for this project in {{ accountIdentifier }}. Run {{ uploadCommand }} to upload and deploy your project."
920
+ noDeployedBuild: "Your project {{#bold}}{{ projectName }}{{/bold}} exists in {{ accountIdentifier }}, but has no deployed build. Projects must be successfully deployed to be developed locally. Address any build and deploy errors your project may have, then run {{ uploadCommand }} to upload and deploy your project."
921
921
  noComponents: "There are no components in this project."
922
922
  noRunnableComponents: "No supported components were found under {{#bold}}{{ projectSourceDir }}{{/bold}}. Run {{ command }} to see a list of available components."
923
923
  betaMessage: "HubSpot projects local development"
@@ -927,16 +927,24 @@ en:
927
927
  exitingStart: "Stopping local dev server ..."
928
928
  exitingSucceed: "Successfully exited"
929
929
  exitingFail: "Failed to cleanup before exiting"
930
+ missingUid: "Could not find a uid for the selected app. Confirm that the app config file contains the uid field and re-run {{ devCommand }}."
930
931
  uploadWarning:
931
932
  appLabel: "[App]"
932
933
  uiExtensionLabel: "[UI Extension]"
933
934
  missingComponents: "Couldn't find the following components in the deployed build for this project: {{#bold}}'{{ missingComponents }}'{{/bold}}. This may cause issues in local development."
934
935
  defaultWarning: "Changing project configuration requires a new project build."
936
+ 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."
935
937
  header: "{{ warning }} To reflect these changes and continue testing:"
936
938
  stopDev: " * Stop {{ command }}"
937
939
  runUpload: " * Run {{ command }}"
938
940
  restartDev: " * Re-run {{ command }}"
939
941
  pushToGithub: " * Commit and push your changes to GitHub"
942
+ activeInstallWarning:
943
+ installCount: "{{#bold}}The app {{ appName }} has {{ installCount }} production installs{{/bold}}"
944
+ genericHeader: "{{#bold}}Local development can affect existing installs of your public app.{{/bold}}"
945
+ 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."
946
+ 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."
947
+ confirmation: "You will always be asked to confirm any permanent changes to your app’s configuration before uploading them."
940
948
  devServer:
941
949
  cleanupError: "Failed to cleanup local dev server: {{ message }}"
942
950
  setupError: "Failed to setup local dev server: {{ message }}"
@@ -950,7 +958,7 @@ en:
950
958
  suggestRecommendedNestedAccount:
951
959
  nonSandboxWarning: "Testing in a sandbox is strongly recommended. To switch the target account, select an option below or run {{#bold}}`hs accounts use`{{/bold}} before running the command again."
952
960
  publicAppNonDeveloperTestAccountWarning: "Local development of public apps is only supported in {{#bold}}developer test accounts{{/bold}}."
953
- privateAppNonDeveloperTestAccountWarning: "Local development of private apps is only supported in {{#bold}}developer test accounts{{/bold}}"
961
+ privateAppInAppDeveloperAccountError: "Local development of private apps is not supported in {{#bold}}app developer accounts{{/bold}}"
954
962
  createNewProjectForLocalDev:
955
963
  projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
956
964
  publicAppProjectMustExistExplanation: "The project {{ projectName }} does not exist in {{ accountIdentifier}}, the app developer account associated with your target account. This command requires the project to exist in this app developer account."
@@ -1216,7 +1224,7 @@ en:
1216
1224
  bug: "[--bug] Report a bug"
1217
1225
  general: "[--general] Tell us about your experience with HubSpot's developer tools"
1218
1226
  bugPrompt: "Create an issue on Github in your browser?"
1219
- generalPrompt: "Open the projects feedback form in your browser?"
1227
+ generalPrompt: "Create an issue on Github in your browser?"
1220
1228
  buildIdPrompt:
1221
1229
  enterBuildId: "[--build] Deploy which build?"
1222
1230
  errors:
@@ -1231,6 +1239,10 @@ en:
1231
1239
  destRequired: "You must specify a destination directory."
1232
1240
  cleanUploadPrompt:
1233
1241
  message: "You are about to remove any remote files in \"{{ filePath }}\" on HubSpot account {{ accountId }} that don't exist locally. Are you sure you want to do this?"
1242
+ installPublicAppPrompt:
1243
+ explanation: "Local development requires this app to be installed in the target test account"
1244
+ prompt: "Open hubspot.com to install this app?"
1245
+ decline: "To continue local development of this app, install it in your target test account and re-run {{#bold}}`hs project dev`{{/bold}}"
1234
1246
  convertFields:
1235
1247
  positionals:
1236
1248
  src:
@@ -4,6 +4,12 @@ const chalk = require('chalk');
4
4
  const { i18n } = require('./lang');
5
5
  const { handleKeypress } = require('./process');
6
6
  const { logger } = require('@hubspot/local-dev-lib/logger');
7
+ const {
8
+ fetchAppInstallationData,
9
+ } = require('@hubspot/local-dev-lib/api/localDevAuth');
10
+ const {
11
+ fetchPublicAppsForPortal,
12
+ } = require('@hubspot/local-dev-lib/api/appsDev');
7
13
  const {
8
14
  getAccountId,
9
15
  getConfigDefaultAccount,
@@ -26,6 +32,8 @@ const {
26
32
  uiLink,
27
33
  uiLine,
28
34
  } = require('./ui');
35
+ const { logErrorInstance } = require('./errorHandlers/standardErrors');
36
+ const { installPublicAppPrompt } = require('./prompts/installPublicAppPrompt');
29
37
 
30
38
  const WATCH_EVENTS = {
31
39
  add: 'add',
@@ -44,6 +52,7 @@ class LocalDevManager {
44
52
 
45
53
  this.projectConfig = options.projectConfig;
46
54
  this.projectDir = options.projectDir;
55
+ this.projectId = options.projectId;
47
56
  this.debug = options.debug || false;
48
57
  this.deployedBuild = options.deployedBuild;
49
58
  this.isGithubLinked = options.isGithubLinked;
@@ -51,6 +60,8 @@ class LocalDevManager {
51
60
  this.uploadWarnings = {};
52
61
  this.runnableComponents = this.getRunnableComponents(options.components);
53
62
  this.activeApp = null;
63
+ this.activePublicAppData = null;
64
+ this.env = options.env;
54
65
 
55
66
  this.projectSourceDir = path.join(
56
67
  this.projectDir,
@@ -84,10 +95,57 @@ class LocalDevManager {
84
95
  return components.filter(component => component.runnable);
85
96
  }
86
97
 
87
- async setActiveApp(app) {
88
- this.activeApp = app;
98
+ async setActiveApp(appUid) {
99
+ if (!appUid) {
100
+ logger.error(
101
+ i18n(`${i18nKey}.missingUid`, {
102
+ devCommand: uiCommandReference('hs project dev'),
103
+ })
104
+ );
105
+ process.exit(EXIT_CODES.ERROR);
106
+ }
107
+ this.activeApp = this.runnableComponents.find(component => {
108
+ return component.config.uid === appUid;
109
+ });
89
110
 
90
- // TODO: Show the install warnings the first time this gets set
111
+ if (this.activeApp.type === COMPONENT_TYPES.publicApp) {
112
+ try {
113
+ await this.setActivePublicAppData();
114
+ await this.checkActivePublicAppInstalls();
115
+ await this.checkPublicAppInstallation();
116
+ } catch (e) {
117
+ logErrorInstance(e);
118
+ }
119
+ }
120
+ }
121
+
122
+ async setActivePublicAppData() {
123
+ if (!this.activeApp) {
124
+ return;
125
+ }
126
+
127
+ const portalPublicApps = await fetchPublicAppsForPortal(
128
+ this.targetProjectAccountId
129
+ );
130
+
131
+ const activePublicAppData = portalPublicApps.find(
132
+ ({ sourceId }) => sourceId === this.activeApp.config.uid
133
+ );
134
+
135
+ this.activePublicAppData = activePublicAppData;
136
+ }
137
+
138
+ async checkActivePublicAppInstalls() {
139
+ // TODO: Add check for installs once we have that info
140
+ if (!this.activePublicAppData) {
141
+ return;
142
+ }
143
+ uiLine();
144
+ // TODO: Replace with final copy
145
+
146
+ logger.warn(i18n(`${i18nKey}.activeInstallWarning.genericHeader`));
147
+ logger.log(i18n(`${i18nKey}.activeInstallWarning.genericExplanation`));
148
+ uiLine();
91
149
  }
92
150
 
93
151
  async start() {
@@ -98,6 +156,7 @@ class LocalDevManager {
98
156
  if (!this.deployedBuild) {
99
157
  logger.error(
100
158
  i18n(`${i18nKey}.noDeployedBuild`, {
159
+ projectName: this.projectConfig.name,
101
160
  accountIdentifier: uiAccountDescription(this.targetProjectAccountId),
102
161
  uploadCommand: this.getUploadCommand(),
103
162
  })
@@ -179,6 +238,32 @@ class LocalDevManager {
179
238
  process.exit(EXIT_CODES.SUCCESS);
180
239
  }
181
240
 
241
+ getActiveAppInstallationData() {
242
+ return fetchAppInstallationData(
243
+ this.targetAccountId,
244
+ this.projectId,
245
+ this.activeApp.config.uid,
246
+ this.activeApp.config.auth.requiredScopes,
247
+ this.activeApp.config.auth.optionalScopes
248
+ );
249
+ }
250
+
251
+ async checkPublicAppInstallation() {
252
+ const {
253
+ isInstalledWithScopeGroups: isInstalled,
254
+ } = await this.getActiveAppInstallationData();
255
+
256
+ if (!isInstalled) {
257
+ await installPublicAppPrompt(
258
+ this.env,
259
+ this.targetAccountId,
260
+ this.activePublicAppData.clientId,
261
+ this.activeApp.config.auth.requiredScopes,
262
+ this.activeApp.config.auth.redirectUrls
263
+ );
264
+ }
265
+ }
266
+
182
267
  updateKeypressListeners() {
183
268
  handleKeypress(async key => {
184
269
  if ((key.ctrl && key.name === 'c') || key.name === 'q') {
@@ -198,7 +283,13 @@ class LocalDevManager {
198
283
  }
199
284
 
200
285
  logUploadWarning(reason) {
201
- const warning = reason || i18n(`${i18nKey}.uploadWarning.defaultWarning`);
286
+ let warning = reason;
287
+ if (!reason) {
288
+ warning =
289
+ this.activeApp.type === COMPONENT_TYPES.publicApp
290
+ ? i18n(`${i18nKey}.uploadWarning.defaultPublicAppWarning`)
291
+ : i18n(`${i18nKey}.uploadWarning.defaultWarning`);
292
+ }
202
293
 
203
294
  // Avoid logging the warning to the console if it is currently the most
204
295
  // recently logged warning. We do not want to spam the console with the same message.
package/lib/localDev.js CHANGED
@@ -114,19 +114,20 @@ const suggestRecommendedNestedAccount = async (
114
114
  logger.log();
115
115
  uiLine();
116
116
  if (hasPublicApps) {
117
- logger.warn(
117
+ logger.log(
118
118
  i18n(
119
119
  `${i18nKey}.suggestRecommendedNestedAccount.publicAppNonDeveloperTestAccountWarning`
120
120
  )
121
121
  );
122
122
  } else if (isAppDeveloperAccount(accountConfig)) {
123
- logger.warn(
123
+ logger.error(
124
124
  i18n(
125
- `${i18nKey}.suggestRecommendedNestedAccount.publicAppNonDeveloperTestAccountWarning`
125
+ `${i18nKey}.suggestRecommendedNestedAccount.privateAppInAppDeveloperAccountError`
126
126
  )
127
127
  );
128
+ process.exit(EXIT_CODES.ERROR);
128
129
  } else {
129
- logger.warn(
130
+ logger.log(
130
131
  i18n(`${i18nKey}.suggestRecommendedNestedAccount.nonSandboxWarning`)
131
132
  );
132
133
  }
@@ -290,7 +291,7 @@ const useExistingDevTestAccount = async (env, account) => {
290
291
  }
291
292
  const devTestAcctConfigName = await saveDevTestAccountToConfig(env, account);
292
293
  logger.success(
293
- i18n(`cli.lib.developerTestAccount.create.success.configFileUpdated`, {
294
+ i18n(`lib.developerTestAccount.create.success.configFileUpdated`, {
294
295
  accountName: devTestAcctConfigName,
295
296
  authType: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
296
297
  })
@@ -319,7 +320,7 @@ const createNewProjectForLocalDev = async (
319
320
  );
320
321
  logger.log();
321
322
  uiLine();
322
- logger.warn(explanationString);
323
+ logger.log(explanationString);
323
324
  uiLine();
324
325
 
325
326
  shouldCreateProject = await confirmPrompt(
@@ -339,7 +340,7 @@ const createNewProjectForLocalDev = async (
339
340
  });
340
341
 
341
342
  try {
342
- await createProject(targetAccountId, projectConfig.name);
343
+ const project = await createProject(targetAccountId, projectConfig.name);
343
344
  SpinniesManager.succeed('createProject', {
344
345
  text: i18n(`${i18nKey}.createNewProjectForLocalDev.createdProject`, {
345
346
  accountIdentifier: uiAccountDescription(targetAccountId),
@@ -347,6 +348,7 @@ const createNewProjectForLocalDev = async (
347
348
  }),
348
349
  succeedColor: 'white',
349
350
  });
351
+ return project;
350
352
  } catch (err) {
351
353
  SpinniesManager.fail('createProject');
352
354
  logger.log(
@@ -417,7 +419,7 @@ const createInitialBuildForNewProject = async (
417
419
 
418
420
  logger.log();
419
421
  failedSubTasks.forEach(failedSubTask => {
420
- console.error(failedSubTask.errorMessage);
422
+ logger.error(failedSubTask.errorMessage);
421
423
  });
422
424
  logger.log();
423
425
 
package/lib/projects.js CHANGED
@@ -893,9 +893,17 @@ const displayWarnLogs = async (
893
893
  let result;
894
894
 
895
895
  if (isDeploy) {
896
- result = await fetchDeployWarnLogs(accountId, projectName, taskId);
896
+ try {
897
+ result = await fetchDeployWarnLogs(accountId, projectName, taskId);
898
+ } catch (e) {
899
+ logApiErrorInstance(e);
900
+ }
897
901
  } else {
898
- result = await fetchBuildWarnLogs(accountId, projectName, taskId);
902
+ try {
903
+ result = await fetchBuildWarnLogs(accountId, projectName, taskId);
904
+ } catch (e) {
905
+ logApiErrorInstance(e);
906
+ }
899
907
  }
900
908
 
901
909
  if (result && result.logs.length) {
@@ -0,0 +1,42 @@
1
+ const open = require('open');
2
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
3
+ const { logger } = require('@hubspot/local-dev-lib/logger');
4
+ const { promptUser } = require('./promptUtils');
5
+ const { i18n } = require('../lang');
6
+ const { EXIT_CODES } = require('../enums/exitCodes');
7
+
8
+ const i18nKey = 'lib.prompts.installPublicAppPrompt';
9
+
10
+ const installPublicAppPrompt = async (
11
+ env,
12
+ targetAccountId,
13
+ clientId,
14
+ scopes,
15
+ redirectUrls
16
+ ) => {
17
+ logger.log('');
18
+ logger.log(i18n(`${i18nKey}.explanation`));
19
+
20
+ const { shouldOpenBrowser } = await promptUser({
21
+ name: 'shouldOpenBrowser',
22
+ type: 'confirm',
23
+ message: i18n(`${i18nKey}.prompt`),
24
+ });
25
+
26
+ if (!shouldOpenBrowser) {
27
+ logger.log(i18n(`${i18nKey}.decline`));
28
+ process.exit(EXIT_CODES.SUCCESS);
29
+ }
30
+
31
+ const websiteOrigin = getHubSpotWebsiteOrigin(env);
32
+
33
+ const url =
34
+ `${websiteOrigin}/oauth/${targetAccountId}/authorize` +
35
+ `?client_id=${encodeURIComponent(clientId)}` +
36
+ `&scope=${encodeURIComponent(scopes)}` +
37
+ `&redirect_uri=${encodeURIComponent(redirectUrls)}`;
38
+
39
+ open(url);
40
+ };
41
+
42
+ module.exports = { installPublicAppPrompt };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "5.2.1-beta.3",
3
+ "version": "5.2.1-beta.5",
4
4
  "description": "CLI for working with HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,10 +8,10 @@
8
8
  "url": "https://github.com/HubSpot/hubspot-cms-tools"
9
9
  },
10
10
  "dependencies": {
11
- "@hubspot/local-dev-lib": "1.0.1",
12
- "@hubspot/serverless-dev-runtime": "5.2.1-beta.2",
13
- "@hubspot/theme-preview-dev-server": "0.0.5",
14
- "@hubspot/ui-extensions-dev-server": "0.8.15",
11
+ "@hubspot/local-dev-lib": "1.1.0",
12
+ "@hubspot/serverless-dev-runtime": "5.2.1-beta.4",
13
+ "@hubspot/theme-preview-dev-server": "0.0.6",
14
+ "@hubspot/ui-extensions-dev-server": "0.8.17",
15
15
  "archiver": "^5.3.0",
16
16
  "chalk": "^4.1.2",
17
17
  "chokidar": "^3.0.1",
@@ -45,5 +45,5 @@
45
45
  "publishConfig": {
46
46
  "access": "public"
47
47
  },
48
- "gitHead": "70d81f550d7916e8eb80ec22ce41ac51265c9a36"
48
+ "gitHead": "21195f0f24e2e148f37c0320f0b31c2ebad817ff"
49
49
  }