@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 +1 -15
- package/bin/cli.js +2 -0
- package/commands/accounts/info.js +53 -0
- package/commands/accounts/list.js +50 -3
- package/commands/accounts/use.js +9 -26
- package/commands/accounts.js +2 -0
- package/commands/auth.js +0 -22
- package/commands/cms/lighthouseScore.js +322 -0
- package/commands/cms.js +16 -0
- package/commands/fetch.js +7 -0
- package/commands/init.js +0 -19
- package/commands/project/logs.js +32 -6
- package/commands/sandbox/create.js +29 -18
- package/commands/sandbox/delete.js +164 -0
- package/commands/sandbox.js +5 -1
- package/hubspot.sample.config.yml +0 -7
- package/lib/__tests__/validation.js +0 -7
- package/lib/projects.js +8 -0
- package/lib/prompts/accountsPrompt.js +47 -0
- package/lib/prompts/personalAccessKeyPrompt.js +0 -15
- package/lib/prompts/sandboxesPrompt.js +19 -0
- package/lib/regex.js +0 -2
- package/lib/validation.js +7 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -133,7 +133,7 @@ hs hubdb delete <id or name>
|
|
|
133
133
|
|
|
134
134
|
## Authentication
|
|
135
135
|
|
|
136
|
-
There are
|
|
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
|
|
32
|
-
|
|
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`),
|
package/commands/accounts/use.js
CHANGED
|
@@ -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(
|
|
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/accounts.js
CHANGED
|
@@ -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
|
+
};
|
package/commands/cms.js
ADDED
|
@@ -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
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`, {
|
package/commands/project/logs.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
134
|
-
({
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
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
|
+
};
|
package/commands/sandbox.js
CHANGED
|
@@ -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
|
|
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
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
|
-
|
|
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.
|
|
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.
|
|
12
|
-
"@hubspot/serverless-dev-runtime": "4.0.1-beta.
|
|
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": "
|
|
40
|
+
"gitHead": "38cd22d1d1a3d8d06d65dfb40ba4d6b3b659e205"
|
|
41
41
|
}
|