@hubspot/cli 4.1.7 → 4.1.8-beta.0

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.
@@ -0,0 +1,84 @@
1
+ const { logger } = require('@hubspot/cli-lib/logger');
2
+ const {
3
+ getConfig,
4
+ getConfigPath,
5
+ deleteAccount,
6
+ getConfigDefaultAccount,
7
+ getAccountId: getAccountIdFromConfig,
8
+ updateDefaultAccount,
9
+ } = require('@hubspot/cli-lib/lib/config');
10
+
11
+ const { trackCommandUsage } = require('../../lib/usageTracking');
12
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
13
+ const { selectAccountFromConfig } = require('../../lib/prompts/accountsPrompt');
14
+ const { loadAndValidateOptions } = require('../../lib/validation');
15
+
16
+ const i18nKey = 'cli.commands.accounts.subcommands.remove';
17
+
18
+ exports.command = 'remove [--account]';
19
+ exports.describe = i18n(`${i18nKey}.describe`);
20
+
21
+ exports.handler = async options => {
22
+ await loadAndValidateOptions(options, false);
23
+
24
+ let config = getConfig();
25
+
26
+ let accountToRemove = options.account;
27
+
28
+ if (accountToRemove && !getAccountIdFromConfig(accountToRemove)) {
29
+ logger.error(
30
+ i18n(`${i18nKey}.errors.accountNotFound`, {
31
+ specifiedAccount: accountToRemove,
32
+ configPath: getConfigPath(),
33
+ })
34
+ );
35
+ }
36
+
37
+ if (!accountToRemove || !getAccountIdFromConfig(accountToRemove)) {
38
+ accountToRemove = await selectAccountFromConfig(
39
+ config,
40
+ i18n(`${i18nKey}.prompts.selectAccountToRemove`)
41
+ );
42
+ }
43
+
44
+ trackCommandUsage(
45
+ 'accounts-remove',
46
+ null,
47
+ getAccountIdFromConfig(accountToRemove)
48
+ );
49
+
50
+ const currentDefaultAccount = getConfigDefaultAccount();
51
+
52
+ await deleteAccount(accountToRemove);
53
+ logger.success(
54
+ i18n(`${i18nKey}.success.accountRemoved`, {
55
+ accountName: accountToRemove,
56
+ })
57
+ );
58
+
59
+ // Get updated version of the config
60
+ config = getConfig();
61
+
62
+ if (accountToRemove === currentDefaultAccount) {
63
+ logger.log();
64
+ logger.log(i18n(`${i18nKey}.logs.replaceDefaultAccount`));
65
+ const newDefaultAccount = await selectAccountFromConfig(config);
66
+ updateDefaultAccount(newDefaultAccount);
67
+ }
68
+ };
69
+
70
+ exports.builder = yargs => {
71
+ yargs.option('account', {
72
+ describe: i18n(`${i18nKey}.options.account.describe`),
73
+ type: 'string',
74
+ });
75
+ yargs.example([
76
+ ['$0 accounts remove', i18n(`${i18nKey}.examples.default`)],
77
+ [
78
+ '$0 accounts remove --account=MyAccount',
79
+ i18n(`${i18nKey}.examples.byName`),
80
+ ],
81
+ ]);
82
+
83
+ return yargs;
84
+ };
@@ -4,6 +4,7 @@ const list = require('./accounts/list');
4
4
  const rename = require('./accounts/rename');
5
5
  const use = require('./accounts/use');
6
6
  const info = require('./accounts/info');
7
+ const remove = require('./accounts/remove');
7
8
 
8
9
  const i18nKey = 'cli.commands.accounts';
9
10
 
