@hubspot/cli 4.0.1-beta.1 → 4.0.1-beta.4

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)
@@ -0,0 +1,53 @@
1
+ const { logger } = require('@hubspot/cli-lib/logger');
2
+ const { getAccountConfig } = require('@hubspot/cli-lib/lib/config');
3
+ const { getAccessToken } = require('@hubspot/cli-lib/personalAccessKey.js');
4
+ const {
5
+ getAccountId,
6
+ addAccountOptions,
7
+ addConfigOptions,
8
+ } = require('../../lib/commonOpts');
9
+ const { loadAndValidateOptions } = require('../../lib/validation');
10
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
11
+
12
+ const i18nKey = 'cli.commands.accounts.subcommands.info';
13
+ exports.describe = i18n(`${i18nKey}.describe`);
14
+
15
+ exports.command = 'info [--account]';
16
+
17
+ exports.handler = async options => {
18
+ await loadAndValidateOptions(options);
19
+
20
+ let accountId = getAccountId(options);
21
+ const config = getAccountConfig(accountId);
22
+
23
+ // check if the provided account is using a personal access key, if not, show an error
24
+ if (config.authType === 'personalaccesskey') {
25
+ const { name, personalAccessKey, env } = config;
26
+
27
+ const response = await getAccessToken(personalAccessKey, env, accountId);
28
+
29
+ let scopeGroups = response.scopeGroups.join('\n');
30
+
31
+ logger.log(i18n(`${i18nKey}.name`, { name }));
32
+ logger.log(i18n(`${i18nKey}.accountId`, { accountId }));
33
+ logger.log(i18n(`${i18nKey}.scopeGroups`, { scopeGroups }));
34
+ } else {
35
+ logger.log(i18n(`${i18nKey}.errors.notUsingPersonalAccessKey`));
36
+ }
37
+ };
38
+
39
+ exports.builder = yargs => {
40
+ addConfigOptions(yargs, true);
41
+ addAccountOptions(yargs, true);
42
+
43
+ yargs.example([
44
+ ['$0 accounts info', i18n(`${i18nKey}.examples.default`)],
45
+ [
46
+ '$0 accounts info --account=MyAccount',
47
+ i18n(`${i18nKey}.examples.nameBased`),
48
+ ],
49
+ ['$0 accounts info --account=1234567', i18n(`${i18nKey}.examples.idBased`)],
50
+ ]);
51
+
52
+ return yargs;
53
+ };
@@ -19,6 +19,54 @@ const i18nKey = 'cli.commands.accounts.subcommands.list';
19
19
  exports.command = 'list';
20
20
  exports.describe = i18n(`${i18nKey}.describe`);
21
21
 
