@hubspot/cli 4.1.8-beta.0 → 4.1.8-beta.2
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 +2 -2
- package/commands/accounts/list.js +2 -2
- package/commands/create/api-sample.js +1 -1
- package/commands/project/add.js +56 -0
- package/commands/project/create.js +6 -3
- package/commands/project/deploy.js +32 -18
- package/commands/project/dev.js +278 -0
- package/commands/project/download.js +27 -16
- package/commands/project/upload.js +27 -75
- package/commands/project.js +4 -0
- package/commands/sandbox/create.js +133 -117
- package/commands/sandbox/delete.js +9 -9
- package/commands/sandbox/sync.js +30 -142
- package/lib/CliProgressMultibarManager.js +66 -0
- package/lib/LocalDevManager.js +526 -0
- package/lib/SpinniesManager.js +96 -0
- package/lib/projects.js +314 -126
- package/lib/prompts/accountsPrompt.js +2 -4
- package/lib/prompts/buildIdPrompt.js +35 -0
- package/lib/prompts/createProjectPrompt.js +43 -6
- package/lib/prompts/downloadProjectPrompt.js +44 -0
- package/lib/prompts/projectAddPrompt.js +57 -0
- package/lib/prompts/projectDevTargetAccountPrompt.js +105 -0
- package/lib/prompts/promptUtils.js +13 -0
- package/lib/prompts/sandboxesPrompt.js +50 -11
- package/lib/sandbox-create.js +199 -0
- package/lib/sandbox-sync.js +199 -0
- package/lib/sandboxes.js +296 -105
- package/lib/ui.js +15 -6
- package/package.json +6 -4
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
const { updateDefaultAccount } = require('@hubspot/cli-lib/lib/config');
|
|
2
2
|
const { promptUser } = require('./promptUtils');
|
|
3
3
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
4
|
-
const {
|
|
4
|
+
const { getAccountName } = require('../sandboxes');
|
|
5
5
|
|
|
6
6
|
const mapAccountChoices = portals =>
|
|
7
7
|
portals.map(p => {
|
|
8
|
-
const isSandbox = p.sandboxAccountType && p.sandboxAccountType !== null;
|
|
9
|
-
const sandboxName = `[${getSandboxType(p.sandboxAccountType)} sandbox] `;
|
|
10
8
|
return {
|
|
11
|
-
name:
|
|
9
|
+
name: getAccountName(p),
|
|
12
10
|
value: p.name || p.portalId,
|
|
13
11
|
};
|
|
14
12
|
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const { promptUser } = require('./promptUtils');
|
|
2
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
3
|
+
|
|
4
|
+
const i18nKey = 'cli.lib.prompts.buildIdPrompt';
|
|
5
|
+
|
|
6
|
+
const buildIdPrompt = (latestBuildId, deployedBuildId, projectName) => {
|
|
7
|
+
return promptUser({
|
|
8
|
+
name: 'buildId',
|
|
9
|
+
message: i18n(`${i18nKey}.enterBuildId`),
|
|
10
|
+
default: () => {
|
|
11
|
+
if (latestBuildId === deployedBuildId) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
return latestBuildId;
|
|
15
|
+
},
|
|
16
|
+
validate: val => {
|
|
17
|
+
if (Number(val) > latestBuildId) {
|
|
18
|
+
return i18n(`${i18nKey}.errors.buildIdDoesNotExist`, {
|
|
19
|
+
buildId: val,
|
|
20
|
+
projectName,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
if (Number(val) === deployedBuildId) {
|
|
24
|
+
return i18n(`${i18nKey}.errors.buildAlreadyDeployed`, {
|
|
25
|
+
buildId: val,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
buildIdPrompt,
|
|
35
|
+
};
|
|
@@ -1,12 +1,49 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const { getCwd } = require('@hubspot/cli-lib/path');
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
PROJECT_COMPONENT_TYPES,
|
|
5
|
+
PROJECT_PROPERTIES,
|
|
6
|
+
} = require('@hubspot/cli-lib/lib/constants');
|
|
4
7
|
const { promptUser } = require('./promptUtils');
|
|
8
|
+
const { fetchJsonFromRepository } = require('@hubspot/cli-lib/github');
|
|
5
9
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
10
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
11
|
+
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
6
12
|
|
|
7
13
|
const i18nKey = 'cli.lib.prompts.createProjectPrompt';
|
|
8
14
|
|
|
9
|
-
const
|
|
15
|
+
const hasAllProperties = projectList => {
|
|
16
|
+
return projectList.every(config =>
|
|
17
|
+
PROJECT_PROPERTIES.every(p =>
|
|
18
|
+
Object.prototype.hasOwnProperty.call(config, p)
|
|
19
|
+
)
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const createTemplateOptions = async repoPath => {
|
|
24
|
+
const isRepoPath = !!repoPath;
|
|
25
|
+
const config = await fetchJsonFromRepository(
|
|
26
|
+
repoPath,
|
|
27
|
+
'main/config.json',
|
|
28
|
+
isRepoPath
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (!config || !config[PROJECT_COMPONENT_TYPES.PROJECTS]) {
|
|
32
|
+
logger.error(i18n(`${i18nKey}.errors.noProjectsInConfig`));
|
|
33
|
+
process.exit(EXIT_CODES.ERROR);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!hasAllProperties(config[PROJECT_COMPONENT_TYPES.PROJECTS])) {
|
|
37
|
+
logger.error(i18n(`${i18nKey}.errors.missingPropertiesInConfig`));
|
|
38
|
+
process.exit(EXIT_CODES.ERROR);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return config[PROJECT_COMPONENT_TYPES.PROJECTS];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const createProjectPrompt = async (promptOptions = {}) => {
|
|
45
|
+
const projectTemplates = await createTemplateOptions(promptOptions.repoPath);
|
|
46
|
+
|
|
10
47
|
return promptUser([
|
|
11
48
|
{
|
|
12
49
|
name: 'name',
|
|
@@ -37,7 +74,7 @@ const createProjectPrompt = (promptOptions = {}) => {
|
|
|
37
74
|
name: 'template',
|
|
38
75
|
message: () => {
|
|
39
76
|
return promptOptions.template &&
|
|
40
|
-
!
|
|
77
|
+
!projectTemplates.find(t => t.name === promptOptions.template)
|
|
41
78
|
? i18n(`${i18nKey}.errors.invalidTemplate`, {
|
|
42
79
|
template: promptOptions.template,
|
|
43
80
|
})
|
|
@@ -45,12 +82,12 @@ const createProjectPrompt = (promptOptions = {}) => {
|
|
|
45
82
|
},
|
|
46
83
|
when:
|
|
47
84
|
!promptOptions.template ||
|
|
48
|
-
!
|
|
85
|
+
!projectTemplates.find(t => t.name === promptOptions.template),
|
|
49
86
|
type: 'list',
|
|
50
|
-
choices:
|
|
87
|
+
choices: projectTemplates.map(template => {
|
|
51
88
|
return {
|
|
52
89
|
name: template.label,
|
|
53
|
-
value: template
|
|
90
|
+
value: template,
|
|
54
91
|
};
|
|
55
92
|
}),
|
|
56
93
|
},
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const { promptUser } = require('./promptUtils');
|
|
2
|
+
const { getAccountId } = require('@hubspot/cli-lib/lib/config');
|
|
3
|
+
const { fetchProjects } = require('@hubspot/cli-lib/api/dfs');
|
|
4
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
5
|
+
|
|
6
|
+
const i18nKey = 'cli.lib.prompts.downloadProjectPrompt';
|
|
7
|
+
|
|
8
|
+
const createProjectsList = async () => {
|
|
9
|
+
const projects = await fetchProjects(getAccountId());
|
|
10
|
+
return projects.results;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const downloadProjectPrompt = async (promptOptions = {}) => {
|
|
14
|
+
const projectsList = await createProjectsList();
|
|
15
|
+
|
|
16
|
+
return promptUser([
|
|
17
|
+
{
|
|
18
|
+
name: 'project',
|
|
19
|
+
message: () => {
|
|
20
|
+
return promptOptions.project &&
|
|
21
|
+
!projectsList.find(p => p.name === promptOptions.name)
|
|
22
|
+
? i18n(`${i18nKey}.errors.projectNotFound`, {
|
|
23
|
+
projectName: promptOptions.project,
|
|
24
|
+
accountId: getAccountId(),
|
|
25
|
+
})
|
|
26
|
+
: i18n(`${i18nKey}.selectProject`);
|
|
27
|
+
},
|
|
28
|
+
when:
|
|
29
|
+
!promptOptions.project ||
|
|
30
|
+
!projectsList.find(p => p.name === promptOptions.project),
|
|
31
|
+
type: 'list',
|
|
32
|
+
choices: projectsList.map(project => {
|
|
33
|
+
return {
|
|
34
|
+
name: project.name,
|
|
35
|
+
value: project.name,
|
|
36
|
+
};
|
|
37
|
+
}),
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
downloadProjectPrompt,
|
|
44
|
+
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
const { promptUser } = require('./promptUtils');
|
|
2
|
+
const { fetchJsonFromRepository } = require('@hubspot/cli-lib/github');
|
|
3
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
4
|
+
const { PROJECT_COMPONENT_TYPES } = require('@hubspot/cli-lib/lib/constants');
|
|
5
|
+
|
|
6
|
+
const i18nKey = 'cli.lib.prompts.projectAddPrompt';
|
|
7
|
+
|
|
8
|
+
const createTypeOptions = async () => {
|
|
9
|
+
const config = await fetchJsonFromRepository(
|
|
10
|
+
'HubSpot/hubspot-project-components',
|
|
11
|
+
'main/config.json'
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
return config[PROJECT_COMPONENT_TYPES.COMPONENTS];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const projectAddPrompt = async (promptOptions = {}) => {
|
|
18
|
+
const components = await createTypeOptions();
|
|
19
|
+
return promptUser([
|
|
20
|
+
{
|
|
21
|
+
name: 'type',
|
|
22
|
+
message: () => {
|
|
23
|
+
return promptOptions.type &&
|
|
24
|
+
!components.find(t => t.path === promptOptions.type.path)
|
|
25
|
+
? i18n(`${i18nKey}.errors.invalidType`, {
|
|
26
|
+
type: promptOptions.type,
|
|
27
|
+
})
|
|
28
|
+
: i18n(`${i18nKey}.selectType`);
|
|
29
|
+
},
|
|
30
|
+
when:
|
|
31
|
+
!promptOptions.type ||
|
|
32
|
+
!components.find(t => t.path === promptOptions.type.path),
|
|
33
|
+
type: 'list',
|
|
34
|
+
choices: components.map(type => {
|
|
35
|
+
return {
|
|
36
|
+
name: type.label,
|
|
37
|
+
value: type,
|
|
38
|
+
};
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'name',
|
|
43
|
+
message: i18n(`${i18nKey}.enterName`),
|
|
44
|
+
when: !promptOptions.name,
|
|
45
|
+
validate: input => {
|
|
46
|
+
if (!input) {
|
|
47
|
+
return i18n(`${i18nKey}.errors.nameRequired`);
|
|
48
|
+
}
|
|
49
|
+
return true;
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
]);
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
projectAddPrompt,
|
|
57
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const { promptUser } = require('./promptUtils');
|
|
2
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
3
|
+
const { uiAccountDescription } = require('../ui');
|
|
4
|
+
const { isSandbox, getAccountName } = require('../sandboxes');
|
|
5
|
+
const { getAccountId } = require('@hubspot/cli-lib');
|
|
6
|
+
const { getSandboxUsageLimits } = require('@hubspot/cli-lib/sandboxes');
|
|
7
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
8
|
+
|
|
9
|
+
const i18nKey = 'cli.lib.prompts.projectDevTargetAccountPrompt';
|
|
10
|
+
|
|
11
|
+
const mapSandboxAccount = accountConfig => ({
|
|
12
|
+
name: getAccountName(accountConfig),
|
|
13
|
+
value: {
|
|
14
|
+
targetAccountId: getAccountId(accountConfig.name),
|
|
15
|
+
chooseNonSandbox: false,
|
|
16
|
+
createNewSandbox: false,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const selectTargetAccountPrompt = async (
|
|
21
|
+
accounts,
|
|
22
|
+
defaultAccountConfig,
|
|
23
|
+
nonSandbox = false
|
|
24
|
+
) => {
|
|
25
|
+
let choices;
|
|
26
|
+
|
|
27
|
+
if (nonSandbox) {
|
|
28
|
+
choices = accounts
|
|
29
|
+
.filter(accountConfig => !isSandbox(accountConfig))
|
|
30
|
+
.map(accountConfig => {
|
|
31
|
+
const accountId = getAccountId(accountConfig.name);
|
|
32
|
+
return {
|
|
33
|
+
name: uiAccountDescription(accountId),
|
|
34
|
+
value: {
|
|
35
|
+
targetAccountId: accountId,
|
|
36
|
+
chooseNonSandbox: false,
|
|
37
|
+
createNewSandbox: false,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
let sandboxUsage = {};
|
|
43
|
+
try {
|
|
44
|
+
const accountId = getAccountId(defaultAccountConfig.portalId);
|
|
45
|
+
sandboxUsage = await getSandboxUsageLimits(accountId);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
logger.debug('Unable to fetch sandbox usage limits: ', err);
|
|
48
|
+
}
|
|
49
|
+
const sandboxAccounts = accounts.reverse().filter(isSandbox);
|
|
50
|
+
let disabledMessage = false;
|
|
51
|
+
if (isSandbox(defaultAccountConfig)) {
|
|
52
|
+
disabledMessage = i18n(`${i18nKey}.defaultAccountNotProd`);
|
|
53
|
+
}
|
|
54
|
+
if (
|
|
55
|
+
sandboxUsage['DEVELOPER'] &&
|
|
56
|
+
sandboxUsage['DEVELOPER'].available === 0
|
|
57
|
+
) {
|
|
58
|
+
disabledMessage = i18n(`${i18nKey}.sandboxLimit`, {
|
|
59
|
+
limit: sandboxUsage['DEVELOPER'].limit,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Order choices by Create new -> Developer Sandbox -> Standard Sandbox -> Non sandbox
|
|
63
|
+
choices = [
|
|
64
|
+
{
|
|
65
|
+
name: i18n(`${i18nKey}.createNewSandboxOption`),
|
|
66
|
+
value: {
|
|
67
|
+
targetAccountId: null,
|
|
68
|
+
chooseNonSandbox: false,
|
|
69
|
+
createNewSandbox: true,
|
|
70
|
+
},
|
|
71
|
+
disabled: disabledMessage,
|
|
72
|
+
},
|
|
73
|
+
...sandboxAccounts
|
|
74
|
+
.filter(a => a.sandboxAccountType === 'DEVELOPER')
|
|
75
|
+
.map(mapSandboxAccount),
|
|
76
|
+
...sandboxAccounts
|
|
77
|
+
.filter(a => a.sandboxAccountType === 'STANDARD')
|
|
78
|
+
.map(mapSandboxAccount),
|
|
79
|
+
{
|
|
80
|
+
name: i18n(`${i18nKey}.chooseNonSandboxOption`),
|
|
81
|
+
value: {
|
|
82
|
+
targetAccountId: null,
|
|
83
|
+
chooseNonSandbox: true,
|
|
84
|
+
createNewSandbox: false,
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
];
|
|
88
|
+
}
|
|
89
|
+
const { targetAccountInfo } = await promptUser([
|
|
90
|
+
{
|
|
91
|
+
name: 'targetAccountInfo',
|
|
92
|
+
type: 'list',
|
|
93
|
+
message: nonSandbox
|
|
94
|
+
? i18n(`${i18nKey}.chooseNonSandboxAccount`)
|
|
95
|
+
: i18n(`${i18nKey}.chooseSandboxAccount`),
|
|
96
|
+
choices,
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
return targetAccountInfo;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
module.exports = {
|
|
104
|
+
selectTargetAccountPrompt,
|
|
105
|
+
};
|
|
@@ -5,6 +5,19 @@ const promptUser = async promptConfig => {
|
|
|
5
5
|
return prompt(promptConfig);
|
|
6
6
|
};
|
|
7
7
|
|
|
8
|
+
const confirmPrompt = async (message, defaultAnswer = true) => {
|
|
9
|
+
const { choice } = await promptUser([
|
|
10
|
+
{
|
|
11
|
+
name: 'choice',
|
|
12
|
+
type: 'confirm',
|
|
13
|
+
default: defaultAnswer,
|
|
14
|
+
message,
|
|
15
|
+
},
|
|
16
|
+
]);
|
|
17
|
+
return choice;
|
|
18
|
+
};
|
|
19
|
+
|
|
8
20
|
module.exports = {
|
|
9
21
|
promptUser,
|
|
22
|
+
confirmPrompt,
|
|
10
23
|
};
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
const { promptUser } = require('./promptUtils');
|
|
2
2
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
3
|
-
const {
|
|
3
|
+
const {
|
|
4
|
+
getSandboxTypeAsString,
|
|
5
|
+
STANDARD_SANDBOX,
|
|
6
|
+
DEVELOPER_SANDBOX,
|
|
7
|
+
} = require('../sandboxes');
|
|
8
|
+
const { accountNameExistsInConfig } = require('@hubspot/cli-lib/lib/config');
|
|
4
9
|
|
|
5
10
|
const i18nKey = 'cli.lib.prompts.sandboxesPrompt';
|
|
6
11
|
|
|
@@ -8,7 +13,9 @@ const mapSandboxAccountChoices = portals =>
|
|
|
8
13
|
portals
|
|
9
14
|
.filter(p => p.sandboxAccountType && p.sandboxAccountType !== null)
|
|
10
15
|
.map(p => {
|
|
11
|
-
const sandboxName = `[${
|
|
16
|
+
const sandboxName = `[${getSandboxTypeAsString(
|
|
17
|
+
p.sandboxAccountType
|
|
18
|
+
)} sandbox] `;
|
|
12
19
|
return {
|
|
13
20
|
name: `${p.name} ${sandboxName}(${p.portalId})`,
|
|
14
21
|
value: p.name || p.portalId,
|
|
@@ -27,18 +34,50 @@ const mapNonSandboxAccountChoices = portals =>
|
|
|
27
34
|
};
|
|
28
35
|
});
|
|
29
36
|
|
|
30
|
-
const
|
|
37
|
+
const sandboxNamePrompt = (type = STANDARD_SANDBOX) => {
|
|
38
|
+
const isDeveloperSandbox = type === DEVELOPER_SANDBOX;
|
|
39
|
+
const namePromptMessage = isDeveloperSandbox
|
|
40
|
+
? `${i18nKey}.name.developmentSandboxMessage`
|
|
41
|
+
: `${i18nKey}.name.message`;
|
|
31
42
|
return promptUser([
|
|
32
43
|
{
|
|
33
44
|
name: 'name',
|
|
34
|
-
message: i18n(
|
|
45
|
+
message: i18n(namePromptMessage),
|
|
35
46
|
validate(val) {
|
|
36
47
|
if (typeof val !== 'string') {
|
|
37
|
-
return i18n(`${i18nKey}.errors.invalidName`);
|
|
48
|
+
return i18n(`${i18nKey}.name.errors.invalidName`);
|
|
49
|
+
} else if (!val.length) {
|
|
50
|
+
return i18n(`${i18nKey}.name.errors.nameRequired`);
|
|
38
51
|
}
|
|
39
|
-
return
|
|
52
|
+
return accountNameExistsInConfig(val)
|
|
53
|
+
? i18n(`${i18nKey}.name.errors.accountNameExists`, { name: val })
|
|
54
|
+
: true;
|
|
40
55
|
},
|
|
41
|
-
default:
|
|
56
|
+
default: `New ${isDeveloperSandbox ? 'development ' : ''}sandbox`,
|
|
57
|
+
},
|
|
58
|
+
]);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const sandboxTypeChoices = [
|
|
62
|
+
{
|
|
63
|
+
name: i18n(`${i18nKey}.type.developer`),
|
|
64
|
+
value: 'DEVELOPER',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: i18n(`${i18nKey}.type.standard`),
|
|
68
|
+
value: 'STANDARD',
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
const sandboxTypePrompt = () => {
|
|
73
|
+
return promptUser([
|
|
74
|
+
{
|
|
75
|
+
name: 'type',
|
|
76
|
+
message: i18n(`${i18nKey}.type.message`),
|
|
77
|
+
type: 'list',
|
|
78
|
+
look: false,
|
|
79
|
+
choices: sandboxTypeChoices,
|
|
80
|
+
default: 'DEVELOPER',
|
|
42
81
|
},
|
|
43
82
|
]);
|
|
44
83
|
};
|
|
@@ -55,8 +94,8 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
|
|
|
55
94
|
name: 'account',
|
|
56
95
|
message: i18n(
|
|
57
96
|
promptParentAccount
|
|
58
|
-
? `${i18nKey}.selectParentAccountName`
|
|
59
|
-
: `${i18nKey}.selectAccountName`
|
|
97
|
+
? `${i18nKey}.name.selectParentAccountName`
|
|
98
|
+
: `${i18nKey}.name.selectAccountName`
|
|
60
99
|
),
|
|
61
100
|
type: 'list',
|
|
62
101
|
look: false,
|
|
@@ -68,7 +107,7 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
|
|
|
68
107
|
};
|
|
69
108
|
|
|
70
109
|
module.exports = {
|
|
71
|
-
|
|
110
|
+
sandboxNamePrompt,
|
|
111
|
+
sandboxTypePrompt,
|
|
72
112
|
deleteSandboxPrompt,
|
|
73
|
-
getSandboxType,
|
|
74
113
|
};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
const Spinnies = require('spinnies');
|
|
2
|
+
const {
|
|
3
|
+
getSandboxLimit,
|
|
4
|
+
getHasSandboxesByType,
|
|
5
|
+
saveSandboxToConfig,
|
|
6
|
+
sandboxApiTypeMap,
|
|
7
|
+
STANDARD_SANDBOX,
|
|
8
|
+
DEVELOPER_SANDBOX,
|
|
9
|
+
} = require('./sandboxes');
|
|
10
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
11
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
12
|
+
const {
|
|
13
|
+
debugErrorAndContext,
|
|
14
|
+
logErrorInstance,
|
|
15
|
+
} = require('@hubspot/cli-lib/errorHandlers/standardErrors');
|
|
16
|
+
const {
|
|
17
|
+
isMissingScopeError,
|
|
18
|
+
isSpecifiedError,
|
|
19
|
+
} = require('@hubspot/cli-lib/errorHandlers/apiErrors');
|
|
20
|
+
const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
21
|
+
const { getEnv, getAccountId } = require('@hubspot/cli-lib');
|
|
22
|
+
const { createSandbox } = require('@hubspot/cli-lib/sandboxes');
|
|
23
|
+
const { getValidEnv } = require('@hubspot/cli-lib/lib/environment');
|
|
24
|
+
|
|
25
|
+
const i18nKey = 'cli.lib.sandbox.create';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {String} name - Name of sandbox
|
|
29
|
+
* @param {String} type - Sandbox type to be created (standard/developer)
|
|
30
|
+
* @param {Object} accountConfig - Account config of parent portal
|
|
31
|
+
* @param {String} env - Environment (QA/Prod)
|
|
32
|
+
* @returns {Object} Object containing sandboxConfigName string and sandbox instance from API
|
|
33
|
+
*/
|
|
34
|
+
const buildSandbox = async ({
|
|
35
|
+
name,
|
|
36
|
+
type,
|
|
37
|
+
accountConfig,
|
|
38
|
+
env,
|
|
39
|
+
force = false,
|
|
40
|
+
}) => {
|
|
41
|
+
const spinnies = new Spinnies({
|
|
42
|
+
succeedColor: 'white',
|
|
43
|
+
});
|
|
44
|
+
const accountId = getAccountId(accountConfig.portalId);
|
|
45
|
+
|
|
46
|
+
let result;
|
|
47
|
+
const spinniesI18nKey = `${i18nKey}.loading.${type}`;
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
logger.log('');
|
|
51
|
+
spinnies.add('sandboxCreate', {
|
|
52
|
+
text: i18n(`${spinniesI18nKey}.add`, {
|
|
53
|
+
sandboxName: name,
|
|
54
|
+
}),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const sandboxApiType = sandboxApiTypeMap[type]; // API expects sandbox type as 1 or 2
|
|
58
|
+
result = await createSandbox(accountId, name, sandboxApiType);
|
|
59
|
+
|
|
60
|
+
spinnies.succeed('sandboxCreate', {
|
|
61
|
+
text: i18n(`${spinniesI18nKey}.succeed`, {
|
|
62
|
+
name: result.sandbox.name,
|
|
63
|
+
sandboxHubId: result.sandbox.sandboxHubId,
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
66
|
+
} catch (err) {
|
|
67
|
+
debugErrorAndContext(err);
|
|
68
|
+
|
|
69
|
+
spinnies.fail('sandboxCreate', {
|
|
70
|
+
text: i18n(`${spinniesI18nKey}.fail`, {
|
|
71
|
+
sandboxName: name,
|
|
72
|
+
}),
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (isMissingScopeError(err)) {
|
|
76
|
+
logger.error(
|
|
77
|
+
i18n(`${i18nKey}.failure.scopes.message`, {
|
|
78
|
+
accountName: accountConfig.name || accountId,
|
|
79
|
+
})
|
|
80
|
+
);
|
|
81
|
+
const websiteOrigin = getHubSpotWebsiteOrigin(env);
|
|
82
|
+
const url = `${websiteOrigin}/personal-access-key/${accountId}`;
|
|
83
|
+
logger.info(
|
|
84
|
+
i18n(`${i18nKey}.failure.scopes.instructions`, {
|
|
85
|
+
accountName: accountConfig.name || accountId,
|
|
86
|
+
url,
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
} else if (
|
|
90
|
+
isSpecifiedError(err, {
|
|
91
|
+
statusCode: 400,
|
|
92
|
+
category: 'VALIDATION_ERROR',
|
|
93
|
+
subCategory:
|
|
94
|
+
'SandboxErrors.NUM_DEVELOPMENT_SANDBOXES_LIMIT_EXCEEDED_ERROR',
|
|
95
|
+
}) &&
|
|
96
|
+
err.error &&
|
|
97
|
+
err.error.message
|
|
98
|
+
) {
|
|
99
|
+
logger.log('');
|
|
100
|
+
const devSandboxLimit = getSandboxLimit(err.error);
|
|
101
|
+
const plural = devSandboxLimit !== 1;
|
|
102
|
+
const hasDevelopmentSandboxes = getHasSandboxesByType(
|
|
103
|
+
accountConfig,
|
|
104
|
+
DEVELOPER_SANDBOX
|
|
105
|
+
);
|
|
106
|
+
if (hasDevelopmentSandboxes) {
|
|
107
|
+
logger.error(
|
|
108
|
+
i18n(
|
|
109
|
+
`${i18nKey}.failure.alreadyInConfig.developer.${
|
|
110
|
+
plural ? 'other' : 'one'
|
|
111
|
+
}`,
|
|
112
|
+
{
|
|
113
|
+
accountName: accountConfig.name || accountId,
|
|
114
|
+
limit: devSandboxLimit,
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
);
|
|
118
|
+
} else {
|
|
119
|
+
const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getEnv(accountId)));
|
|
120
|
+
logger.error(
|
|
121
|
+
i18n(
|
|
122
|
+
`${i18nKey}.failure.limit.developer.${plural ? 'other' : 'one'}`,
|
|
123
|
+
{
|
|
124
|
+
accountName: accountConfig.name || accountId,
|
|
125
|
+
limit: devSandboxLimit,
|
|
126
|
+
link: `${baseUrl}/sandboxes-developer/${accountId}/development`,
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
logger.log('');
|
|
132
|
+
} else if (
|
|
133
|
+
isSpecifiedError(err, {
|
|
134
|
+
statusCode: 400,
|
|
135
|
+
category: 'VALIDATION_ERROR',
|
|
136
|
+
subCategory:
|
|
137
|
+
'SandboxErrors.NUM_STANDARD_SANDBOXES_LIMIT_EXCEEDED_ERROR',
|
|
138
|
+
}) &&
|
|
139
|
+
err.error &&
|
|
140
|
+
err.error.message
|
|
141
|
+
) {
|
|
142
|
+
logger.log('');
|
|
143
|
+
const standardSandboxLimit = getSandboxLimit(err.error);
|
|
144
|
+
const plural = standardSandboxLimit !== 1;
|
|
145
|
+
const hasStandardSandboxes = getHasSandboxesByType(
|
|
146
|
+
accountConfig,
|
|
147
|
+
STANDARD_SANDBOX
|
|
148
|
+
);
|
|
149
|
+
if (hasStandardSandboxes) {
|
|
150
|
+
logger.error(
|
|
151
|
+
i18n(
|
|
152
|
+
`${i18nKey}.failure.alreadyInConfig.standard.${
|
|
153
|
+
plural ? 'other' : 'one'
|
|
154
|
+
}`,
|
|
155
|
+
{
|
|
156
|
+
accountName: accountConfig.name || accountId,
|
|
157
|
+
limit: standardSandboxLimit,
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
);
|
|
161
|
+
} else {
|
|
162
|
+
const baseUrl = getHubSpotWebsiteOrigin(getValidEnv(getEnv(accountId)));
|
|
163
|
+
logger.error(
|
|
164
|
+
i18n(
|
|
165
|
+
`${i18nKey}.failure.limit.standard.${plural ? 'other' : 'one'}`,
|
|
166
|
+
{
|
|
167
|
+
accountName: accountConfig.name || accountId,
|
|
168
|
+
limit: standardSandboxLimit,
|
|
169
|
+
link: `${baseUrl}/sandboxes-developer/${accountId}/standard`,
|
|
170
|
+
}
|
|
171
|
+
)
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
logger.log('');
|
|
175
|
+
} else {
|
|
176
|
+
logErrorInstance(err);
|
|
177
|
+
}
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let sandboxConfigName;
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
// Response contains PAK, save to config here
|
|
185
|
+
sandboxConfigName = await saveSandboxToConfig(env, result, force);
|
|
186
|
+
} catch (err) {
|
|
187
|
+
logErrorInstance(err);
|
|
188
|
+
throw err;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
sandboxConfigName,
|
|
193
|
+
result,
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
module.exports = {
|
|
198
|
+
buildSandbox,
|
|
199
|
+
};
|