@hubspot/cli 4.0.1-beta.2 → 4.0.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.
package/README.md CHANGED
@@ -133,7 +133,7 @@ hs hubdb delete <id or name>
133
133
 
134
134
  ## Authentication
135
135
 
136
- There are three ways that the tools can authenticate with HubSpot.
136
+ There are two ways that the tools can authenticate with HubSpot.
137
137
 
138
138
  ### Personal Access Key (recommended)
139
139
 
@@ -146,20 +146,6 @@ There are three ways that the tools can authenticate with HubSpot.
146
146
  3. Select `OAuth2` and follow the steps
147
147
 
148
148
  _**Note:** The Account ID used should be the Test Account ID (not the developer app ID). Client ID and Client Secret are from the developer app._
149
-
150
- ### HubSpot API Key
151
-
152
- 1. [Set up an API Key for the Account](https://knowledge.hubspot.com/articles/kcs_article/integrations/how-do-i-get-my-hubspot-api-key)
153
- 2. Edit the [hubspot.config.yml](../../docs/HubspotConfigFile.md) file to set the `authType` for the account to `apikey` and add `apiKey` as shown below:
154
-
155
- ```yaml
156
- defaultPortal: DEV
157
- portals:
158
- - name: DEV
159
- portalId: 123
160
- authType: apikey
161
- apiKey: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
162
- ```
163
149
  ### Exit Codes
164
150
 
165
151
  The CLI will exit with one of the following exit codes:
package/bin/cli.js CHANGED
@@ -35,6 +35,7 @@ const moduleCommand = require('../commands/module');
35
35
  const configCommand = require('../commands/config');
36
36
  const accountsCommand = require('../commands/accounts');
37
37
  const sandboxesCommand = require('../commands/sandbox');
38
+ const cmsCommand = require('../commands/cms');
38
39
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
39
40
 
40
41
  const notifier = updateNotifier({ pkg: { ...pkg, name: '@hubspot/cli' } });
@@ -129,6 +130,7 @@ const argv = yargs
129
130
  .command(authCommand)
130
131
  .command(initCommand)
131
132
  .command(logsCommand)
133
+ .command(cmsCommand)
132
134
  .command(lintCommand)
133
135
  .command(hubdbCommand)
134
136
  .command(watchCommand)
@@ -5,47 +5,24 @@ const {
5
5
  updateDefaultAccount,
6
6
  getAccountId: getAccountIdFromConfig,
7
7
  } = require('@hubspot/cli-lib/lib/config');
8
- const { loadAndValidateOptions } = require('../../lib/validation');
9
8
 
10
- const { getAccountId } = require('../../lib/commonOpts');
11
9
  const { trackCommandUsage } = require('../../lib/usageTracking');
12
- const { promptUser } = require('../../lib/prompts/promptUtils');
13
10
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
11
+ const { selectAccountFromConfig } = require('../../lib/prompts/accountsPrompt');
12
+ const { loadAndValidateOptions } = require('../../lib/validation');
14
13
 
15
14
  const i18nKey = 'cli.commands.accounts.subcommands.use';
16
15
 
17
- const selectAccountFromConfig = async config => {
18
- const { default: selectedDefault } = await promptUser([
19
- {
20
- type: 'list',
21
- look: false,
22
- name: 'default',
23
- pageSize: 20,
24
- message: i18n(`${i18nKey}.promptMessage`),
25
- choices: config.portals.map(p => ({
26
- name: `${p.name} (${p.portalId})`,
27
- value: p.name || p.portalId,
28
- })),
29
- default: config.defaultPortal,
30
- },
31
- ]);
32
-
33
- return selectedDefault;
34
- };
35
-
36
16
  exports.command = 'use [--account]';
37
17
  exports.describe = i18n(`${i18nKey}.describe`);
38
18
 
39
19
  exports.handler = async options => {
40
- await loadAndValidateOptions({ debug: options.debug });
20
+ await loadAndValidateOptions(options, false);
41
21
 
42
- const accountId = getAccountId(options);
43
22
  const config = getConfig();
44
23
 
45
24
  let newDefaultAccount = options.account;
46
25
 
47
- trackCommandUsage('accounts-use', {}, accountId);
48
-
49
26
  if (!newDefaultAccount) {
50
27
  newDefaultAccount = await selectAccountFromConfig(config);
51
28
  } else if (!getAccountIdFromConfig(newDefaultAccount)) {
@@ -58,6 +35,12 @@ exports.handler = async options => {
58
35
  newDefaultAccount = await selectAccountFromConfig(config);
59
36
  }
60
37
 
38
+ trackCommandUsage(
39
+ 'accounts-use',
40
+ {},
41
+ getAccountIdFromConfig(newDefaultAccount)
42
+ );
43
+
61
44
  updateDefaultAccount(newDefaultAccount);
62
45
 
63
46
  return logger.success(
package/commands/auth.js CHANGED
@@ -3,7 +3,6 @@ const { logger } = require('@hubspot/cli-lib/logger');
3
3
  const {
4
4
  OAUTH_AUTH_METHOD,
5
5
  PERSONAL_ACCESS_KEY_AUTH_METHOD,
6
- API_KEY_AUTH_METHOD,
7
6
  ENVIRONMENTS,
8
7
  DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
9
8
  } = require('@hubspot/cli-lib/lib/constants');
@@ -22,7 +21,6 @@ const { promptUser } = require('../lib/prompts/promptUtils');
22
21
  const {
23
22
  personalAccessKeyPrompt,
24
23
  OAUTH_FLOW,
25
- API_KEY_FLOW,
26
24
  } = require('../lib/prompts/personalAccessKeyPrompt');
27
25
  const {
28
26
  enterAccountNamePrompt,
@@ -80,25 +78,6 @@ exports.handler = async options => {
80
78
  let successAuthMethod;
81
79
 
82
80
  switch (authType) {
83
- case API_KEY_AUTH_METHOD.value:
84
- configData = await promptUser(API_KEY_FLOW);
85
- updatedConfig = await updateAccountConfig(configData);
86
- validName = updatedConfig.name;
87
-
88
- if (!validName) {
89
- const { name: namePrompt } = await enterAccountNamePrompt();
90
- validName = namePrompt;
91
- }
92
-
93
- updateAccountConfig({
94
- ...updatedConfig,
95
- environment: updatedConfig.env,
96
- name: validName,
97
- });
98
- writeConfig();
99
-
100
- successAuthMethod = API_KEY_AUTH_METHOD.name;
101
- break;
102
81
  case OAUTH_AUTH_METHOD.value:
103
82
  configData = await promptUser(OAUTH_FLOW);
104
83
  await authenticateWithOauth({
@@ -188,7 +167,6 @@ exports.builder = yargs => {
188
167
  choices: [
189
168
  `${PERSONAL_ACCESS_KEY_AUTH_METHOD.value}`,
190
169
  `${OAUTH_AUTH_METHOD.value}`,
191
- `${API_KEY_AUTH_METHOD.value}`,
192
170
  ],
193
171
  default: PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
194
172
  defaultDescription: i18n(`${i18nKey}.positionals.type.defaultDescription`, {
@@ -0,0 +1,325 @@
1
+ //const chalk = require('chalk');
2
+ const Spinnies = require('spinnies');
3
+ const {
4
+ addAccountOptions,
5
+ addConfigOptions,
6
+ addUseEnvironmentOptions,
7
+ getAccountId,
8
+ } = require('../../lib/commonOpts');
9
+ const { logger } = require('@hubspot/cli-lib/logger');
10
+ const {
11
+ getTableContents,
12
+ getTableHeader,
13
+ } = require('@hubspot/cli-lib/lib/table');
14
+ const { loadAndValidateOptions } = require('../../lib/validation');
15
+ const { promptUser } = require('../../lib/prompts/promptUtils');
16
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
17
+ const { fetchThemes } = require('@hubspot/cli-lib/api/designManager');
18
+ const {
19
+ requestLighthouseScore,
20
+ getLighthouseScoreStatus,
21
+ getLighthouseScore,
22
+ } = require('@hubspot/cli-lib/api/lighthouseScore');
23
+ const {
24
+ HUBSPOT_FOLDER,
25
+ MARKETPLACE_FOLDER,
26
+ } = require('@hubspot/cli-lib/lib/constants');
27
+ const { uiLink } = require('../../lib/ui');
28
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
29
+
30
+ const i18nKey = 'cli.commands.cms.subcommands.lighthouseScore';
31
+
32
+ const DEFAULT_TABLE_HEADER = [
33
+ 'Accessibility',
34
+ 'Best practices',
35
+ 'Performace',
36
+ 'PWA',
37
+ 'SEO',
38
+ ];
39
+
40
+ exports.command = 'lighthouse-score [--theme]';
41
+ exports.describe = i18n(`${i18nKey}.describe`);
42
+
43
+ const selectTheme = async accountId => {
44
+ const { theme: selectedTheme } = await promptUser([
45
+ {
46
+ type: 'list',
47
+ look: false,
48
+ name: 'theme',
49
+ message: i18n(`${i18nKey}.info.promptMessage`),
50
+ choices: async () => {
51
+ try {
52
+ const result = await fetchThemes(accountId, {
53
+ limit: 500,
54
+ sorting: 'MOST_USED',
55
+ });
56
+ if (result && result.objects) {
57
+ return result.objects
58
+ .map(({ theme }) => theme.path)
59
+ .filter(
60
+ themePath =>
61
+ !themePath.startsWith(HUBSPOT_FOLDER) &&
62
+ !themePath.startsWith(MARKETPLACE_FOLDER)
63
+ );
64
+ }
65
+ } catch (err) {
66
+ logger.error(i18n(`${i18nKey}.errors.failedToFetchThemes`));
67
+ process.exit(EXIT_CODES.ERROR);
68
+ }
69
+ },
70
+ },
71
+ ]);
72
+
73
+ return selectedTheme;
74
+ };
75
+
76
+ exports.handler = async options => {
77
+ await loadAndValidateOptions(options);
78
+ const accountId = getAccountId(options);
79
+
80
+ const includeDesktopScore = options.target === 'desktop' || !options.verbose;
81
+ const includeMobileScore = options.target === 'mobile' || !options.verbose;
82
+ let themeToCheck = options.theme;
83
+
84
+ if (themeToCheck) {
85
+ let isValidTheme = true;
86
+ try {
87
+ const result = await fetchThemes(accountId, {
88
+ name: encodeURIComponent(themeToCheck),
89
+ });
90
+ isValidTheme = result && result.total;
91
+ } catch (err) {
92
+ isValidTheme = false;
93
+ }
94
+ if (!isValidTheme) {
95
+ logger.error(
96
+ i18n(`${i18nKey}.errors.themeNotFound`, { theme: themeToCheck })
97
+ );
98
+ process.exit(EXIT_CODES.ERROR);
99
+ }
100
+ } else {
101
+ themeToCheck = await selectTheme(accountId);
102
+ logger.log();
103
+ }
104
+
105
+ // Kick off the scoring
106
+ let requestResult;
107
+ try {
108
+ requestResult = await requestLighthouseScore(accountId, {
109
+ themePath: themeToCheck,
110
+ });
111
+ } catch (err) {
112
+ logger.debug(err);
113
+ }
114
+
115
+ if (!requestResult || !requestResult.mobileId || !requestResult.desktopId) {
116
+ logger.error(i18n(`${i18nKey}.errors.failedToGetLighthouseScore`));
117
+ process.exit(EXIT_CODES.ERROR);
118
+ }
119
+
120
+ // Poll till scoring is finished
121
+ try {
122
+ const spinnies = new Spinnies();
123
+
124
+ spinnies.add('lighthouseScore', {
125
+ text: i18n(`${i18nKey}.info.generatingScore`, { theme: themeToCheck }),
126
+ });
127
+
128
+ const checkScoreStatus = async () => {
129
+ const desktopScoreStatus = includeDesktopScore
130
+ ? await getLighthouseScoreStatus(accountId, {
131
+ themeId: requestResult.desktopId,
132
+ })
133
+ : 'COMPLETED';
134
+ const mobileScoreStatus = includeMobileScore
135
+ ? await getLighthouseScoreStatus(accountId, {
136
+ themeId: requestResult.mobileId,
137
+ })
138
+ : 'COMPLETED';
139
+
140
+ if (
141
+ desktopScoreStatus === 'REQUESTED' ||
142
+ mobileScoreStatus === 'REQUESTED'
143
+ ) {
144
+ await new Promise(resolve => setTimeout(resolve, 2000));
145
+ await checkScoreStatus();
146
+ }
147
+ };
148
+
149
+ await checkScoreStatus();
150
+
151
+ spinnies.remove('lighthouseScore');
152
+ } catch (err) {
153
+ logger.debug(err);
154
+ process.exit(EXIT_CODES.ERROR);
155
+ }
156
+
157
+ // Fetch the scoring results
158
+ let desktopScoreResult;
159
+ let mobileScoreResult;
160
+ let verboseViewAverageScoreResult;
161
+ try {
162
+ const params = { isAverage: !options.verbose };
163
+ desktopScoreResult = includeDesktopScore
164
+ ? await getLighthouseScore(accountId, {
165
+ ...params,
166
+ desktopId: requestResult.desktopId,
167
+ })
168
+ : {};
169
+ mobileScoreResult = includeMobileScore
170
+ ? await getLighthouseScore(accountId, {
171
+ ...params,
172
+ mobileId: requestResult.mobileId,
173
+ })
174
+ : {};
175
+ // This is needed to show the average scores above the verbose output
176
+ verboseViewAverageScoreResult = options.verbose
177
+ ? await getLighthouseScore(accountId, {
178
+ ...params,
179
+ isAverage: true,
180
+ desktopId: includeDesktopScore ? requestResult.desktopId : null,
181
+ mobileId: includeMobileScore ? requestResult.mobileId : null,
182
+ })
183
+ : {};
184
+ } catch (err) {
185
+ logger.error(i18n(`${i18nKey}.errors.failedToGetLighthouseScore`));
186
+ process.exit(EXIT_CODES.ERROR);
187
+ }
188
+
189
+ if (options.verbose) {
190
+ logger.log(`${themeToCheck} ${options.target} scores`);
191
+
192
+ const tableHeader = getTableHeader(DEFAULT_TABLE_HEADER);
193
+
194
+ const scores = verboseViewAverageScoreResult.scores
195
+ ? verboseViewAverageScoreResult.scores[0]
196
+ : {};
197
+
198
+ const averageTableData = [
199
+ scores.accessibilityScore,
200
+ scores.bestPracticesScore,
201
+ scores.performanceScore,
202
+ scores.pwaScore,
203
+ scores.seoScore,
204
+ ];
205
+
206
+ logger.log(
207
+ getTableContents([tableHeader, averageTableData], {
208
+ border: { bodyLeft: ' ' },
209
+ })
210
+ );
211
+ logger.log(i18n(`${i18nKey}.info.pageTemplateScoreTitle`));
212
+
213
+ const table2Header = getTableHeader([
214
+ 'Template path',
215
+ ...DEFAULT_TABLE_HEADER,
216
+ ]);
217
+
218
+ const scoreResult =
219
+ options.target === 'desktop' ? desktopScoreResult : mobileScoreResult;
220
+
221
+ const templateTableData = scoreResult.scores.map(score => {
222
+ return [
223
+ score.templatePath,
224
+ score.accessibilityScore,
225
+ score.bestPracticesScore,
226
+ score.performanceScore,
227
+ score.pwaScore,
228
+ score.seoScore,
229
+ ];
230
+ });
231
+
232
+ logger.log(
233
+ getTableContents([table2Header, ...templateTableData], {
234
+ border: { bodyLeft: ' ' },
235
+ })
236
+ );
237
+
238
+ logger.log(i18n(`${i18nKey}.info.lighthouseLinksTitle`));
239
+
240
+ scoreResult.scores.forEach(score => {
241
+ logger.log(' ', uiLink(score.templatePath, score.link));
242
+ });
243
+
244
+ if (scoreResult.failedTemplatePaths.length) {
245
+ logger.log();
246
+ logger.error(i18n(`${i18nKey}.info.failedTemplatePathsTitle`));
247
+ scoreResult.failedTemplatePaths.forEach(failedTemplatePath => {
248
+ logger.log(' ', failedTemplatePath);
249
+ });
250
+ }
251
+
252
+ logger.log();
253
+ logger.info(
254
+ i18n(`${i18nKey}.info.targetDeviceNote`, { target: options.target })
255
+ );
256
+ } else {
257
+ logger.log(`Theme: ${themeToCheck}`);
258
+ const tableHeader = getTableHeader(['Target', ...DEFAULT_TABLE_HEADER]);
259
+
260
+ const getTableData = (target, scoreResult) => {
261
+ const scores = scoreResult.scores ? scoreResult.scores[0] : {};
262
+ return [
263
+ target,
264
+ scores.accessibilityScore,
265
+ scores.bestPracticesScore,
266
+ scores.performanceScore,
267
+ scores.pwaScore,
268
+ scores.seoScore,
269
+ ];
270
+ };
271
+
272
+ const tableData = [
273
+ getTableData('desktop', desktopScoreResult),
274
+ getTableData('mobile', mobileScoreResult),
275
+ ];
276
+
277
+ logger.log(
278
+ getTableContents([tableHeader, ...tableData], {
279
+ border: { bodyLeft: ' ' },
280
+ })
281
+ );
282
+
283
+ logger.info(i18n(`${i18nKey}.info.verboseOptionNote`));
284
+ }
285
+
286
+ logger.log();
287
+ logger.log(
288
+ `Powered by ${uiLink(
289
+ 'Google Lighthouse',
290
+ 'https://developer.chrome.com/docs/lighthouse/overview/'
291
+ )}.`
292
+ );
293
+
294
+ process.exit();
295
+ };
296
+
297
+ exports.builder = yargs => {
298
+ yargs.option('theme', {
299
+ describe: i18n(`${i18nKey}.options.theme.describe`),
300
+ type: 'string',
301
+ });
302
+ yargs.option('target', {
303
+ describe: i18n(`${i18nKey}.options.target.describe`),
304
+ type: 'string',
305
+ choices: ['desktop', 'mobile'],
306
+ default: 'desktop',
307
+ });
308
+ yargs.option('verbose', {
309
+ describe: i18n(`${i18nKey}.options.verbose.describe`),
310
+ boolean: true,
311
+ default: false,
312
+ });
313
+ yargs.example([
314
+ [
315
+ '$0 cms lighthouse-score --theme=my-theme',
316
+ i18n(`${i18nKey}.examples.default`),
317
+ ],
318
+ ]);
319
+
320
+ addConfigOptions(yargs, true);
321
+ addAccountOptions(yargs, true);
322
+ addUseEnvironmentOptions(yargs, true);
323
+
324
+ return yargs;
325
+ };
@@ -0,0 +1,16 @@
1
+ const { addConfigOptions, addAccountOptions } = require('../lib/commonOpts');
2
+ const lighthouseScore = require('./cms/lighthouseScore');
3
+
4
+ // const i18nKey = 'cli.commands.cms';
5
+
6
+ exports.command = 'cms';
7
+ exports.describe = false; // i18n(`${i18nKey}.describe`);
8
+
9
+ exports.builder = yargs => {
10
+ addConfigOptions(yargs, true);
11
+ addAccountOptions(yargs, true);
12
+
13
+ yargs.command(lighthouseScore).demandCommand(1, '');
14
+
15
+ return yargs;
16
+ };
package/commands/fetch.js CHANGED
@@ -76,5 +76,12 @@ exports.builder = yargs => {
76
76
  },
77
77
  });
78
78
 
79
+ yargs.options({
80
+ assetVersion: {
81
+ type: 'number',
82
+ describe: i18n(`${i18nKey}.options.assetVersion.describe`),
83
+ },
84
+ });
85
+
79
86
  return yargs;
80
87
  };
package/commands/init.js CHANGED
@@ -5,17 +5,15 @@ const {
5
5
  createEmptyConfigFile,
6
6
  deleteEmptyConfigFile,
7
7
  updateDefaultAccount,
8
- writeConfig,
9
- updateAccountConfig,
10
8
  } = require('@hubspot/cli-lib/lib/config');
11
9
  const { addConfigOptions } = require('../lib/commonOpts');
12
10
  const { handleExit } = require('@hubspot/cli-lib/lib/process');
11
+ const { checkAndUpdateGitignore } = require('@hubspot/cli-lib/lib/git');
13
12
  const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
14
13
  const {
15
14
  DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
16
15
  PERSONAL_ACCESS_KEY_AUTH_METHOD,
17
16
  OAUTH_AUTH_METHOD,
18
- API_KEY_AUTH_METHOD,
19
17
  ENVIRONMENTS,
20
18
  } = require('@hubspot/cli-lib/lib/constants');
21
19
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
@@ -29,7 +27,6 @@ const { setLogLevel, addTestingOptions } = require('../lib/commonOpts');
29
27
  const { promptUser } = require('../lib/prompts/promptUtils');
30
28
  const {
31
29
  OAUTH_FLOW,
32
- API_KEY_FLOW,
33
30
  personalAccessKeyPrompt,
34
31
  } = require('../lib/prompts/personalAccessKeyPrompt');
35
32
  const {
@@ -70,28 +67,14 @@ const oauthConfigCreationFlow = async env => {
70
67
  return accountConfig;
71
68
  };
72
69
 
73
- const apiKeyConfigCreationFlow = async env => {
74
- const configData = await promptUser(API_KEY_FLOW);
75
- const accountConfig = {
76
- ...configData,
77
- env,
78
- };
79
- updateAccountConfig(accountConfig);
80
- updateDefaultAccount(accountConfig.name);
81
- writeConfig();
82
- return accountConfig;
83
- };
84
-
85
70
  const CONFIG_CREATION_FLOWS = {
86
71
  [PERSONAL_ACCESS_KEY_AUTH_METHOD.value]: personalAccessKeyConfigCreationFlow,
87
72
  [OAUTH_AUTH_METHOD.value]: oauthConfigCreationFlow,
88
- [API_KEY_AUTH_METHOD.value]: apiKeyConfigCreationFlow,
89
73
  };
90
74
 
91
75
  const AUTH_TYPE_NAMES = {
92
76
  [PERSONAL_ACCESS_KEY_AUTH_METHOD.value]: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
93
77
  [OAUTH_AUTH_METHOD.value]: OAUTH_AUTH_METHOD.name,
94
- [API_KEY_AUTH_METHOD.value]: API_KEY_AUTH_METHOD.name,
95
78
  };
96
79
 
97
80
  exports.command = 'init [--account]';
@@ -134,6 +117,8 @@ exports.handler = async options => {
134
117
  );
135
118
  const configPath = getConfigPath();
136
119
 
120
+ checkAndUpdateGitignore(configPath);
121
+
137
122
  logger.log('');
138
123
  logger.success(
139
124
  i18n(`${i18nKey}.success.configFileCreated`, {
@@ -164,7 +149,6 @@ exports.builder = yargs => {
164
149
  choices: [
165
150
  `${PERSONAL_ACCESS_KEY_AUTH_METHOD.value}`,
166
151
  `${OAUTH_AUTH_METHOD.value}`,
167
- `${API_KEY_AUTH_METHOD.value}`,
168
152
  ],
169
153
  default: PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
170
154
  defaultDescription: i18n(`${i18nKey}.options.auth.defaultDescription`, {
@@ -10,7 +10,10 @@ const {
10
10
  const { trackCommandUsage } = require('../../lib/usageTracking');
11
11
  const { logger } = require('@hubspot/cli-lib/logger');
12
12
  const { outputLogs } = require('@hubspot/cli-lib/lib/logs');
13
- const { fetchProject } = require('@hubspot/cli-lib/api/dfs');
13
+ const {
14
+ fetchProject,
15
+ fetchDeployComponentsMetadata,
16
+ } = require('@hubspot/cli-lib/api/dfs');
14
17
  const {
15
18
  getTableContents,
16
19
  getTableHeader,
@@ -142,12 +145,30 @@ exports.handler = async options => {
142
145
  const endpointName = options.endpoint || promptEndpointName;
143
146
 
144
147
  let relativeAppPath;
148
+ let appId;
145
149
 
146
150
  if (appName && !endpointName) {
147
151
  await ensureProjectExists(accountId, projectName, {
148
152
  allowCreate: false,
149
153
  });
150
- const { deployedBuild } = await fetchProject(accountId, projectName);
154
+
155
+ const { deployedBuild, id: projectId } = await fetchProject(
156
+ accountId,
157
+ projectName
158
+ );
159
+
160
+ const { results: deployComponents } = await fetchDeployComponentsMetadata(
161
+ accountId,
162
+ projectId
163
+ );
164
+
165
+ const appComponent = deployComponents.find(
166
+ c => c.componentName === appName
167
+ );
168
+
169
+ if (appComponent) {
170
+ appId = appComponent.componentIdentifier;
171
+ }
151
172
 
152
173
  if (deployedBuild && deployedBuild.subbuildStatuses) {
153
174
  const appSubbuild = deployedBuild.subbuildStatuses.find(
@@ -196,10 +217,15 @@ exports.handler = async options => {
196
217
  );
197
218
 
198
219
  logger.log(
199
- uiLink(
200
- i18n(`${i18nKey}.logs.hubspotLogsLink`),
201
- getPrivateAppsUrl(accountId)
202
- )
220
+ appId
221
+ ? uiLink(
222
+ i18n(`${i18nKey}.logs.hubspotLogsDirectLink`),
223
+ `${getPrivateAppsUrl(accountId)}/${appId}/logs/extensions`
224
+ )
225
+ : uiLink(
226
+ i18n(`${i18nKey}.logs.hubspotLogsLink`),
227
+ getPrivateAppsUrl(accountId)
228
+ )
203
229
  );
204
230
  logger.log();
205
231
  uiLine();
@@ -7,12 +7,15 @@ const {
7
7
  } = require('../../lib/commonOpts');
8
8
  const { trackCommandUsage } = require('../../lib/usageTracking');
9
9
  const { logger } = require('@hubspot/cli-lib/logger');
10
-
10
+ const Spinnies = require('spinnies');
11
11
  const { createSandbox } = require('@hubspot/cli-lib/sandboxes');
12
12
  const { loadAndValidateOptions } = require('../../lib/validation');
13
13
  const { createSandboxPrompt } = require('../../lib/prompts/sandboxesPrompt');
14
14
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
15
15
  const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
16
+ const {
17
+ debugErrorAndContext,
18
+ } = require('@hubspot/cli-lib/errorHandlers/standardErrors');
16
19
  const {
17
20
  ENVIRONMENTS,
18
21
  PERSONAL_ACCESS_KEY_AUTH_METHOD,
@@ -103,7 +106,7 @@ const personalAccessKeyFlow = async (env, account, name) => {
103
106
  ]);
104
107
  };
105
108
 
106
- exports.command = 'create [name]';
109
+ exports.command = 'create [--name]';
107
110
  exports.describe = i18n(`${i18nKey}.describe`);
108
111
 
109
112
  exports.handler = async options => {
@@ -113,6 +116,10 @@ exports.handler = async options => {
113
116
  const accountId = getAccountId(options);
114
117
  const accountConfig = getAccountConfig(accountId);
115
118
  const env = options.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
119
+ const spinnies = new Spinnies({
120
+ succeedColor: 'white',
121
+ });
122
+
116
123
  let namePrompt;
117
124
 
118
125
  trackCommandUsage('sandbox-create', {}, accountId);
@@ -123,26 +130,33 @@ exports.handler = async options => {
123
130
 
124
131
  const sandboxName = name || namePrompt.name;
125
132
 
126
- logger.debug(
127
- i18n(`${i18nKey}.debug.creating`, {
128
- name: sandboxName,
129
- })
130
- );
131
133
  let result;
134
+
132
135
  try {
133
- result = await createSandbox(accountId, sandboxName).then(
134
- ({ name, sandboxHubId }) => {
135
- logger.log('');
136
- logger.success(
137
- i18n(`${i18nKey}.success.create`, {
138
- name,
139
- sandboxHubId,
140
- })
141
- );
142
- return { name, sandboxHubId };
143
- }
144
- );
136
+ spinnies.add('sandboxCreate', {
137
+ text: i18n(`${i18nKey}.loading.add`, {
138
+ sandboxName,
139
+ }),
140
+ });
141
+
142
+ result = await createSandbox(accountId, sandboxName);
143
+
144
+ logger.log('');
145
+ spinnies.succeed('sandboxCreate', {
146
+ text: i18n(`${i18nKey}.loading.succeed`, {
147
+ name: result.name,
148
+ sandboxHubId: result.sandboxHubId,
149
+ }),
150
+ });
145
151
  } catch (err) {
152
+ debugErrorAndContext(err);
153
+
154
+ spinnies.fail('sandboxCreate', {
155
+ text: i18n(`${i18nKey}.loading.fail`, {
156
+ sandboxName,
157
+ }),
158
+ });
159
+
146
160
  if (isMissingScopeError(err)) {
147
161
  logger.error(
148
162
  i18n(`${i18nKey}.failure.scopes.message`, {
@@ -167,17 +181,21 @@ exports.handler = async options => {
167
181
  process.exit(EXIT_CODES.SUCCESS);
168
182
  } catch (err) {
169
183
  logErrorInstance(err);
184
+ process.exit(EXIT_CODES.ERROR);
170
185
  }
171
186
  };
172
187
 
173
188
  exports.builder = yargs => {
174
- yargs.positional('name', {
175
- describe: i18n(`${i18nKey}.positionals.name.describe`),
189
+ yargs.option('name', {
190
+ describe: i18n(`${i18nKey}.options.name.describe`),
176
191
  type: 'string',
177
192
  });
178
193
 
179
194
  yargs.example([
180
- ['$0 sandbox create MySandboxAccount', i18n(`${i18nKey}.examples.default`)],
195
+ [
196
+ '$0 sandbox create --name=MySandboxAccount',
197
+ i18n(`${i18nKey}.examples.default`),
198
+ ],
181
199
  ]);
182
200
 
183
201
  addConfigOptions(yargs, true);
@@ -2,22 +2,149 @@ const {
2
2
  addAccountOptions,
3
3
  addConfigOptions,
4
4
  addUseEnvironmentOptions,
5
+ getAccountId,
6
+ addTestingOptions,
5
7
  } = require('../../lib/commonOpts');
6
8
  const { logger } = require('@hubspot/cli-lib/logger');
7
-
9
+ const { trackCommandUsage } = require('../../lib/usageTracking');
8
10
  const { loadAndValidateOptions } = require('../../lib/validation');
11
+ const {
12
+ debugErrorAndContext,
13
+ } = require('@hubspot/cli-lib/errorHandlers/standardErrors');
14
+
15
+ const { deleteSandbox } = require('@hubspot/cli-lib/sandboxes');
9
16
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
17
+ const { getConfig, getEnv } = require('@hubspot/cli-lib');
18
+ const { deleteSandboxPrompt } = require('../../lib/prompts/sandboxesPrompt');
19
+ const { removeAccountFromConfig } = require('@hubspot/cli-lib/lib/config');
20
+ const {
21
+ selectAndSetAsDefaultAccountPrompt,
22
+ } = require('../../lib/prompts/accountsPrompt');
23
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
24
+ const { promptUser } = require('../../lib/prompts/promptUtils');
25
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
26
+ const { ENVIRONMENTS } = require('@hubspot/cli-lib/lib/constants');
10
27
 
11
28
  const i18nKey = 'cli.commands.sandbox.subcommands.delete';
12
29
 
13
- exports.command = 'delete';
30
+ const SANDBOX_NOT_FOUND = 'SandboxErrors.SANDBOX_NOT_FOUND';
31
+ const OBJECT_NOT_FOUND = 'OBJECT_NOT_FOUND';
32
+
33
+ exports.command = 'delete [--account]';
14
34
  exports.describe = i18n(`${i18nKey}.describe`);
15
35
 
16
36
  exports.handler = async options => {
17
- await loadAndValidateOptions(options);
37
+ await loadAndValidateOptions(options, false);
38
+
39
+ const { account } = options;
40
+ const config = getConfig();
41
+
42
+ let accountPrompt;
43
+ if (!account) {
44
+ accountPrompt = await deleteSandboxPrompt(config);
45
+ }
46
+ const sandboxAccountId = getAccountId({
47
+ account: account || accountPrompt.account,
48
+ });
49
+
50
+ trackCommandUsage('sandbox-delete', {}, sandboxAccountId);
51
+
52
+ let parentAccountId;
53
+ for (const portal of config.portals) {
54
+ if (portal.portalId === sandboxAccountId) {
55
+ if (portal.parentAccountId) {
56
+ parentAccountId = portal.parentAccountId;
57
+ } else {
58
+ const parentAccountPrompt = await deleteSandboxPrompt(config, true);
59
+ parentAccountId = getAccountId({
60
+ account: parentAccountPrompt.account,
61
+ });
62
+ }
63
+ }
64
+ }
65
+
66
+ if (!getAccountId({ account: parentAccountId })) {
67
+ const baseUrl = getHubSpotWebsiteOrigin(
68
+ getEnv(sandboxAccountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
69
+ );
70
+ const url = `${baseUrl}/sandboxes/${parentAccountId}`;
71
+ const command = `hs auth ${
72
+ getEnv(sandboxAccountId) === 'qa' ? '--qa' : ''
73
+ } --account=${parentAccountId}`;
74
+ logger.log('');
75
+ logger.error(
76
+ i18n(`${i18nKey}.noParentPortalAvailable`, {
77
+ parentAccountId,
78
+ url,
79
+ command,
80
+ })
81
+ );
82
+ logger.log('');
83
+ process.exit(EXIT_CODES.ERROR);
84
+ }
85
+
86
+ logger.debug(
87
+ i18n(`${i18nKey}.debug.deleting`, {
88
+ account: account || accountPrompt.account,
89
+ })
90
+ );
91
+
92
+ try {
93
+ const { confirmSandboxDeletePrompt: confirmed } = await promptUser([
94
+ {
95
+ name: 'confirmSandboxDeletePrompt',
96
+ type: 'confirm',
97
+ message: i18n(`${i18nKey}.confirm`, {
98
+ account: account || accountPrompt.account,
99
+ }),
100
+ },
101
+ ]);
102
+ if (!confirmed) {
103
+ process.exit(EXIT_CODES.SUCCESS);
104
+ }
105
+
106
+ await deleteSandbox(parentAccountId, sandboxAccountId);
107
+
108
+ logger.log('');
109
+ logger.success(
110
+ i18n(`${i18nKey}.success.delete`, {
111
+ account: account || accountPrompt.account,
112
+ sandboxHubId: sandboxAccountId,
113
+ })
114
+ );
115
+ logger.log('');
116
+
117
+ const promptDefaultAccount = removeAccountFromConfig(sandboxAccountId);
118
+ if (promptDefaultAccount) {
119
+ await selectAndSetAsDefaultAccountPrompt(config);
120
+ }
121
+ process.exit(EXIT_CODES.SUCCESS);
122
+ } catch (err) {
123
+ debugErrorAndContext(err);
124
+
125
+ if (
126
+ err.error &&
127
+ err.error.category === OBJECT_NOT_FOUND &&
128
+ err.error.subCategory === SANDBOX_NOT_FOUND
129
+ ) {
130
+ logger.log('');
131
+ logger.warn(
132
+ i18n(`${i18nKey}.objectNotFound`, {
133
+ account: account || accountPrompt.account,
134
+ })
135
+ );
136
+ logger.log('');
18
137
 
19
- logger.log('');
20
- logger.log(i18n(`${i18nKey}.temporaryMessage`));
138
+ const promptDefaultAccount = removeAccountFromConfig(sandboxAccountId);
139
+ if (promptDefaultAccount) {
140
+ await selectAndSetAsDefaultAccountPrompt(config);
141
+ }
142
+ process.exit(EXIT_CODES.SUCCESS);
143
+ } else {
144
+ logger.error(err.error.message);
145
+ }
146
+ process.exit(EXIT_CODES.ERROR);
147
+ }
21
148
  };
22
149
 
23
150
  exports.builder = yargs => {
@@ -36,6 +163,7 @@ exports.builder = yargs => {
36
163
  addConfigOptions(yargs, true);
37
164
  addAccountOptions(yargs, true);
38
165
  addUseEnvironmentOptions(yargs, true);
166
+ addTestingOptions(yargs, true);
39
167
 
40
168
  return yargs;
41
169
  };
@@ -11,13 +11,6 @@ allowUsageTracking: false
11
11
 
12
12
  # List of accounts that are intended to be used
13
13
  portals:
14
- # Account set up to use an API Key
15
- - name: DEV
16
- portalId: 123
17
- # Override mode for account
18
- defaultMode: draft
19
- authType: apikey
20
- apiKey: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
21
14
  # Account set up to use OAuth2
22
15
  - name: PROD
23
16
  portalId: 456
@@ -24,13 +24,6 @@ describe('validation', () => {
24
24
  getAccountConfig.mockReturnValueOnce(undefined);
25
25
  expect(await validateAccount({ account: 123 })).toBe(false);
26
26
  });
27
- it('returns false if an api key is missing', async () => {
28
- getAccountId.mockReturnValueOnce(123);
29
- getAccountConfig.mockReturnValueOnce({
30
- accountId: 123,
31
- });
32
- expect(await validateAccount({ account: 123 })).toBe(false);
33
- });
34
27
  it('returns false for oauth2 authType if auth is missing', async () => {
35
28
  getAccountId.mockReturnValueOnce(123);
36
29
  getAccountConfig.mockReturnValueOnce({
package/lib/projects.js CHANGED
@@ -343,8 +343,9 @@ const makePollTaskStatusFunc = ({
343
343
  }
344
344
  };
345
345
 
346
- return async (accountId, taskName, taskId) => {
347
- let hubspotLinkText = '';
346
+ return async (accountId, taskName, taskId, deployedBuildId = null) => {
347
+ const displayId = deployedBuildId || taskId;
348
+
348
349
  if (linkToHubSpot) {
349
350
  logger.log(`\n${linkToHubSpot(taskName, taskId, accountId)}\n`);
350
351
  }
@@ -372,7 +373,7 @@ const makePollTaskStatusFunc = ({
372
373
  const subTaskName = subTask[statusText.SUBTASK_NAME_KEY];
373
374
 
374
375
  spinnies.add(subTaskName, {
375
- text: `${chalk.bold(subTaskName)} #${taskId} ${
376
+ text: `${chalk.bold(subTaskName)} #${displayId} ${
376
377
  statusText.STATUS_TEXT[statusText.STATES.ENQUEUED]
377
378
  }\n`,
378
379
  indent: 2,
@@ -395,7 +396,7 @@ const makePollTaskStatusFunc = ({
395
396
  return;
396
397
  }
397
398
 
398
- const updatedText = `${chalk.bold(subTaskName)} #${taskId} ${
399
+ const updatedText = `${chalk.bold(subTaskName)} #${displayId} ${
399
400
  statusText.STATUS_TEXT[subTask.status]
400
401
  }\n`;
401
402
 
@@ -413,17 +414,17 @@ const makePollTaskStatusFunc = ({
413
414
  });
414
415
 
415
416
  if (isTaskComplete(taskStatus)) {
416
- subTaskStatus.forEach(subTask => {
417
- spinnies.remove(subTask[statusText.SUBTASK_NAME_KEY]);
418
- });
417
+ // subTaskStatus.forEach(subTask => {
418
+ // spinnies.remove(subTask[statusText.SUBTASK_NAME_KEY]);
419
+ // });
419
420
 
420
421
  if (status === statusText.STATES.SUCCESS) {
421
422
  spinnies.succeed('overallTaskStatus', {
422
- text: `${statusStrings.SUCCESS(taskName)}${hubspotLinkText}`,
423
+ text: statusStrings.SUCCESS(taskName),
423
424
  });
424
425
  } else if (status === statusText.STATES.FAILURE) {
425
426
  spinnies.fail('overallTaskStatus', {
426
- text: `${statusStrings.FAIL(taskName)}${hubspotLinkText}`,
427
+ text: statusStrings.FAIL(taskName),
427
428
  });
428
429
 
429
430
  const failedSubtask = subTaskStatus.filter(
@@ -433,7 +434,7 @@ const makePollTaskStatusFunc = ({
433
434
  uiLine();
434
435
  logger.log(
435
436
  `${statusStrings.SUBTASK_FAIL(
436
- taskId,
437
+ displayId,
437
438
  failedSubtask.length === 1
438
439
  ? failedSubtask[0][statusText.SUBTASK_NAME_KEY]
439
440
  : failedSubtask.length + ' components'
@@ -481,8 +482,8 @@ const pollBuildStatus = makePollTaskStatusFunc({
481
482
  INITIALIZE: name => `Building ${chalk.bold(name)}`,
482
483
  SUCCESS: name => `Built ${chalk.bold(name)}`,
483
484
  FAIL: name => `Failed to build ${chalk.bold(name)}`,
484
- SUBTASK_FAIL: (taskId, name) =>
485
- `Build #${taskId} failed because there was a problem\nbuilding ${chalk.bold(
485
+ SUBTASK_FAIL: (buildId, name) =>
486
+ `Build #${buildId} failed because there was a problem\nbuilding ${chalk.bold(
486
487
  name
487
488
  )}`,
488
489
  },
@@ -495,8 +496,8 @@ const pollDeployStatus = makePollTaskStatusFunc({
495
496
  INITIALIZE: name => `Deploying ${chalk.bold(name)}`,
496
497
  SUCCESS: name => `Deployed ${chalk.bold(name)}`,
497
498
  FAIL: name => `Failed to deploy ${chalk.bold(name)}`,
498
- SUBTASK_FAIL: (taskId, name) =>
499
- `Deploy for build #${taskId} failed because there was a\nproblem deploying ${chalk.bold(
499
+ SUBTASK_FAIL: (deployedBuildId, name) =>
500
+ `Deploy for build #${deployedBuildId} failed because there was a\nproblem deploying ${chalk.bold(
500
501
  name
501
502
  )}`,
502
503
  },
@@ -0,0 +1,47 @@
1
+ const { updateDefaultAccount } = require('@hubspot/cli-lib/lib/config');
2
+ const { promptUser } = require('./promptUtils');
3
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
4
+
5
+ const i18nKey = 'cli.commands.accounts.subcommands.use';
6
+
7
+ const selectAccountFromConfig = async config => {
8
+ const { default: selectedDefault } = await promptUser([
9
+ {
10
+ type: 'list',
11
+ look: false,
12
+ name: 'default',
13
+ pageSize: 20,
14
+ message: i18n(`${i18nKey}.promptMessage`),
15
+ choices: config.portals.map(p => ({
16
+ name: `${p.name} (${p.portalId})`,
17
+ value: p.name || p.portalId,
18
+ })),
19
+ default: config.defaultPortal,
20
+ },
21
+ ]);
22
+
23
+ return selectedDefault;
24
+ };
25
+
26
+ const selectAndSetAsDefaultAccountPrompt = async config => {
27
+ const { default: selectedDefault } = await promptUser([
28
+ {
29
+ type: 'list',
30
+ look: false,
31
+ name: 'default',
32
+ pageSize: 20,
33
+ message: i18n(`${i18nKey}.promptMessage`),
34
+ choices: config.portals.map(p => ({
35
+ name: `${p.name} (${p.portalId})`,
36
+ value: p.name || p.portalId,
37
+ })),
38
+ default: config.defaultPortal,
39
+ },
40
+ ]);
41
+ updateDefaultAccount(selectedDefault);
42
+ };
43
+
44
+ module.exports = {
45
+ selectAndSetAsDefaultAccountPrompt,
46
+ selectAccountFromConfig,
47
+ };
@@ -6,7 +6,6 @@ const {
6
6
  const { deleteEmptyConfigFile } = require('@hubspot/cli-lib/lib/config');
7
7
  const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
8
8
  const { logger } = require('@hubspot/cli-lib/logger');
9
- const { API_KEY_REGEX } = require('../regex');
10
9
  const { promptUser } = require('./promptUtils');
11
10
  const { accountNamePrompt } = require('./enterAccountNamePrompt');
12
11
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
@@ -89,17 +88,6 @@ const CLIENT_SECRET = {
89
88
  },
90
89
  };
91
90
 
92
- const ACCOUNT_API_KEY = {
93
- name: 'apiKey',
94
- message: i18n(`${i18nKey}.enterApiKey`),
95
- validate(val) {
96
- if (!API_KEY_REGEX.test(val)) {
97
- return i18n(`${i18nKey}.errors.invalidAPIKey`);
98
- }
99
- return true;
100
- },
101
- };
102
-
103
91
  const PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP = {
104
92
  name: 'personalAcessKeyBrowserOpenPrep',
105
93
  type: 'confirm',
@@ -142,17 +130,14 @@ const OAUTH_FLOW = [
142
130
  CLIENT_SECRET,
143
131
  SCOPES,
144
132
  ];
145
- const API_KEY_FLOW = [accountNamePrompt(), ACCOUNT_ID, ACCOUNT_API_KEY];
146
133
 
147
134
  module.exports = {
148
135
  personalAccessKeyPrompt,
149
136
  CLIENT_ID,
150
137
  CLIENT_SECRET,
151
- ACCOUNT_API_KEY,
152
138
  ACCOUNT_ID,
153
139
  SCOPES,
154
140
  PERSONAL_ACCESS_KEY,
155
141
  // Flows
156
- API_KEY_FLOW,
157
142
  OAUTH_FLOW,
158
143
  };
@@ -19,6 +19,25 @@ const createSandboxPrompt = () => {
19
19
  ]);
20
20
  };
21
21
 
22
+ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
23
+ return promptUser([
24
+ {
25
+ name: 'account',
26
+ message: i18n(
27
+ promptParentAccount
28
+ ? `${i18nKey}.selectParentAccountName`
29
+ : `${i18nKey}.selectAccountName`
30
+ ),
31
+ type: 'list',
32
+ look: false,
33
+ pageSize: 20,
34
+ choices: config.portals.map(p => p.name || p.portalId),
35
+ default: config.defaultPortal,
36
+ },
37
+ ]);
38
+ };
39
+
22
40
  module.exports = {
23
41
  createSandboxPrompt,
42
+ deleteSandboxPrompt,
24
43
  };
package/lib/regex.js CHANGED
@@ -1,7 +1,5 @@
1
- const API_KEY_REGEX = /^([a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12})$/i;
2
1
  const STRING_WITH_NO_SPACES_REGEX = /^\S*$/;
3
2
 
4
3
  module.exports = {
5
- API_KEY_REGEX,
6
4
  STRING_WITH_NO_SPACES_REGEX,
7
5
  };
package/lib/validation.js CHANGED
@@ -26,14 +26,19 @@ const fs = require('fs');
26
26
  const path = require('path');
27
27
  const { EXIT_CODES } = require('./enums/exitCodes');
28
28
 
29
- async function loadAndValidateOptions(options) {
29
+ async function loadAndValidateOptions(options, shouldValidateAccount = true) {
30
30
  setLogLevel(options);
31
31
  logDebugInfo(options);
32
32
  const { config: configPath } = options;
33
33
  loadConfig(configPath, options);
34
34
  checkAndWarnGitInclusion(getConfigPath());
35
35
 
36
- if (!(validateConfig() && (await validateAccount(options)))) {
36
+ let validAccount = true;
37
+ if (shouldValidateAccount) {
38
+ validAccount = await validateAccount(options);
39
+ }
40
+
41
+ if (!(validateConfig() && validAccount)) {
37
42
  process.exit(EXIT_CODES.ERROR);
38
43
  }
39
44
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "4.0.1-beta.2",
3
+ "version": "4.0.1-beta.5",
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/cli-lib": "4.0.1-beta.2",
12
- "@hubspot/serverless-dev-runtime": "4.0.1-beta.2",
11
+ "@hubspot/cli-lib": "4.0.1-beta.5",
12
+ "@hubspot/serverless-dev-runtime": "4.0.1-beta.5",
13
13
  "archiver": "^5.3.0",
14
14
  "chalk": "^4.1.2",
15
15
  "express": "^4.17.1",
@@ -37,5 +37,5 @@
37
37
  "publishConfig": {
38
38
  "access": "public"
39
39
  },
40
- "gitHead": "c8b5dbfe4753fc1b71c038801d5f641313712e71"
40
+ "gitHead": "af927b5e071f91db0dd415fca154a446f259fe3b"
41
41
  }