@@ -22,6 +23,7 @@ exports.builder = yargs => {
22
23
  .command(rename)
23
24
  .command(use)
24
25
  .command(info)
26
+ .command(remove)
25
27
  .demandCommand(1, '');
26
28
 
27
29
  return yargs;
@@ -108,7 +108,6 @@ exports.handler = async options => {
108
108
  );
109
109
  } else {
110
110
  logApiErrorInstance(
111
- accountId,
112
111
  e,
113
112
  new ApiErrorContext({ accountId, request: functionPath })
114
113
  );
@@ -36,7 +36,7 @@ exports.handler = async options => {
36
36
  logger.debug(i18n(`${i18nKey}.debug.gettingFunctions`));
37
37
 
38
38
  const routesResp = await getRoutes(accountId).catch(async e => {
39
- await logApiErrorInstance(accountId, e, new ApiErrorContext({ accountId }));
39
+ await logApiErrorInstance(e, new ApiErrorContext({ accountId }));
40
40
  process.exit(EXIT_CODES.SUCCESS);
41
41
  });
42
42
 
package/commands/list.js CHANGED
@@ -48,11 +48,7 @@ exports.handler = async options => {
48
48
  try {
49
49
  contentsResp = await getDirectoryContentsByPath(accountId, directoryPath);
50
50
  } catch (e) {
51
- logApiErrorInstance(
52
- accountId,
53
- e,
54
- new ApiErrorContext({ accountId, directoryPath })
55
- );
51
+ logApiErrorInstance(e, new ApiErrorContext({ accountId, directoryPath }));
56
52
  process.exit(EXIT_CODES.SUCCESS);
57
53
  }
58
54
 
@@ -14,6 +14,8 @@ const { createSandboxPrompt } = require('../../lib/prompts/sandboxesPrompt');
14
14
  const {
15
15
  getSandboxType,
16
16
  sandboxCreatePersonalAccessKeyFlow,
17
+ getHasDevelopmentSandboxes,
18
+ getDevSandboxLimit,
17
19
  } = require('../../lib/sandboxes');
18
20
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
19
21
  const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
@@ -22,7 +24,7 @@ const {
22
24
  } = require('@hubspot/cli-lib/errorHandlers/standardErrors');
23
25
  const { ENVIRONMENTS } = require('@hubspot/cli-lib/lib/constants');
24
26
  const { EXIT_CODES } = require('../../lib/enums/exitCodes');
25
- const { getAccountConfig } = require('@hubspot/cli-lib');
27
+ const { getAccountConfig, getEnv } = require('@hubspot/cli-lib');
26
28
  const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
27
29
  const {
28
30
  isMissingScopeError,
@@ -127,7 +129,32 @@ exports.handler = async options => {
127
129
  err.error.message
128
130
  ) {
129
131
  logger.log('');
130
- logger.error(err.error.message);
132
+ const devSandboxLimit = getDevSandboxLimit(err.error);
133
+ const plural = devSandboxLimit !== 1;
134
+ const hasDevelopmentSandboxes = getHasDevelopmentSandboxes(accountConfig);
135
+ if (hasDevelopmentSandboxes) {
136
+ logger.error(
137
+ i18n(
138
+ `${i18nKey}.failure.alreadyInConfig.${plural ? 'other' : 'one'}`,
139
+ {
140
+ accountName: accountConfig.name || accountId,
141
+ limit: devSandboxLimit,
142
+ }
143
+ )
144
+ );
145
+ } else {
146
+ const baseUrl = getHubSpotWebsiteOrigin(
147
+ getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
148
+ );
149
+ logger.error(
150
+ i18n(`${i18nKey}.failure.limit.${plural ? 'other' : 'one'}`, {
151
+ accountName: accountConfig.name || accountId,
152
+ limit: devSandboxLimit,
153
+ devSandboxesLink: `${baseUrl}/sandboxes-developer/${accountId}/development`,
154
+ })
155
+ );
156
+ }
157
+ logger.log('');
131
158
  } else {
132
159
  logErrorInstance(err);
133
160
  }
@@ -14,7 +14,7 @@ const {
14
14
  const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
15
15
  const { deleteSandbox } = require('@hubspot/cli-lib/sandboxes');
16
16
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
17
- const { getConfig, getEnv } = require('@hubspot/cli-lib');
17
+ const { getConfig, getEnv, getAccountConfig } = require('@hubspot/cli-lib');
18
18
  const { deleteSandboxPrompt } = require('../../lib/prompts/sandboxesPrompt');
19
19
  const {
20
20
  removeSandboxAccountFromConfig,
@@ -30,6 +30,8 @@ const { ENVIRONMENTS } = require('@hubspot/cli-lib/lib/constants');
30
30
  const {
31
31
  isSpecifiedError,
32
32
  } = require('@hubspot/cli-lib/errorHandlers/apiErrors');
33
+ const { HubSpotAuthError } = require('@hubspot/cli-lib/lib/models/Errors');
34
+ const { getAccountName } = require('../../lib/sandboxes');
33
35
 
34
36
  const i18nKey = 'cli.commands.sandbox.subcommands.delete';
35
37
 
@@ -62,7 +64,7 @@ exports.handler = async options => {
62
64
  const sandboxAccountId = getAccountId({
63
65
  account: account || accountPrompt.account,
64
66
  });
65
-
67
+ const accountConfig = getAccountConfig(sandboxAccountId);
66
68
  const isDefaultAccount =
67
69
  sandboxAccountId === getAccountId(config.defaultPortal);
68
70
 
@@ -89,12 +91,13 @@ exports.handler = async options => {
89
91
  }
90
92
  }
91
93
 
94
+ const parentAccount = getAccountConfig(parentAccountId);
92
95
  const url = `${baseUrl}/sandboxes/${parentAccountId}`;
93
96
  const command = `hs auth ${
94
97
  getEnv(sandboxAccountId) === 'qa' ? '--qa' : ''
95
98
  } --account=${parentAccountId}`;
96
99
 
97
- if (!getAccountId({ account: parentAccountId })) {
100
+ if (parentAccountId && !getAccountId({ account: parentAccountId })) {
98
101
  logger.log('');
99
102
  logger.error(
100
103
  i18n(`${i18nKey}.failure.noParentPortalAvailable`, {
@@ -109,14 +112,14 @@ exports.handler = async options => {
109
112
 
110
113
  logger.debug(
111
114
  i18n(`${i18nKey}.debug.deleting`, {
112
- account: account || accountPrompt.account,
115
+ account: getAccountName(accountConfig),
113
116
  })
114
117
  );
115
118
 
116
119
  if (isDefaultAccount) {
117
120
  logger.log(
118
121
  i18n(`${i18nKey}.defaultAccountWarning`, {
119
- account: account || accountPrompt.account,
122
+ account: getAccountName(accountConfig),
120
123
  })
121
124
  );
122
125
  }
@@ -128,7 +131,7 @@ exports.handler = async options => {
128
131
  name: 'confirmSandboxDeletePrompt',
129
132
  type: 'confirm',
130
133
  message: i18n(`${i18nKey}.confirm`, {
131
- account: account || accountPrompt.account,
134
+ account: getAccountName(accountConfig),
132
135
  }),
133
136
  },
134
137
  ]);
@@ -170,7 +173,20 @@ exports.handler = async options => {
170
173
  sandboxAccountId
171
174
  );
172
175
 
173
- if (
176
+ if (err instanceof HubSpotAuthError) {
177
+ // Intercept invalid key error
178
+ // This command uses the parent portal PAK to delete a sandbox, so we must specify which account needs a new key
179
+ const regex = /\bYour personal access key is invalid\b/;
180
+ const match = err.message.match(regex);
181
+ if (match && match[0]) {
182
+ logger.log('');
183
+ logger.error(
184
+ i18n(`${i18nKey}.failure.invalidKey`, {
185
+ account: getAccountName(parentAccount),
186
+ })
187
+ );
188
+ }
189
+ } else if (
174
190
  isSpecifiedError(
175
191
  err,
176
192
  404,
@@ -181,7 +197,7 @@ exports.handler = async options => {
181
197
  logger.log('');
182
198
  logger.warn(
183
199
  i18n(`${i18nKey}.failure.objectNotFound`, {
184
- account: account || accountPrompt.account,
200
+ account: getAccountName(accountConfig),
185
201
  })
186
202
  );
187
203
  logger.log('');
@@ -15,14 +15,14 @@ const mapAccountChoices = portals =>
15
15
 
16
16
  const i18nKey = 'cli.commands.accounts.subcommands.use';
17
17
 
18
- const selectAccountFromConfig = async config => {
18
+ const selectAccountFromConfig = async (config, prompt) => {
19
19
  const { default: selectedDefault } = await promptUser([
20
20
  {
21
21
  type: 'list',
22
22
  look: false,
23
23
  name: 'default',
24
24
  pageSize: 20,
25
- message: i18n(`${i18nKey}.promptMessage`),
25
+ message: prompt || i18n(`${i18nKey}.promptMessage`),
26
26
  choices: mapAccountChoices(config.portals),
27
27
  default: config.defaultPortal,
28
28
  },
@@ -66,7 +66,7 @@ const projectLogsPrompt = (accountId, promptOptions = {}) => {
66
66
 
67
67
  if (deployedBuild && deployedBuild.subbuildStatuses) {
68
68
  return deployedBuild.subbuildStatuses
69
- .filter(subbuild => subbuild.buildType === 'APP')
69
+ .filter(subbuild => subbuild.buildType === 'PRIVATE_APP')
70
70
  .map(subbuild => ({
71
71
  name: subbuild.buildName,
72
72
  value: subbuild.buildName,
package/lib/sandboxes.js CHANGED
@@ -35,6 +35,29 @@ function getAccountName(config) {
35
35
  return `${config.name} ${isSandbox ? sandboxName : ''}(${config.portalId})`;
36
36
  }
37
37
 
38
+ function getHasDevelopmentSandboxes(parentAccountConfig) {
39
+ const config = getConfig();
40
+ const parentPortalId = parentAccountConfig.portalId;
41
+ for (const portal of config.portals) {
42
+ if (
43
+ (portal.parentAccountId !== null ||
44
+ portal.parentAccountId !== undefined) &&
45
+ portal.parentAccountId === parentPortalId &&
46
+ portal.sandboxAccountType &&
47
+ portal.sandboxAccountType === 'DEVELOPER'
48
+ ) {
49
+ return true;
50
+ }
51
+ }
52
+ return false;
53
+ }
54
+
55
+ function getDevSandboxLimit(error) {
56
+ // Error context should contain a limit property with a list of one number. That number is the current limit
57
+ const limit = error.context && error.context.limit && error.context.limit[0];
58
+ return limit ? parseInt(limit, 10) : 1; // Default to 1
59
+ }
60
+
38
61
  // Fetches available sync types for a given sandbox portal
39
62
  async function getAvailableSyncTypes(parentAccountConfig, config) {
40
63
  const parentPortalId = parentAccountConfig.portalId;
@@ -211,6 +234,8 @@ function pollSyncTaskStatus(accountId, taskId, syncStatusUrl) {
211
234
  module.exports = {
212
235
  getSandboxType,
213
236
  getAccountName,
237
+ getHasDevelopmentSandboxes,
238
+ getDevSandboxLimit,
214
239
  getAvailableSyncTypes,
215
240
  sandboxCreatePersonalAccessKeyFlow,
216
241
  pollSyncTaskStatus,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "4.1.7",
3
+ "version": "4.1.8-beta.0",
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.1.7",
12
- "@hubspot/serverless-dev-runtime": "4.1.7",
11
+ "@hubspot/cli-lib": "4.1.8-beta.0",
12
+ "@hubspot/serverless-dev-runtime": "4.1.8-beta.0",
13
13
  "archiver": "^5.3.0",
14
14
  "chalk": "^4.1.2",
15
15
  "cli-progress": "^3.11.2",
@@ -38,5 +38,5 @@
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
- "gitHead": "ef2292568ec8308feb0c5841e4d3dd25a64d1050"
41
+ "gitHead": "af16182b2c7abd401771d5e0eed3de21e7b0477c"
42
42
  }