22
+ const sortAndMapPortals = portals => {
23
+ const mappedPortalData = {};
24
+ portals
25
+ .sort((a, b) => {
26
+ if (a.sandboxAccountType === null && b.sandboxAccountType !== null) {
27
+ return -1;
28
+ }
29
+ if (a.sandboxAccountType !== null && b.sandboxAccountType === null) {
30
+ return 1;
31
+ }
32
+ return 0;
33
+ })
34
+ .forEach(portal => {
35
+ if (
36
+ portal.sandboxAccountType !== undefined &&
37
+ portal.sandboxAccountType === null
38
+ ) {
39
+ mappedPortalData[portal.portalId] = [portal];
40
+ } else if (portal.sandboxAccountType && portal.parentAccountId) {
41
+ mappedPortalData[portal.parentAccountId] = [
42
+ ...(mappedPortalData[portal.parentAccountId] || []),
43
+ portal,
44
+ ];
45
+ } else {
46
+ mappedPortalData[portal.portalId] = [portal];
47
+ }
48
+ });
49
+ return mappedPortalData;
50
+ };
51
+
52
+ const getPortalData = mappedPortalData => {
53
+ const portalData = [];
54
+ Object.values(mappedPortalData).forEach(set => {
55
+ set.forEach((portal, i) => {
56
+ if (i === 0) {
57
+ portalData.push([portal.name, portal.portalId, portal.authType]);
58
+ } else {
59
+ portalData.push([
60
+ `↳ ${portal.name} [sandbox]`,
61
+ portal.portalId,
62
+ portal.authType,
63
+ ]);
64
+ }
65
+ });
66
+ });
67
+ return portalData;
68
+ };
69
+
22
70
  exports.handler = async options => {
23
71
  await loadAndValidateOptions(options);
24
72
 
@@ -28,9 +76,8 @@ exports.handler = async options => {
28
76
 
29
77
  const config = getConfig();
30
78
  const configPath = getConfigPath();
31
- const portalData = config.portals.map(portal => {
32
- return [portal.name, portal.portalId, portal.authType];
33
- });
79
+ const mappedPortalData = sortAndMapPortals(config.portals);
80
+ const portalData = getPortalData(mappedPortalData);
34
81
  portalData.unshift(
35
82
  getTableHeader([
36
83
  i18n(`${i18nKey}.labels.name`),
@@ -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(
@@ -3,6 +3,7 @@ const { i18n } = require('@hubspot/cli-lib/lib/lang');
3
3
  const list = require('./accounts/list');
4
4
  const rename = require('./accounts/rename');
5
5
  const use = require('./accounts/use');
6
+ const info = require('./accounts/info');
6
7
 
7
8
  const i18nKey = 'cli.commands.accounts';
8
9
 
@@ -20,6 +21,7 @@ exports.builder = yargs => {
20
21
  })
21
22
  .command(rename)
22
23
  .command(use)
24
+ .command(info)
23
25
  .demandCommand(1, '');
24
26
 
25
27
  return yargs;
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,322 @@
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
+ if (result && result.objects) {
54
+ return result.objects
55
+ .map(({ theme }) => theme.path)
56
+ .filter(
57
+ themePath =>
58
+ !themePath.startsWith(HUBSPOT_FOLDER) &&
59
+ !themePath.startsWith(MARKETPLACE_FOLDER)
60
+ );
61
+ }
62
+ } catch (err) {
63
+ logger.error(i18n(`${i18nKey}.errors.failedToFetchThemes`));
64
+ process.exit(EXIT_CODES.ERROR);
65
+ }
66
+ },
67
+ },
68
+ ]);
69
+
70
+ return selectedTheme;
71
+ };
72
+
73
+ exports.handler = async options => {
74
+ await loadAndValidateOptions(options);
75
+ const accountId = getAccountId(options);
76
+
77
+ const includeDesktopScore = options.target === 'desktop' || !options.verbose;
78
+ const includeMobileScore = options.target === 'mobile' || !options.verbose;
79
+ let themeToCheck = options.theme;
80
+
81
+ if (themeToCheck) {
82
+ let isValidTheme = true;
83
+ try {
84
+ const result = await fetchThemes(accountId, {
85
+ name: encodeURIComponent(themeToCheck),
86
+ });
87
+ isValidTheme = result && result.total;
88
+ } catch (err) {
89
+ isValidTheme = false;
90
+ }
91
+ if (!isValidTheme) {
92
+ logger.error(
93
+ i18n(`${i18nKey}.errors.themeNotFound`, { theme: themeToCheck })
94
+ );
95
+ process.exit(EXIT_CODES.ERROR);
96
+ }
97
+ } else {
98
+ themeToCheck = await selectTheme(accountId);
99
+ logger.log();
100
+ }
101
+
102
+ // Kick off the scoring
103
+ let requestResult;
104
+ try {
105
+ requestResult = await requestLighthouseScore(accountId, {
106
+ themePath: themeToCheck,
107
+ });
108
+ } catch (err) {
109
+ logger.debug(err);
110
+ }
111
+
112
+ if (!requestResult || !requestResult.mobileId || !requestResult.desktopId) {
113
+ logger.error(i18n(`${i18nKey}.errors.failedToGetLighthouseScore`));
114
+ process.exit(EXIT_CODES.ERROR);
115
+ }
116
+
117
+ // Poll till scoring is finished
118
+ try {
119
+ const spinnies = new Spinnies();
120
+
121
+ spinnies.add('lighthouseScore', {
122
+ text: i18n(`${i18nKey}.info.generatingScore`, { theme: themeToCheck }),
123
+ });
124
+
125
+ const checkScoreStatus = async () => {
126
+ const desktopScoreStatus = includeDesktopScore
127
+ ? await getLighthouseScoreStatus(accountId, {
128
+ themeId: requestResult.desktopId,
129
+ })
130
+ : 'COMPLETED';
131
+ const mobileScoreStatus = includeMobileScore
132
+ ? await getLighthouseScoreStatus(accountId, {
133
+ themeId: requestResult.mobileId,
134
+ })
135
+ : 'COMPLETED';
136
+
137
+ if (
138
+ desktopScoreStatus === 'REQUESTED' ||
139
+ mobileScoreStatus === 'REQUESTED'
140
+ ) {
141
+ await new Promise(resolve => setTimeout(resolve, 2000));
142
+ await checkScoreStatus();
143
+ }
144
+ };
145
+
146
+ await checkScoreStatus();
147
+
148
+ spinnies.remove('lighthouseScore');
149
+ } catch (err) {
150
+ logger.debug(err);
151
+ process.exit(EXIT_CODES.ERROR);
152
+ }
153
+
154
+ // Fetch the scoring results
155
+ let desktopScoreResult;
156
+ let mobileScoreResult;
157
+ let verboseViewAverageScoreResult;
158
+ try {
159
+ const params = { isAverage: !options.verbose };
160
+ desktopScoreResult = includeDesktopScore
161
+ ? await getLighthouseScore(accountId, {
162
+ ...params,
163
+ desktopId: requestResult.desktopId,
164
+ })
165
+ : {};
166
+ mobileScoreResult = includeMobileScore
167
+ ? await getLighthouseScore(accountId, {
168
+ ...params,
169
+ mobileId: requestResult.mobileId,
170
+ })
171
+ : {};
172
+ // This is needed to show the average scores above the verbose output
173
+ verboseViewAverageScoreResult = options.verbose
174
+ ? await getLighthouseScore(accountId, {
175
+ ...params,
176
+ isAverage: true,
177
+ desktopId: includeDesktopScore ? requestResult.desktopId : null,
178
+ mobileId: includeMobileScore ? requestResult.mobileId : null,
179
+ })
180
+ : {};
181
+ } catch (err) {
182
+ logger.error(i18n(`${i18nKey}.errors.failedToGetLighthouseScore`));
183
+ process.exit(EXIT_CODES.ERROR);
184
+ }
185
+
186
+ if (options.verbose) {
187
+ logger.log(`${themeToCheck} ${options.target} scores`);
188
+
189
+ const tableHeader = getTableHeader(DEFAULT_TABLE_HEADER);
190
+
191
+ const scores = verboseViewAverageScoreResult.scores
192
+ ? verboseViewAverageScoreResult.scores[0]
193
+ : {};
194
+
195
+ const averageTableData = [
196
+ scores.accessibilityScore,
197
+ scores.bestPracticesScore,
198
+ scores.performanceScore,
199
+ scores.pwaScore,
200
+ scores.seoScore,
201
+ ];
202
+
203
+ logger.log(
204
+ getTableContents([tableHeader, averageTableData], {
205
+ border: { bodyLeft: ' ' },
206
+ })
207
+ );
208
+ logger.log(i18n(`${i18nKey}.info.pageTemplateScoreTitle`));
209
+
210
+ const table2Header = getTableHeader([
211
+ 'Template path',
212
+ ...DEFAULT_TABLE_HEADER,
213
+ ]);
214
+
215
+ const scoreResult =
216
+ options.target === 'desktop' ? desktopScoreResult : mobileScoreResult;
217
+
218
+ const templateTableData = scoreResult.scores.map(score => {
219
+ return [
220
+ score.templatePath,
221
+ score.accessibilityScore,
222
+ score.bestPracticesScore,
223
+ score.performanceScore,
224
+ score.pwaScore,
225
+ score.seoScore,
226
+ ];
227
+ });
228
+
229
+ logger.log(
230
+ getTableContents([table2Header, ...templateTableData], {
231
+ border: { bodyLeft: ' ' },
232
+ })
233
+ );
234
+
235
+ logger.log(i18n(`${i18nKey}.info.lighthouseLinksTitle`));
236
+
237
+ scoreResult.scores.forEach(score => {
238
+ logger.log(' ', uiLink(score.templatePath, score.link));
239
+ });
240
+
241
+ if (scoreResult.failedTemplatePaths.length) {
242
+ logger.log();
243
+ logger.error(i18n(`${i18nKey}.info.failedTemplatePathsTitle`));
244
+ scoreResult.failedTemplatePaths.forEach(failedTemplatePath => {
245
+ logger.log(' ', failedTemplatePath);
246
+ });
247
+ }
248
+
249
+ logger.log();
250
+ logger.info(
251
+ i18n(`${i18nKey}.info.targetDeviceNote`, { target: options.target })
252
+ );
253
+ } else {
254
+ logger.log(`Theme: ${themeToCheck}`);
255
+ const tableHeader = getTableHeader(['Target', ...DEFAULT_TABLE_HEADER]);
256
+
257
+ const getTableData = (target, scoreResult) => {
258
+ const scores = scoreResult.scores ? scoreResult.scores[0] : {};
259
+ return [
260
+ target,
261
+ scores.accessibilityScore,
262
+ scores.bestPracticesScore,
263
+ scores.performanceScore,
264
+ scores.pwaScore,
265
+ scores.seoScore,
266
+ ];
267
+ };
268
+
269
+ const tableData = [
270
+ getTableData('desktop', desktopScoreResult),
271
+ getTableData('mobile', mobileScoreResult),
272
+ ];
273
+
274
+ logger.log(
275
+ getTableContents([tableHeader, ...tableData], {
276
+ border: { bodyLeft: ' ' },
277
+ })
278
+ );
279
+
280
+ logger.info(i18n(`${i18nKey}.info.verboseOptionNote`));
281
+ }
282
+
283
+ logger.log();
284
+ logger.log(
285
+ `Powered by ${uiLink(
286
+ 'Google Lighthouse',
287
+ 'https://developer.chrome.com/docs/lighthouse/overview/'
288
+ )}.`
289
+ );
290
+
291
+ process.exit();
292
+ };
293
+
294
+ exports.builder = yargs => {
295
+ yargs.option('theme', {
296
+ describe: i18n(`${i18nKey}.options.theme.describe`),
297
+ type: 'string',
298
+ });
299
+ yargs.option('target', {
300
+ describe: i18n(`${i18nKey}.options.target.describe`),
301
+ type: 'string',
302
+ choices: ['desktop', 'mobile'],
303
+ default: 'desktop',
304
+ });
305
+ yargs.option('verbose', {
306
+ describe: i18n(`${i18nKey}.options.verbose.describe`),
307
+ boolean: true,
308
+ default: false,
309
+ });
310
+ yargs.example([
311
+ [
312
+ '$0 cms lighthouse-score --theme=my-theme',
313
+ i18n(`${i18nKey}.examples.default`),
314
+ ],
315
+ ]);
316
+
317
+ addConfigOptions(yargs, true);
318
+ addAccountOptions(yargs, true);
319
+ addUseEnvironmentOptions(yargs, true);
320
+
321
+ return yargs;
322
+ };
@@ -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,8 +5,6 @@ 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');
@@ -15,7 +13,6 @@ const {
15
13
  DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
16
14
  PERSONAL_ACCESS_KEY_AUTH_METHOD,
17
15
  OAUTH_AUTH_METHOD,
18
- API_KEY_AUTH_METHOD,
19
16
  ENVIRONMENTS,
20
17
  } = require('@hubspot/cli-lib/lib/constants');
