@hubspot/cli 4.1.8-beta.4 → 4.1.8-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/project/dev.js +65 -58
- package/commands/project/upload.js +28 -0
- package/lang/en.lyaml +23 -24
- package/lib/DevServerManager.js +21 -2
- package/lib/LocalDevManager.js +106 -43
- package/lib/SpinniesManager.js +14 -5
- package/lib/projects.js +12 -18
- package/lib/prompts/projectDevTargetAccountPrompt.js +45 -71
- package/lib/sandboxes.js +7 -4
- package/package.json +2 -2
package/commands/project/dev.js
CHANGED
|
@@ -29,6 +29,7 @@ const {
|
|
|
29
29
|
LocalDevManager,
|
|
30
30
|
UPLOAD_PERMISSIONS,
|
|
31
31
|
} = require('../../lib/LocalDevManager');
|
|
32
|
+
const { isSandbox } = require('../../lib/sandboxes');
|
|
32
33
|
const { getAccountConfig, getEnv } = require('@hubspot/cli-lib');
|
|
33
34
|
const { sandboxNamePrompt } = require('../../lib/prompts/sandboxesPrompt');
|
|
34
35
|
const {
|
|
@@ -37,12 +38,18 @@ const {
|
|
|
37
38
|
getAvailableSyncTypes,
|
|
38
39
|
} = require('../../lib/sandboxes');
|
|
39
40
|
const { getValidEnv } = require('@hubspot/cli-lib/lib/environment');
|
|
40
|
-
const {
|
|
41
|
+
const { ERROR_TYPES } = require('@hubspot/cli-lib/lib/constants');
|
|
42
|
+
const {
|
|
43
|
+
logErrorInstance,
|
|
44
|
+
logApiErrorInstance,
|
|
45
|
+
ApiErrorContext,
|
|
46
|
+
} = require('@hubspot/cli-lib/errorHandlers');
|
|
41
47
|
const { buildSandbox } = require('../../lib/sandbox-create');
|
|
42
48
|
const { syncSandbox } = require('../../lib/sandbox-sync');
|
|
43
49
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
44
50
|
const {
|
|
45
51
|
isMissingScopeError,
|
|
52
|
+
isSpecifiedError,
|
|
46
53
|
} = require('@hubspot/cli-lib/errorHandlers/apiErrors');
|
|
47
54
|
|
|
48
55
|
const i18nKey = 'cli.commands.project.subcommands.dev';
|
|
@@ -60,13 +67,7 @@ exports.handler = async options => {
|
|
|
60
67
|
|
|
61
68
|
const { projectConfig, projectDir } = await getProjectConfig();
|
|
62
69
|
|
|
63
|
-
logger.log(i18n(`${i18nKey}.logs.
|
|
64
|
-
uiLine();
|
|
65
|
-
logger.log(i18n(`${i18nKey}.logs.introBody1`));
|
|
66
|
-
logger.log();
|
|
67
|
-
logger.log(i18n(`${i18nKey}.logs.introBody2`));
|
|
68
|
-
uiLine();
|
|
69
|
-
logger.log();
|
|
70
|
+
logger.log(i18n(`${i18nKey}.logs.betaMessage`));
|
|
70
71
|
|
|
71
72
|
if (!projectConfig) {
|
|
72
73
|
logger.error(i18n(`${i18nKey}.errors.noProjectConfig`));
|
|
@@ -74,47 +75,32 @@ exports.handler = async options => {
|
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
const accounts = getConfigAccounts();
|
|
77
|
-
let targetAccountId = options.accountId;
|
|
78
|
+
let targetAccountId = options.account ? accountId : null;
|
|
78
79
|
let createNewSandbox = false;
|
|
79
|
-
|
|
80
|
+
const defaultAccountIsSandbox = isSandbox(accountConfig);
|
|
81
|
+
|
|
82
|
+
if (!targetAccountId && defaultAccountIsSandbox) {
|
|
83
|
+
targetAccountId = accountId;
|
|
84
|
+
}
|
|
80
85
|
|
|
81
86
|
if (!targetAccountId) {
|
|
87
|
+
//logger.log(i18n(`${i18nKey}.logs.learnMoreLink`));
|
|
88
|
+
logger.log();
|
|
89
|
+
uiLine();
|
|
90
|
+
logger.warn(i18n(`${i18nKey}.logs.nonSandboxWarning`));
|
|
91
|
+
uiLine();
|
|
92
|
+
logger.log();
|
|
93
|
+
|
|
82
94
|
const {
|
|
83
95
|
targetAccountId: promptTargetAccountId,
|
|
84
|
-
chooseNonSandbox: promptChooseNonSandbox,
|
|
85
96
|
createNewSandbox: promptCreateNewSandbox,
|
|
86
|
-
} = await selectTargetAccountPrompt(accounts, accountConfig
|
|
97
|
+
} = await selectTargetAccountPrompt(accounts, accountConfig);
|
|
87
98
|
|
|
88
99
|
targetAccountId = promptTargetAccountId;
|
|
89
|
-
chooseNonSandbox = promptChooseNonSandbox;
|
|
90
100
|
createNewSandbox = promptCreateNewSandbox;
|
|
91
101
|
}
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// Show a warning if the user chooses a non-sandbox account (false)
|
|
96
|
-
let shouldTargetNonSandboxAccount;
|
|
97
|
-
if (chooseNonSandbox) {
|
|
98
|
-
uiLine();
|
|
99
|
-
logger.warn(i18n(`${i18nKey}.logs.prodAccountWarning`));
|
|
100
|
-
uiLine();
|
|
101
|
-
logger.log();
|
|
102
|
-
|
|
103
|
-
shouldTargetNonSandboxAccount = await confirmPrompt(
|
|
104
|
-
i18n(`${i18nKey}.prompt.targetNonSandbox`)
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
if (shouldTargetNonSandboxAccount) {
|
|
108
|
-
const {
|
|
109
|
-
targetAccountId: promptNonSandboxTargetAccountId,
|
|
110
|
-
} = await selectTargetAccountPrompt(accounts, accountConfig, true);
|
|
111
|
-
|
|
112
|
-
targetAccountId = promptNonSandboxTargetAccountId;
|
|
113
|
-
logger.log();
|
|
114
|
-
} else {
|
|
115
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
116
|
-
}
|
|
117
|
-
} else if (createNewSandbox) {
|
|
103
|
+
if (createNewSandbox) {
|
|
118
104
|
try {
|
|
119
105
|
await validateSandboxUsageLimits(accountConfig, DEVELOPER_SANDBOX, env);
|
|
120
106
|
} catch (err) {
|
|
@@ -179,7 +165,8 @@ exports.handler = async options => {
|
|
|
179
165
|
}
|
|
180
166
|
);
|
|
181
167
|
|
|
182
|
-
const isNonSandboxAccount =
|
|
168
|
+
const isNonSandboxAccount =
|
|
169
|
+
!defaultAccountIsSandbox && targetAccountId === accountId;
|
|
183
170
|
|
|
184
171
|
let uploadPermission = isNonSandboxAccount
|
|
185
172
|
? UPLOAD_PERMISSIONS.manual
|
|
@@ -198,21 +185,26 @@ exports.handler = async options => {
|
|
|
198
185
|
const spinnies = SpinniesManager.init();
|
|
199
186
|
|
|
200
187
|
if (!projectExists) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
188
|
+
// Create the project without prompting if this is a newly created sandbox
|
|
189
|
+
let shouldCreateProject = createNewSandbox;
|
|
190
|
+
|
|
191
|
+
if (!shouldCreateProject) {
|
|
192
|
+
uiLine();
|
|
193
|
+
logger.warn(
|
|
194
|
+
i18n(`${i18nKey}.logs.projectMustExistExplanation`, {
|
|
195
|
+
accountIdentifier: uiAccountDescription(targetAccountId),
|
|
196
|
+
projectName: projectConfig.name,
|
|
197
|
+
})
|
|
198
|
+
);
|
|
199
|
+
uiLine();
|
|
209
200
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
201
|
+
shouldCreateProject = await confirmPrompt(
|
|
202
|
+
i18n(`${i18nKey}.prompt.createProject`, {
|
|
203
|
+
accountIdentifier: uiAccountDescription(targetAccountId),
|
|
204
|
+
projectName: projectConfig.name,
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
}
|
|
216
208
|
|
|
217
209
|
if (shouldCreateProject) {
|
|
218
210
|
try {
|
|
@@ -228,9 +220,10 @@ exports.handler = async options => {
|
|
|
228
220
|
accountIdentifier: uiAccountDescription(targetAccountId),
|
|
229
221
|
projectName: projectConfig.name,
|
|
230
222
|
}),
|
|
223
|
+
succeedColor: 'white',
|
|
231
224
|
});
|
|
232
225
|
} catch (err) {
|
|
233
|
-
logger.log(i18n(`${i18nKey}.
|
|
226
|
+
logger.log(i18n(`${i18nKey}.status.failedToCreateProject`));
|
|
234
227
|
process.exit(EXIT_CODES.ERROR);
|
|
235
228
|
}
|
|
236
229
|
} else {
|
|
@@ -260,10 +253,24 @@ exports.handler = async options => {
|
|
|
260
253
|
);
|
|
261
254
|
}
|
|
262
255
|
|
|
263
|
-
if (result &&
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
256
|
+
if (result && result.error) {
|
|
257
|
+
if (
|
|
258
|
+
isSpecifiedError(result.error, {
|
|
259
|
+
subCategory: ERROR_TYPES.PROJECT_LOCKED,
|
|
260
|
+
})
|
|
261
|
+
) {
|
|
262
|
+
logger.log();
|
|
263
|
+
logger.error(i18n(`${i18nKey}.errors.projectLockedError`));
|
|
264
|
+
logger.log();
|
|
265
|
+
} else {
|
|
266
|
+
logApiErrorInstance(
|
|
267
|
+
result.error,
|
|
268
|
+
new ApiErrorContext({
|
|
269
|
+
accountId,
|
|
270
|
+
projectName: projectConfig.name,
|
|
271
|
+
})
|
|
272
|
+
);
|
|
273
|
+
}
|
|
267
274
|
process.exit(EXIT_CODES.ERROR);
|
|
268
275
|
} else {
|
|
269
276
|
spinnies.remove('devModeSetup');
|
|
@@ -19,6 +19,14 @@ const {
|
|
|
19
19
|
} = require('../../lib/projects');
|
|
20
20
|
const { i18n } = require('../../lib/lang');
|
|
21
21
|
const { getAccountConfig } = require('@hubspot/cli-lib');
|
|
22
|
+
const { ERROR_TYPES } = require('@hubspot/cli-lib/lib/constants');
|
|
23
|
+
const {
|
|
24
|
+
isSpecifiedError,
|
|
25
|
+
} = require('@hubspot/cli-lib/errorHandlers/apiErrors');
|
|
26
|
+
const {
|
|
27
|
+
logApiErrorInstance,
|
|
28
|
+
ApiErrorContext,
|
|
29
|
+
} = require('@hubspot/cli-lib/errorHandlers');
|
|
22
30
|
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
23
31
|
|
|
24
32
|
const i18nKey = 'cli.commands.project.subcommands.upload';
|
|
@@ -50,6 +58,26 @@ exports.handler = async options => {
|
|
|
50
58
|
message
|
|
51
59
|
);
|
|
52
60
|
|
|
61
|
+
if (result.error) {
|
|
62
|
+
if (
|
|
63
|
+
isSpecifiedError(result.error, {
|
|
64
|
+
subCategory: ERROR_TYPES.PROJECT_LOCKED,
|
|
65
|
+
})
|
|
66
|
+
) {
|
|
67
|
+
logger.log();
|
|
68
|
+
logger.error(i18n(`${i18nKey}.errors.projectLockedError`));
|
|
69
|
+
logger.log();
|
|
70
|
+
} else {
|
|
71
|
+
logApiErrorInstance(
|
|
72
|
+
result.error,
|
|
73
|
+
new ApiErrorContext({
|
|
74
|
+
accountId,
|
|
75
|
+
projectName: projectConfig.name,
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
process.exit(EXIT_CODES.ERROR);
|
|
80
|
+
}
|
|
53
81
|
if (result.buildSucceeded && !result.autodeployEnabled) {
|
|
54
82
|
uiLine();
|
|
55
83
|
logger.log(
|
package/lang/en.lyaml
CHANGED
|
@@ -448,11 +448,10 @@ en:
|
|
|
448
448
|
dev:
|
|
449
449
|
describe: "Start local dev for the current project"
|
|
450
450
|
logs:
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
451
|
+
betaMessage: "{{#yellow}}{{#bold}}[beta]{{/bold}}{{/yellow}} HubSpot projects local development"
|
|
452
|
+
learnMoreLink: "Learn more about the projects local dev server"
|
|
453
|
+
nonSandboxWarning: "Testing in a sandbox is strongly recommended. To switch the target account, select an option below or run {{#bold}}`hs accounts use`{{/bold}} before running the command again."
|
|
454
454
|
placeholderAccountSelection: "Using default account as target account (for now)"
|
|
455
|
-
prodAccountWarning: "Targeting a non-sandbox account is risky because this command will upload changes to the target account. We recommend picking a sandbox account for any work that has not been finalized or is undergoing testing prior to deploying to production."
|
|
456
455
|
projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
|
|
457
456
|
choseNotToCreateProject: "Exiting because this command requires the project to exist in the target account."
|
|
458
457
|
initialUploadMessage: "HubSpot Local Dev Server Startup"
|
|
@@ -461,7 +460,6 @@ en:
|
|
|
461
460
|
createdProject: "Created project {{ projectName }} in {{ accountIdentifier }}"
|
|
462
461
|
failedToCreateProject: "Failed to create project in the target account."
|
|
463
462
|
startupMessage: "Starting local dev server for {{#bold}}{{ projectName }}{{/bold}} ..."
|
|
464
|
-
startupFailed: "Failed to initialize local dev server"
|
|
465
463
|
prompt:
|
|
466
464
|
createProject: "Create new project {{ projectName}} in {{#bold}}[{{ accountIdentifier }}]{{/bold}}?"
|
|
467
465
|
targetNonSandbox: "Continue testing in a non-sandbox account?"
|
|
@@ -470,6 +468,7 @@ en:
|
|
|
470
468
|
describe: "The port that the running server will listen on"
|
|
471
469
|
errors:
|
|
472
470
|
noProjectConfig: "No project detected. Please run this command again from a project directory."
|
|
471
|
+
projectLockedError: "Your project is locked. This may mean that another user is running the {{#bold}}`hs project dev`{{/bold}} command for this project. If this is you, unlock the project in Projects UI."
|
|
473
472
|
examples:
|
|
474
473
|
default: "Start local dev for the current project"
|
|
475
474
|
create:
|
|
@@ -561,6 +560,8 @@ en:
|
|
|
561
560
|
buildSucceeded: "Build #{{ buildId }} succeeded\n"
|
|
562
561
|
readyToGoLive: "🚀 Ready to take your project live?"
|
|
563
562
|
runCommand: "Run `{{ command }}`"
|
|
563
|
+
errors:
|
|
564
|
+
projectLockedError: "Your project is locked. This may mean that another user is running the {{#bold}}`hs project dev`{{/bold}} command for this project. If this is you, unlock the project in Projects UI."
|
|
564
565
|
options:
|
|
565
566
|
forceCreate:
|
|
566
567
|
describe: "Automatically create project if it does not exist"
|
|
@@ -569,15 +570,6 @@ en:
|
|
|
569
570
|
positionals:
|
|
570
571
|
path:
|
|
571
572
|
describe: "Path to a project folder"
|
|
572
|
-
unlock:
|
|
573
|
-
describe: "Unlock a locked project"
|
|
574
|
-
examples:
|
|
575
|
-
default: "Unlock a locked project in the myProjectsFolder folder"
|
|
576
|
-
logs:
|
|
577
|
-
unlockSucceeded: "Successfully unlocked the project"
|
|
578
|
-
positionals:
|
|
579
|
-
path:
|
|
580
|
-
describe: "Path to a project folder"
|
|
581
573
|
watch:
|
|
582
574
|
describe: "Watch your local project for changes and automatically upload changed files to a new build in HubSpot"
|
|
583
575
|
examples:
|
|
@@ -844,6 +836,8 @@ en:
|
|
|
844
836
|
options:
|
|
845
837
|
describe: "Options to pass to javascript fields files"
|
|
846
838
|
lib:
|
|
839
|
+
DevServerManager:
|
|
840
|
+
portConflict: "The port {{ port }} is already in use."
|
|
847
841
|
LocalDevManager:
|
|
848
842
|
exitingStart: "Stopping local dev server ..."
|
|
849
843
|
exitingSucceed: "Successfully exited"
|
|
@@ -851,6 +845,8 @@ en:
|
|
|
851
845
|
previousStagingBuildCancelled: "Failed to create a staging build because the project was already locked. It is now unlocked. Run the command again."
|
|
852
846
|
cancelledFromUI: "The dev process has been cancelled from the UI. Any changes made since cancelling have not been uploaded. To resume dev mode, rerun {{#yellow}}`hs project dev`{{/yellow}}."
|
|
853
847
|
header:
|
|
848
|
+
betaMessage: "{{#yellow}}{{#bold}}[beta]{{/bold}}{{/yellow}} HubSpot projects local development"
|
|
849
|
+
learnMoreLink: "Learn more about the projects local dev server"
|
|
854
850
|
running: "Running {{ projectName}} locally on {{ accountIdentifier }}, waiting for project file changes ..."
|
|
855
851
|
quitHelper: "Press {{#bold}}'q'{{/bold}} to stop the local dev server"
|
|
856
852
|
viewInHubSpotLink: "View in HubSpot"
|
|
@@ -863,20 +859,25 @@ en:
|
|
|
863
859
|
manualUpload: "{{#bold}}Status:{{/bold}} {{#green}}Manually uploading pending changes{{/green}}"
|
|
864
860
|
upload:
|
|
865
861
|
noUploadsAllowed: "The change to {{ filePath }} requires an upload, but the CLI cannot upload to a project that is using a github integration."
|
|
866
|
-
manualUploadSkipped: "
|
|
867
|
-
|
|
862
|
+
manualUploadSkipped: "Manually upload and deploy project: {{#green}}(n){{/green}}"
|
|
863
|
+
manualUploadConfirmed: "Manually upload and deploy project: {{#green}}(Y){{/green}}"
|
|
864
|
+
manualUploadRequired: "Project file changes require a manual upload and deploy ..."
|
|
868
865
|
manualUploadExplanation1: "{{#yellow}}> Dev server is running on a {{#bold}}non-sandbox account{{/bold}}.{{/yellow}}"
|
|
869
866
|
manualUploadExplanation2: "{{#yellow}}> Uploading changes may overwrite production data.{{/yellow}}"
|
|
870
867
|
manualUploadPrompt: "? Manually upload and deploy project? {{#green}}Y/n{{/green}}"
|
|
871
|
-
|
|
868
|
+
uploadingAddChange: "[INFO] Uploading {{ filePath }}"
|
|
869
|
+
uploadedAddChange: "[INFO] Uploaded {{ filePath }}"
|
|
870
|
+
uploadingRemoveChange: "[INFO] Removing {{ filePath }}"
|
|
871
|
+
uploadedRemoveChange: "[INFO] Removed {{ filePath }}"
|
|
872
872
|
uploadingChanges: "{{#bold}}Building and deploying recent changes on {{ accountIdentifier }}{{/bold}}"
|
|
873
|
+
uploadedChangesSucceeded: "{{#bold}}Built and deployed recent changes on {{ accountIdentifier }}{{/bold}}"
|
|
874
|
+
uploadedChangesFailed: "{{#bold}}Failed to build and deploy recent changes on {{ accountIdentifier }}{{/bold}}"
|
|
873
875
|
projects:
|
|
874
876
|
uploadProjectFiles:
|
|
875
877
|
add: "Uploading {{#bold}}{{ projectName }}{{/bold}} project files to {{ accountIdentifier }}"
|
|
876
878
|
fail: "Failed to upload {{#bold}}{{ projectName }}{{/bold}} project files to {{ accountIdentifier }}"
|
|
877
879
|
succeed: "Uploaded {{#bold}}{{ projectName }}{{/bold}} project files to {{ accountIdentifier }}"
|
|
878
880
|
buildCreated: "Project \"{{ projectName }}\" uploaded and build #{{ buildId }} created"
|
|
879
|
-
projectLockedError: "\nYour project may be locked by an active `{{#yellow}}hs project watch{{/yellow}}`. Try stopping the `{{#yellow}}watch{{/yellow}}` process before uploading.\nIf that doesn't work, you may need to start and stop a new `{{#yellow}}watch{{/yellow}}` process to unlock the project."
|
|
880
881
|
handleProjectUpload:
|
|
881
882
|
emptySource: "Source directory \"{{ srcDir }}\" is empty. Add files to your project and rerun `{{#yellow}}hs project upload{{/yellow}}` to upload them to HubSpot."
|
|
882
883
|
compressed: "Project files compressed: {{ byteCount }} bytes"
|
|
@@ -958,12 +959,10 @@ en:
|
|
|
958
959
|
describe: "Use environment variable config"
|
|
959
960
|
prompts:
|
|
960
961
|
projectDevTargetAccountPrompt:
|
|
961
|
-
createNewSandboxOption: "
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
defaultAccountNotProd: "Option unavailable because your default account must be a production account."
|
|
966
|
-
sandboxLimit: "Option unavailable because you’ve reached the limit of {{ limit }} development sandboxes."
|
|
962
|
+
createNewSandboxOption: "<Test on a new dev sandbox>"
|
|
963
|
+
chooseDefaultAccountOption: "<{{#bold}}!{{/bold}} Test on this production account {{#bold}}!{{/bold}}>"
|
|
964
|
+
promptMessage: "[--account] Choose a sandbox under {{ accountIdentifier }} to test with:"
|
|
965
|
+
sandboxLimit: "You’ve reached the limit of {{ limit }} development sandboxes"
|
|
967
966
|
projectLogsPrompt:
|
|
968
967
|
projectName:
|
|
969
968
|
message: "[--project] Enter the project name:"
|
package/lib/DevServerManager.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
const express = require('express');
|
|
2
2
|
const bodyParser = require('body-parser');
|
|
3
3
|
const cors = require('cors');
|
|
4
|
+
const { i18n } = require('./lang');
|
|
4
5
|
const { getProjectDetailUrl } = require('./projects');
|
|
6
|
+
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
7
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
8
|
+
|
|
9
|
+
const i18nKey = 'cli.lib.DevServerManager';
|
|
5
10
|
|
|
6
11
|
const DEFAULT_PORT = 8080;
|
|
7
12
|
|
|
@@ -41,6 +46,10 @@ class DevServerManager {
|
|
|
41
46
|
app.get('/hs/project', (req, res) => {
|
|
42
47
|
res.redirect(getProjectDetailUrl(projectConfig.name, accountId));
|
|
43
48
|
});
|
|
49
|
+
app.get('/hs/learnMore', (req, res) => {
|
|
50
|
+
//TODO link to docs
|
|
51
|
+
res.redirect(getProjectDetailUrl(projectConfig.name, accountId));
|
|
52
|
+
});
|
|
44
53
|
|
|
45
54
|
// Initialize component servers
|
|
46
55
|
await this.iterateDevServers(async (serverInterface, serverKey) => {
|
|
@@ -51,9 +60,19 @@ class DevServerManager {
|
|
|
51
60
|
});
|
|
52
61
|
|
|
53
62
|
// Start server
|
|
54
|
-
this.server = app.listen(port || DEFAULT_PORT)
|
|
63
|
+
this.server = app.listen(port || DEFAULT_PORT).on('error', err => {
|
|
64
|
+
if (err.code === 'EADDRINUSE') {
|
|
65
|
+
logger.error(
|
|
66
|
+
i18n(`${i18nKey}.portConflict`, { port: port || DEFAULT_PORT })
|
|
67
|
+
);
|
|
68
|
+
logger.log();
|
|
69
|
+
process.exit(EXIT_CODES.ERROR);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
55
72
|
|
|
56
|
-
return
|
|
73
|
+
return this.server.address()
|
|
74
|
+
? `http://localhost:${this.server.address().port}`
|
|
75
|
+
: null;
|
|
57
76
|
}
|
|
58
77
|
|
|
59
78
|
async notify() {
|
package/lib/LocalDevManager.js
CHANGED
|
@@ -29,7 +29,7 @@ const { uiAccountDescription, uiLink } = require('./ui');
|
|
|
29
29
|
|
|
30
30
|
const i18nKey = 'cli.lib.LocalDevManager';
|
|
31
31
|
|
|
32
|
-
const BUILD_DEBOUNCE_TIME =
|
|
32
|
+
const BUILD_DEBOUNCE_TIME = 3500;
|
|
33
33
|
|
|
34
34
|
const WATCH_EVENTS = {
|
|
35
35
|
add: 'add',
|
|
@@ -146,6 +146,26 @@ class LocalDevManager {
|
|
|
146
146
|
|
|
147
147
|
logConsoleHeader() {
|
|
148
148
|
this.spinnies.removeAll();
|
|
149
|
+
this.spinnies.add('betaMessage', {
|
|
150
|
+
text: i18n(`${i18nKey}.header.betaMessage`),
|
|
151
|
+
category: 'header',
|
|
152
|
+
status: 'non-spinnable',
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// this.spinnies.add('learnMoreLink', {
|
|
156
|
+
// text: uiLink(
|
|
157
|
+
// i18n(`${i18nKey}.header.learnMoreLink`),
|
|
158
|
+
// this.generateLocalURL(`/hs/learnMore`),
|
|
159
|
+
// { inSpinnies: true }
|
|
160
|
+
// ),
|
|
161
|
+
// category: 'header',
|
|
162
|
+
// status: 'non-spinnable',
|
|
163
|
+
// });
|
|
164
|
+
this.spinnies.add('spacer-1', {
|
|
165
|
+
text: ' ',
|
|
166
|
+
status: 'non-spinnable',
|
|
167
|
+
category: 'header',
|
|
168
|
+
});
|
|
149
169
|
this.spinnies.add('devModeRunning', {
|
|
150
170
|
text: i18n(`${i18nKey}.header.running`, {
|
|
151
171
|
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
@@ -164,15 +184,13 @@ class LocalDevManager {
|
|
|
164
184
|
text: uiLink(
|
|
165
185
|
i18n(`${i18nKey}.header.viewInHubSpotLink`),
|
|
166
186
|
this.generateLocalURL(`/hs/project`),
|
|
167
|
-
{
|
|
168
|
-
inSpinnies: true,
|
|
169
|
-
}
|
|
187
|
+
{ inSpinnies: true }
|
|
170
188
|
),
|
|
171
189
|
status: 'non-spinnable',
|
|
172
190
|
indent: 1,
|
|
173
191
|
category: 'header',
|
|
174
192
|
});
|
|
175
|
-
this.spinnies.add('spacer-
|
|
193
|
+
this.spinnies.add('spacer-2', {
|
|
176
194
|
text: ' ',
|
|
177
195
|
status: 'non-spinnable',
|
|
178
196
|
category: 'header',
|
|
@@ -199,24 +217,29 @@ class LocalDevManager {
|
|
|
199
217
|
handleKeypress(async key => {
|
|
200
218
|
if ((key.ctrl && key.name === 'c') || key.name === 'q') {
|
|
201
219
|
this.stop();
|
|
202
|
-
} else if (
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
220
|
+
} else if (
|
|
221
|
+
(key.name === 'y' || key.name === 'n') &&
|
|
222
|
+
this.uploadPermission === UPLOAD_PERMISSIONS.manual &&
|
|
223
|
+
this.hasAnyUnsupportedStandbyChanges()
|
|
224
|
+
) {
|
|
225
|
+
this.spinnies.remove('manualUploadRequired');
|
|
226
|
+
this.spinnies.remove('manualUploadExplanation1');
|
|
227
|
+
this.spinnies.remove('manualUploadExplanation2');
|
|
228
|
+
this.spinnies.remove('manualUploadPrompt');
|
|
229
|
+
|
|
230
|
+
if (key.name === 'y') {
|
|
231
|
+
this.spinnies.add(null, {
|
|
232
|
+
text: i18n(`${i18nKey}.upload.manualUploadConfirmed`),
|
|
233
|
+
status: 'succeed',
|
|
234
|
+
succeedColor: 'white',
|
|
235
|
+
noIndent: true,
|
|
236
|
+
});
|
|
208
237
|
this.updateDevModeStatus('manualUpload');
|
|
209
238
|
await this.createNewStagingBuild();
|
|
210
239
|
await this.flushStandbyChanges();
|
|
211
240
|
await this.queueBuild();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (
|
|
215
|
-
this.uploadPermission === UPLOAD_PERMISSIONS.manual &&
|
|
216
|
-
this.hasAnyUnsupportedStandbyChanges()
|
|
217
|
-
) {
|
|
218
|
-
this.clearConsoleContent();
|
|
219
|
-
this.spinnies.add('manualUploadSkipped', {
|
|
241
|
+
} else if (key.name === 'n') {
|
|
242
|
+
this.spinnies.add(null, {
|
|
220
243
|
text: i18n(`${i18nKey}.upload.manualUploadSkipped`),
|
|
221
244
|
status: 'fail',
|
|
222
245
|
failColor: 'white',
|
|
@@ -240,13 +263,6 @@ class LocalDevManager {
|
|
|
240
263
|
}
|
|
241
264
|
|
|
242
265
|
async pauseUploadQueue() {
|
|
243
|
-
this.spinnies.add('uploading', {
|
|
244
|
-
text: i18n(`${i18nKey}.upload.uploadingChanges`, {
|
|
245
|
-
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
246
|
-
}),
|
|
247
|
-
noIndent: true,
|
|
248
|
-
});
|
|
249
|
-
|
|
250
266
|
this.uploadQueue.pause();
|
|
251
267
|
await this.uploadQueue.onIdle();
|
|
252
268
|
}
|
|
@@ -314,13 +330,7 @@ class LocalDevManager {
|
|
|
314
330
|
}
|
|
315
331
|
|
|
316
332
|
if (this.uploadQueue.isPaused) {
|
|
317
|
-
|
|
318
|
-
!this.standbyChanges.find(
|
|
319
|
-
changeInfo => changeInfo.filePath === filePath
|
|
320
|
-
)
|
|
321
|
-
) {
|
|
322
|
-
this.addChangeToStandbyQueue({ ...changeInfo, supported: false });
|
|
323
|
-
}
|
|
333
|
+
this.addChangeToStandbyQueue({ ...changeInfo, supported: false });
|
|
324
334
|
} else {
|
|
325
335
|
await this.flushStandbyChanges();
|
|
326
336
|
|
|
@@ -337,7 +347,6 @@ class LocalDevManager {
|
|
|
337
347
|
handlePreventedUpload(changeInfo) {
|
|
338
348
|
const { remotePath } = changeInfo;
|
|
339
349
|
|
|
340
|
-
this.clearConsoleContent();
|
|
341
350
|
if (this.uploadPermission === UPLOAD_PERMISSIONS.never) {
|
|
342
351
|
this.updateDevModeStatus('noUploadsAllowed');
|
|
343
352
|
|
|
@@ -391,35 +400,63 @@ class LocalDevManager {
|
|
|
391
400
|
logger.debug(`File ignored: ${filePath}`);
|
|
392
401
|
return;
|
|
393
402
|
}
|
|
394
|
-
|
|
403
|
+
|
|
404
|
+
const existingIndex = this.standbyChanges.findIndex(
|
|
405
|
+
standyChangeInfo => standyChangeInfo.filePath === filePath
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
if (existingIndex > -1) {
|
|
409
|
+
// Make sure the most recent event to this file is the one that gets acted on
|
|
410
|
+
this.standbyChanges[existingIndex].event = event;
|
|
411
|
+
} else {
|
|
412
|
+
this.standbyChanges.push(changeInfo);
|
|
413
|
+
}
|
|
395
414
|
}
|
|
396
415
|
|
|
397
416
|
async sendChanges(changeInfo) {
|
|
398
417
|
const { event, filePath, remotePath } = changeInfo;
|
|
399
418
|
|
|
400
|
-
this.spinnies.add(filePath, {
|
|
401
|
-
text: i18n(`${i18nKey}.upload.uploadingChange`, {
|
|
402
|
-
filePath: remotePath,
|
|
403
|
-
}),
|
|
404
|
-
status: 'non-spinnable',
|
|
405
|
-
});
|
|
406
419
|
try {
|
|
407
420
|
if (event === WATCH_EVENTS.add || event === WATCH_EVENTS.change) {
|
|
421
|
+
const spinniesKey = this.spinnies.add(null, {
|
|
422
|
+
text: i18n(`${i18nKey}.upload.uploadingAddChange`, {
|
|
423
|
+
filePath: remotePath,
|
|
424
|
+
}),
|
|
425
|
+
status: 'non-spinnable',
|
|
426
|
+
});
|
|
408
427
|
await uploadFileToBuild(
|
|
409
428
|
this.targetAccountId,
|
|
410
429
|
this.projectConfig.name,
|
|
411
430
|
filePath,
|
|
412
431
|
remotePath
|
|
413
432
|
);
|
|
433
|
+
this.spinnies.update(spinniesKey, {
|
|
434
|
+
text: i18n(`${i18nKey}.upload.uploadedAddChange`, {
|
|
435
|
+
filePath: remotePath,
|
|
436
|
+
}),
|
|
437
|
+
status: 'non-spinnable',
|
|
438
|
+
});
|
|
414
439
|
} else if (
|
|
415
440
|
event === WATCH_EVENTS.unlink ||
|
|
416
441
|
event === WATCH_EVENTS.unlinkDir
|
|
417
442
|
) {
|
|
443
|
+
const spinniesKey = this.spinnies.add(null, {
|
|
444
|
+
text: i18n(`${i18nKey}.upload.uploadingRemoveChange`, {
|
|
445
|
+
filePath: remotePath,
|
|
446
|
+
}),
|
|
447
|
+
status: 'non-spinnable',
|
|
448
|
+
});
|
|
418
449
|
await deleteFileFromBuild(
|
|
419
450
|
this.targetAccountId,
|
|
420
451
|
this.projectConfig.name,
|
|
421
452
|
remotePath
|
|
422
453
|
);
|
|
454
|
+
this.spinnies.update(spinniesKey, {
|
|
455
|
+
text: i18n(`${i18nKey}.upload.uploadedRemoveChange`, {
|
|
456
|
+
filePath: remotePath,
|
|
457
|
+
}),
|
|
458
|
+
status: 'non-spinnable',
|
|
459
|
+
});
|
|
423
460
|
}
|
|
424
461
|
} catch (err) {
|
|
425
462
|
logger.debug(err);
|
|
@@ -442,6 +479,13 @@ class LocalDevManager {
|
|
|
442
479
|
}
|
|
443
480
|
|
|
444
481
|
async queueBuild() {
|
|
482
|
+
const spinniesKey = this.spinnies.add(null, {
|
|
483
|
+
text: i18n(`${i18nKey}.upload.uploadingChanges`, {
|
|
484
|
+
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
485
|
+
}),
|
|
486
|
+
noIndent: true,
|
|
487
|
+
});
|
|
488
|
+
|
|
445
489
|
await this.pauseUploadQueue();
|
|
446
490
|
|
|
447
491
|
try {
|
|
@@ -467,7 +511,7 @@ class LocalDevManager {
|
|
|
467
511
|
return;
|
|
468
512
|
}
|
|
469
513
|
|
|
470
|
-
await pollProjectBuildAndDeploy(
|
|
514
|
+
const result = await pollProjectBuildAndDeploy(
|
|
471
515
|
this.targetAccountId,
|
|
472
516
|
this.projectConfig,
|
|
473
517
|
null,
|
|
@@ -475,12 +519,31 @@ class LocalDevManager {
|
|
|
475
519
|
true
|
|
476
520
|
);
|
|
477
521
|
|
|
522
|
+
if (result && result.succeeded) {
|
|
523
|
+
this.spinnies.succeed(spinniesKey, {
|
|
524
|
+
text: i18n(`${i18nKey}.upload.uploadedChangesSucceeded`, {
|
|
525
|
+
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
526
|
+
}),
|
|
527
|
+
succeedColor: 'white',
|
|
528
|
+
noIndent: true,
|
|
529
|
+
});
|
|
530
|
+
} else {
|
|
531
|
+
this.spinnies.fail(spinniesKey, {
|
|
532
|
+
text: i18n(`${i18nKey}.upload.uploadedChangesFailed`, {
|
|
533
|
+
accountIdentifier: uiAccountDescription(this.targetAccountId),
|
|
534
|
+
}),
|
|
535
|
+
failColor: 'white',
|
|
536
|
+
noIndent: true,
|
|
537
|
+
});
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
this.spinnies.removeAll({ targetCategory: 'projectPollStatus' });
|
|
541
|
+
|
|
478
542
|
if (this.uploadPermission === UPLOAD_PERMISSIONS.always) {
|
|
479
543
|
await this.createNewStagingBuild();
|
|
480
544
|
}
|
|
481
545
|
|
|
482
546
|
this.uploadQueue.start();
|
|
483
|
-
this.clearConsoleContent();
|
|
484
547
|
|
|
485
548
|
if (this.hasAnyUnsupportedStandbyChanges()) {
|
|
486
549
|
this.flushStandbyChanges();
|
package/lib/SpinniesManager.js
CHANGED
|
@@ -51,18 +51,23 @@ class SpinniesManager {
|
|
|
51
51
|
const { category, isParent, noIndent, ...rest } = options;
|
|
52
52
|
const originalIndent = rest.indent || 0;
|
|
53
53
|
|
|
54
|
+
// Support adding generic spinnies lines without specifying a key
|
|
55
|
+
const uniqueKey = key || `${Date.now()}`;
|
|
56
|
+
|
|
54
57
|
if (category) {
|
|
55
|
-
this.addKeyToCategory(
|
|
58
|
+
this.addKeyToCategory(uniqueKey, category);
|
|
56
59
|
}
|
|
57
60
|
|
|
58
|
-
this.spinnies.add(
|
|
61
|
+
this.spinnies.add(uniqueKey, {
|
|
59
62
|
...rest,
|
|
60
63
|
indent: this.parentKey && !noIndent ? originalIndent + 1 : originalIndent,
|
|
61
64
|
});
|
|
62
65
|
|
|
63
66
|
if (isParent) {
|
|
64
|
-
this.parentKey =
|
|
67
|
+
this.parentKey = uniqueKey;
|
|
65
68
|
}
|
|
69
|
+
|
|
70
|
+
return uniqueKey;
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
remove(key) {
|
|
@@ -79,10 +84,14 @@ class SpinniesManager {
|
|
|
79
84
|
* Removes all spinnies instances
|
|
80
85
|
* @param {string} preserveCategory - do not remove spinnies with a matching category
|
|
81
86
|
*/
|
|
82
|
-
removeAll({ preserveCategory = null } = {}) {
|
|
87
|
+
removeAll({ preserveCategory = null, targetCategory = null } = {}) {
|
|
83
88
|
if (this.spinnies) {
|
|
84
89
|
Object.keys(this.spinnies.spinners).forEach(key => {
|
|
85
|
-
if (
|
|
90
|
+
if (targetCategory) {
|
|
91
|
+
if (this.getCategoryForKey(key) === targetCategory) {
|
|
92
|
+
this.remove(key);
|
|
93
|
+
}
|
|
94
|
+
} else if (
|
|
86
95
|
!preserveCategory ||
|
|
87
96
|
this.getCategoryForKey(key) !== preserveCategory
|
|
88
97
|
) {
|
package/lib/projects.js
CHANGED
|
@@ -10,7 +10,6 @@ const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
|
10
10
|
const {
|
|
11
11
|
ENVIRONMENTS,
|
|
12
12
|
FEEDBACK_INTERVAL,
|
|
13
|
-
ERROR_TYPES,
|
|
14
13
|
POLLING_DELAY,
|
|
15
14
|
PROJECT_BUILD_TEXT,
|
|
16
15
|
PROJECT_DEPLOY_TEXT,
|
|
@@ -320,6 +319,7 @@ const uploadProjectFiles = async (
|
|
|
320
319
|
});
|
|
321
320
|
|
|
322
321
|
let buildId;
|
|
322
|
+
let error;
|
|
323
323
|
|
|
324
324
|
try {
|
|
325
325
|
const upload = await uploadProject(
|
|
@@ -352,20 +352,10 @@ const uploadProjectFiles = async (
|
|
|
352
352
|
}),
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
-
|
|
356
|
-
err,
|
|
357
|
-
new ApiErrorContext({
|
|
358
|
-
accountId,
|
|
359
|
-
projectName,
|
|
360
|
-
})
|
|
361
|
-
);
|
|
362
|
-
if (err.error.subCategory === ERROR_TYPES.PROJECT_LOCKED) {
|
|
363
|
-
logger.log(i18n(`${i18nKey}.uploadProjectFiles.projectLockedError`));
|
|
364
|
-
}
|
|
365
|
-
process.exit(EXIT_CODES.ERROR);
|
|
355
|
+
error = err;
|
|
366
356
|
}
|
|
367
357
|
|
|
368
|
-
return { buildId };
|
|
358
|
+
return { buildId, error };
|
|
369
359
|
};
|
|
370
360
|
|
|
371
361
|
const pollProjectBuildAndDeploy = async (
|
|
@@ -478,7 +468,7 @@ const handleProjectUpload = async (
|
|
|
478
468
|
|
|
479
469
|
const result = new Promise(resolve =>
|
|
480
470
|
output.on('close', async function() {
|
|
481
|
-
let
|
|
471
|
+
let uploadResult = {};
|
|
482
472
|
|
|
483
473
|
logger.debug(
|
|
484
474
|
i18n(`${i18nKey}.handleProjectUpload.compressed`, {
|
|
@@ -486,22 +476,24 @@ const handleProjectUpload = async (
|
|
|
486
476
|
})
|
|
487
477
|
);
|
|
488
478
|
|
|
489
|
-
const { buildId } = await uploadProjectFiles(
|
|
479
|
+
const { buildId, error } = await uploadProjectFiles(
|
|
490
480
|
accountId,
|
|
491
481
|
projectConfig.name,
|
|
492
482
|
tempFile.name,
|
|
493
483
|
uploadMessage
|
|
494
484
|
);
|
|
495
485
|
|
|
496
|
-
if (
|
|
497
|
-
|
|
486
|
+
if (error) {
|
|
487
|
+
uploadResult.error = error;
|
|
488
|
+
} else if (callbackFunc) {
|
|
489
|
+
uploadResult = await callbackFunc(
|
|
498
490
|
accountId,
|
|
499
491
|
projectConfig,
|
|
500
492
|
tempFile,
|
|
501
493
|
buildId
|
|
502
494
|
);
|
|
503
495
|
}
|
|
504
|
-
resolve(
|
|
496
|
+
resolve(uploadResult);
|
|
505
497
|
})
|
|
506
498
|
);
|
|
507
499
|
|
|
@@ -558,6 +550,7 @@ const makePollTaskStatusFunc = ({
|
|
|
558
550
|
succeedColor: 'white',
|
|
559
551
|
failColor: 'white',
|
|
560
552
|
failPrefix: chalk.bold('!'),
|
|
553
|
+
category: 'projectPollStatus',
|
|
561
554
|
});
|
|
562
555
|
|
|
563
556
|
const [
|
|
@@ -618,6 +611,7 @@ const makePollTaskStatusFunc = ({
|
|
|
618
611
|
indent,
|
|
619
612
|
succeedColor: 'white',
|
|
620
613
|
failColor: 'white',
|
|
614
|
+
category: 'projectPollStatus',
|
|
621
615
|
});
|
|
622
616
|
};
|
|
623
617
|
|
|
@@ -9,90 +9,64 @@ const { logger } = require('@hubspot/cli-lib/logger');
|
|
|
9
9
|
const i18nKey = 'cli.lib.prompts.projectDevTargetAccountPrompt';
|
|
10
10
|
|
|
11
11
|
const mapSandboxAccount = accountConfig => ({
|
|
12
|
-
name: getAccountName(accountConfig),
|
|
12
|
+
name: getAccountName(accountConfig, false),
|
|
13
13
|
value: {
|
|
14
14
|
targetAccountId: getAccountId(accountConfig.name),
|
|
15
|
-
chooseNonSandbox: false,
|
|
16
15
|
createNewSandbox: false,
|
|
17
16
|
},
|
|
18
17
|
});
|
|
19
18
|
|
|
20
|
-
const selectTargetAccountPrompt = async (
|
|
21
|
-
|
|
22
|
-
defaultAccountConfig
|
|
23
|
-
nonSandbox = false
|
|
24
|
-
) => {
|
|
25
|
-
let choices;
|
|
19
|
+
const selectTargetAccountPrompt = async (accounts, defaultAccountConfig) => {
|
|
20
|
+
let sandboxUsage = {};
|
|
21
|
+
const defaultAccountId = getAccountId(defaultAccountConfig.name);
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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,
|
|
23
|
+
try {
|
|
24
|
+
sandboxUsage = await getSandboxUsageLimits(defaultAccountId);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
logger.debug('Unable to fetch sandbox usage limits: ', err);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const sandboxAccounts = accounts.reverse().filter(isSandbox);
|
|
30
|
+
let disabledMessage = false;
|
|
31
|
+
|
|
32
|
+
if (sandboxUsage['DEVELOPER'] && sandboxUsage['DEVELOPER'].available === 0) {
|
|
33
|
+
disabledMessage = i18n(`${i18nKey}.sandboxLimit`, {
|
|
34
|
+
limit: sandboxUsage['DEVELOPER'].limit,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Order choices by Developer Sandbox -> Standard Sandbox
|
|
39
|
+
const choices = [
|
|
40
|
+
...sandboxAccounts
|
|
41
|
+
.filter(a => a.sandboxAccountType === 'DEVELOPER')
|
|
42
|
+
.map(mapSandboxAccount),
|
|
43
|
+
...sandboxAccounts
|
|
44
|
+
.filter(a => a.sandboxAccountType === 'STANDARD')
|
|
45
|
+
.map(mapSandboxAccount),
|
|
46
|
+
{
|
|
47
|
+
name: i18n(`${i18nKey}.createNewSandboxOption`),
|
|
48
|
+
value: {
|
|
49
|
+
targetAccountId: null,
|
|
50
|
+
createNewSandbox: true,
|
|
72
51
|
},
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
name: i18n(`${i18nKey}.chooseNonSandboxOption`),
|
|
81
|
-
value: {
|
|
82
|
-
targetAccountId: null,
|
|
83
|
-
chooseNonSandbox: true,
|
|
84
|
-
createNewSandbox: false,
|
|
85
|
-
},
|
|
52
|
+
disabled: disabledMessage,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: i18n(`${i18nKey}.chooseDefaultAccountOption`),
|
|
56
|
+
value: {
|
|
57
|
+
targetAccountId: defaultAccountId,
|
|
58
|
+
createNewSandbox: false,
|
|
86
59
|
},
|
|
87
|
-
|
|
88
|
-
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
|
|
89
63
|
const { targetAccountInfo } = await promptUser([
|
|
90
64
|
{
|
|
91
65
|
name: 'targetAccountInfo',
|
|
92
66
|
type: 'list',
|
|
93
|
-
message:
|
|
94
|
-
|
|
95
|
-
|
|
67
|
+
message: i18n(`${i18nKey}.promptMessage`, {
|
|
68
|
+
accountIdentifier: uiAccountDescription(defaultAccountId),
|
|
69
|
+
}),
|
|
96
70
|
choices,
|
|
97
71
|
},
|
|
98
72
|
]);
|
package/lib/sandboxes.js
CHANGED
|
@@ -55,13 +55,16 @@ const getSandboxTypeAsString = type =>
|
|
|
55
55
|
const isSandbox = config =>
|
|
56
56
|
config.sandboxAccountType && config.sandboxAccountType !== null;
|
|
57
57
|
|
|
58
|
-
function getAccountName(config) {
|
|
58
|
+
function getAccountName(config, bold = true) {
|
|
59
59
|
const sandboxName = `[${getSandboxTypeAsString(
|
|
60
60
|
config.sandboxAccountType
|
|
61
61
|
)} sandbox] `;
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
|
|
63
|
+
const message = `${config.name} ${isSandbox(config) ? sandboxName : ''}(${
|
|
64
|
+
config.portalId
|
|
65
|
+
})`;
|
|
66
|
+
|
|
67
|
+
return bold ? chalk.bold(message) : message;
|
|
65
68
|
}
|
|
66
69
|
|
|
67
70
|
function getHasSandboxesByType(parentAccountConfig, type) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "4.1.8-beta.
|
|
3
|
+
"version": "4.1.8-beta.5",
|
|
4
4
|
"description": "CLI for working with HubSpot",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -43,5 +43,5 @@
|
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public"
|
|
45
45
|
},
|
|
46
|
-
"gitHead": "
|
|
46
|
+
"gitHead": "5b5d25bf5e3a4d29b516dbc31195f4cd7051b898"
|
|
47
47
|
}
|