21
18
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
@@ -29,7 +26,6 @@ const { setLogLevel, addTestingOptions } = require('../lib/commonOpts');
29
26
  const { promptUser } = require('../lib/prompts/promptUtils');
30
27
  const {
31
28
  OAUTH_FLOW,
32
- API_KEY_FLOW,
33
29
  personalAccessKeyPrompt,
34
30
  } = require('../lib/prompts/personalAccessKeyPrompt');
35
31
  const {
@@ -70,28 +66,14 @@ const oauthConfigCreationFlow = async env => {
70
66
  return accountConfig;
71
67
  };
72
68
 
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
69
  const CONFIG_CREATION_FLOWS = {
86
70
  [PERSONAL_ACCESS_KEY_AUTH_METHOD.value]: personalAccessKeyConfigCreationFlow,
87
71
  [OAUTH_AUTH_METHOD.value]: oauthConfigCreationFlow,
88
- [API_KEY_AUTH_METHOD.value]: apiKeyConfigCreationFlow,
89
72
  };
90
73
 
91
74
  const AUTH_TYPE_NAMES = {
92
75
  [PERSONAL_ACCESS_KEY_AUTH_METHOD.value]: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
93
76
  [OAUTH_AUTH_METHOD.value]: OAUTH_AUTH_METHOD.name,
94
- [API_KEY_AUTH_METHOD.value]: API_KEY_AUTH_METHOD.name,
95
77
  };
96
78
 
97
79
  exports.command = 'init [--account]';
@@ -164,7 +146,6 @@ exports.builder = yargs => {
164
146
  choices: [
165
147
  `${PERSONAL_ACCESS_KEY_AUTH_METHOD.value}`,
166
148
  `${OAUTH_AUTH_METHOD.value}`,
167
- `${API_KEY_AUTH_METHOD.value}`,
168
149
  ],
169
150
  default: PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
170
151
  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,7 +7,7 @@ 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');
@@ -113,6 +113,10 @@ exports.handler = async options => {
113
113
  const accountId = getAccountId(options);
114
114
  const accountConfig = getAccountConfig(accountId);
115
115
  const env = options.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
116
+ const spinnies = new Spinnies({
117
+ succeedColor: 'white',
118
+ });
119
+
116
120
  let namePrompt;
117
121
 
118
122
  trackCommandUsage('sandbox-create', {}, accountId);
@@ -123,26 +127,30 @@ exports.handler = async options => {
123
127
 
124
128
  const sandboxName = name || namePrompt.name;
125
129
 
126
- logger.debug(
127
- i18n(`${i18nKey}.debug.creating`, {
128
- name: sandboxName,
129
- })
130
- );
131
130
  let result;
131
+
132
132
  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
- );
133
+ spinnies.add('sandboxCreate', {
134
+ text: i18n(`${i18nKey}.loading.add`, {
135
+ sandboxName,
136
+ }),
137
+ });
138
+
139
+ result = await createSandbox(accountId, sandboxName);
140
+
141
+ logger.log('');
142
+ spinnies.succeed('sandboxCreate', {
143
+ text: i18n(`${i18nKey}.loading.succeed`, {
144
+ name: result.name,
145
+ sandboxHubId: result.sandboxHubId,
146
+ }),
147
+ });
145
148
  } catch (err) {
149
+ spinnies.fail('sandboxCreate', {
150
+ text: i18n(`${i18nKey}.loading.fail`, {
151
+ sandboxName,
152
+ }),
153
+ });
146
154
  if (isMissingScopeError(err)) {
147
155
  logger.error(
148
156
  i18n(`${i18nKey}.failure.scopes.message`, {
@@ -157,6 +165,8 @@ exports.handler = async options => {
157
165
  url,
158
166
  })
159
167
  );
168
+ } else {
169
+ logger.error(err.error.message);
160
170
  }
161
171
  process.exit(EXIT_CODES.ERROR);
162
172
  }
@@ -165,6 +175,7 @@ exports.handler = async options => {
165
175
  process.exit(EXIT_CODES.SUCCESS);
166
176
  } catch (err) {
167
177
  logErrorInstance(err);
178
+ process.exit(EXIT_CODES.ERROR);
168
179
  }
169
180
  };
170
181
 
@@ -0,0 +1,164 @@
1
+ const {
2
+ addAccountOptions,
3
+ addConfigOptions,
4
+ addUseEnvironmentOptions,
5
+ getAccountId,
6
+ addTestingOptions,
7
+ } = require('../../lib/commonOpts');
8
+ const { logger } = require('@hubspot/cli-lib/logger');
9
+ const { trackCommandUsage } = require('../../lib/usageTracking');
10
+ const { loadAndValidateOptions } = require('../../lib/validation');
11
+
12
+ const { deleteSandbox } = require('@hubspot/cli-lib/sandboxes');
13
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
14
+ const { getConfig, getEnv } = require('@hubspot/cli-lib');
15
+ const { deleteSandboxPrompt } = require('../../lib/prompts/sandboxesPrompt');
16
+ const { removeAccountFromConfig } = require('@hubspot/cli-lib/lib/config');
17
+ const {
18
+ selectAndSetAsDefaultAccountPrompt,
19
+ } = require('../../lib/prompts/accountsPrompt');
20
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
21
+ const { promptUser } = require('../../lib/prompts/promptUtils');
22
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
23
+ const { ENVIRONMENTS } = require('@hubspot/cli-lib/lib/constants');
24
+
25
+ const i18nKey = 'cli.commands.sandbox.subcommands.delete';
26
+
27
+ const SANDBOX_NOT_FOUND = 'SandboxErrors.SANDBOX_NOT_FOUND';
28
+ const OBJECT_NOT_FOUND = 'OBJECT_NOT_FOUND';
29
+
30
+ exports.command = 'delete [--account]';
31
+ exports.describe = i18n(`${i18nKey}.describe`);
32
+
33
+ exports.handler = async options => {
34
+ await loadAndValidateOptions(options, false);
35
+
36
+ const { account } = options;
37
+ const config = getConfig();
38
+
39
+ let accountPrompt;
40
+ if (!account) {
41
+ accountPrompt = await deleteSandboxPrompt(config);
42
+ }
43
+ const sandboxAccountId = getAccountId({
44
+ account: account || accountPrompt.account,
45
+ });
46
+
47
+ trackCommandUsage('sandbox-delete', {}, sandboxAccountId);
48
+
49
+ let parentAccountId;
50
+ for (const portal of config.portals) {
51
+ if (portal.portalId === sandboxAccountId) {
52
+ if (portal.parentAccountId) {
53
+ parentAccountId = portal.parentAccountId;
54
+ } else {
55
+ const parentAccountPrompt = await deleteSandboxPrompt(config, true);
56
+ parentAccountId = getAccountId({
57
+ account: parentAccountPrompt.account,
58
+ });
59
+ }
60
+ }
61
+ }
62
+
63
+ if (!getAccountId({ account: parentAccountId })) {
64
+ const baseUrl = getHubSpotWebsiteOrigin(
65
+ getEnv(sandboxAccountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
66
+ );
67
+ const url = `${baseUrl}/sandboxes/${parentAccountId}`;
68
+ const command = `hs auth ${
69
+ getEnv(sandboxAccountId) === 'qa' ? '--qa' : ''
70
+ } --account=${parentAccountId}`;
71
+ logger.log('');
72
+ logger.error(
73
+ i18n(`${i18nKey}.noParentPortalAvailable`, {
74
+ parentAccountId,
75
+ url,
76
+ command,
77
+ })
78
+ );
79
+ logger.log('');
80
+ process.exit(EXIT_CODES.ERROR);
81
+ }
82
+
83
+ logger.debug(
84
+ i18n(`${i18nKey}.debug.deleting`, {
85
+ account: account || accountPrompt.account,
86
+ })
87
+ );
88
+
89
+ try {
90
+ const { confirmSandboxDeletePrompt: confirmed } = await promptUser([
91
+ {
92
+ name: 'confirmSandboxDeletePrompt',
93
+ type: 'confirm',
94
+ message: i18n(`${i18nKey}.confirm`, {
95
+ account: account || accountPrompt.account,
96
+ }),
97
+ },
98
+ ]);
99
+ if (!confirmed) {
100
+ process.exit(EXIT_CODES.SUCCESS);
101
+ }
102
+
103
+ await deleteSandbox(parentAccountId, sandboxAccountId);
104
+
105
+ logger.log('');
106
+ logger.success(
107
+ i18n(`${i18nKey}.success.delete`, {
108
+ account: account || accountPrompt.account,
109
+ sandboxHubId: sandboxAccountId,
110
+ })
111
+ );
112
+ logger.log('');
113
+
114
+ const promptDefaultAccount = removeAccountFromConfig(sandboxAccountId);
115
+ if (promptDefaultAccount) {
116
+ await selectAndSetAsDefaultAccountPrompt(config);
117
+ }
118
+ process.exit(EXIT_CODES.SUCCESS);
119
+ } catch (err) {
120
+ if (
121
+ err.error &&
122
+ err.error.category === OBJECT_NOT_FOUND &&
123
+ err.error.subCategory === SANDBOX_NOT_FOUND
124
+ ) {
125
+ logger.log('');
126
+ logger.warn(
127
+ i18n(`${i18nKey}.objectNotFound`, {
128
+ account: account || accountPrompt.account,
129
+ })
130
+ );
131
+ logger.log('');
132
+
133
+ const promptDefaultAccount = removeAccountFromConfig(sandboxAccountId);
134
+ if (promptDefaultAccount) {
135
+ await selectAndSetAsDefaultAccountPrompt(config);
136
+ }
137
+ process.exit(EXIT_CODES.SUCCESS);
138
+ } else {
139
+ logger.error(err.error.message);
140
+ }
141
+ process.exit(EXIT_CODES.ERROR);
142
+ }
143
+ };
144
+
145
+ exports.builder = yargs => {
146
+ yargs.option('account', {
147
+ describe: i18n(`${i18nKey}.options.account.describe`),
148
+ type: 'string',
149
+ });
150
+
151
+ yargs.example([
152
+ [
153
+ '$0 sandbox delete --account=MySandboxAccount',
154
+ i18n(`${i18nKey}.examples.default`),
155
+ ],
156
+ ]);
157
+
158
+ addConfigOptions(yargs, true);
159
+ addAccountOptions(yargs, true);
160
+ addUseEnvironmentOptions(yargs, true);
161
+ addTestingOptions(yargs, true);
162
+
163
+ return yargs;
164
+ };
@@ -1,5 +1,6 @@
1
1
  const { addConfigOptions, addAccountOptions } = require('../lib/commonOpts');
2
2
  const create = require('./sandbox/create');
3
+ const del = require('./sandbox/delete');
3
4
 
4
5
  // const i18nKey = 'cli.commands.sandbox';
5
6
 
@@ -10,7 +11,10 @@ exports.builder = yargs => {
10
11
  addConfigOptions(yargs, true);
11
12
  addAccountOptions(yargs, true);
12
13
 
13
- yargs.command(create).demandCommand(1, '');
14
+ yargs
15
+ .command(create)
16
+ .command(del)
17
+ .demandCommand(1, '');
14
18
 
15
19
  return yargs;
16
20
  };
@@ -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
@@ -449,6 +449,14 @@ const makePollTaskStatusFunc = ({
449
449
  } with the following error ---`
450
450
  );
451
451
  logger.error(subTask.errorMessage);
452
+
453
+ // Log nested errors
454
+ if (subTask.standardError && subTask.standardError.errors) {
455
+ logger.log();
456
+ subTask.standardError.errors.forEach(error => {
457
+ logger.log(error.message);
458
+ });
459
+ }
452
460
  });
453
461
  }
454
462
 
@@ -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.1",
3
+ "version": "4.0.1-beta.4",
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.1",
12
- "@hubspot/serverless-dev-runtime": "4.0.1-beta.1",
11
+ "@hubspot/cli-lib": "4.0.1-beta.4",
12
+ "@hubspot/serverless-dev-runtime": "4.0.1-beta.4",
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": "eb69a779b705cb7cd43f2c2930fb09f2121283b1"
40
+ "gitHead": "38cd22d1d1a3d8d06d65dfb40ba4d6b3b659e205"
41
41
  }