@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
package/lib/sandboxes.js
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
const cliProgress = require('cli-progress');
|
|
2
1
|
const {
|
|
3
2
|
getConfig,
|
|
4
3
|
writeConfig,
|
|
5
4
|
updateAccountConfig,
|
|
5
|
+
getAccountId,
|
|
6
6
|
} = require('@hubspot/cli-lib');
|
|
7
|
-
const
|
|
8
|
-
DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
|
|
9
|
-
PERSONAL_ACCESS_KEY_AUTH_METHOD,
|
|
10
|
-
} = require('@hubspot/cli-lib/lib/constants');
|
|
7
|
+
const chalk = require('chalk');
|
|
11
8
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
12
9
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
13
10
|
const {
|
|
@@ -15,36 +12,68 @@ const {
|
|
|
15
12
|
} = require('@hubspot/cli-lib/personalAccessKey');
|
|
16
13
|
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
17
14
|
const { enterAccountNamePrompt } = require('./prompts/enterAccountNamePrompt');
|
|
15
|
+
const {
|
|
16
|
+
fetchTaskStatus,
|
|
17
|
+
fetchTypes,
|
|
18
|
+
getSandboxUsageLimits,
|
|
19
|
+
} = require('@hubspot/cli-lib/sandboxes');
|
|
20
|
+
const { handleExit, handleKeypress } = require('@hubspot/cli-lib/lib/process');
|
|
21
|
+
const { accountNameExistsInConfig } = require('@hubspot/cli-lib/lib/config');
|
|
22
|
+
const CliProgressMultibarManager = require('./CliProgressMultibarManager');
|
|
23
|
+
const { promptUser } = require('./prompts/promptUtils');
|
|
24
|
+
const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
18
25
|
const {
|
|
19
26
|
personalAccessKeyPrompt,
|
|
20
27
|
} = require('./prompts/personalAccessKeyPrompt');
|
|
21
|
-
const {
|
|
22
|
-
setAsDefaultAccountPrompt,
|
|
23
|
-
} = require('./prompts/setAsDefaultAccountPrompt');
|
|
24
|
-
const { uiFeatureHighlight } = require('./ui');
|
|
25
|
-
const { fetchTaskStatus, fetchTypes } = require('@hubspot/cli-lib/sandboxes');
|
|
26
|
-
const { handleExit, handleKeypress } = require('@hubspot/cli-lib/lib/process');
|
|
27
28
|
|
|
28
|
-
const
|
|
29
|
+
const STANDARD_SANDBOX = 'standard';
|
|
30
|
+
const DEVELOPER_SANDBOX = 'developer';
|
|
31
|
+
|
|
32
|
+
const syncTypes = {
|
|
33
|
+
OBJECT_RECORDS: 'object-records',
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const sandboxTypeMap = {
|
|
37
|
+
DEV: DEVELOPER_SANDBOX,
|
|
38
|
+
dev: DEVELOPER_SANDBOX,
|
|
39
|
+
DEVELOPER: DEVELOPER_SANDBOX,
|
|
40
|
+
developer: DEVELOPER_SANDBOX,
|
|
41
|
+
DEVELOPMENT: DEVELOPER_SANDBOX,
|
|
42
|
+
development: DEVELOPER_SANDBOX,
|
|
43
|
+
STANDARD: STANDARD_SANDBOX,
|
|
44
|
+
standard: STANDARD_SANDBOX,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const sandboxApiTypeMap = {
|
|
48
|
+
standard: 1,
|
|
49
|
+
developer: 2,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const getSandboxTypeAsString = type =>
|
|
29
53
|
type === 'DEVELOPER' ? 'development' : 'standard';
|
|
30
54
|
|
|
55
|
+
const isSandbox = config =>
|
|
56
|
+
config.sandboxAccountType && config.sandboxAccountType !== null;
|
|
57
|
+
|
|
31
58
|
function getAccountName(config) {
|
|
32
|
-
const
|
|
33
|
-
config.sandboxAccountType
|
|
34
|
-
|
|
35
|
-
return
|
|
59
|
+
const sandboxName = `[${getSandboxTypeAsString(
|
|
60
|
+
config.sandboxAccountType
|
|
61
|
+
)} sandbox] `;
|
|
62
|
+
return chalk.bold(
|
|
63
|
+
`${config.name} ${isSandbox(config) ? sandboxName : ''}(${config.portalId})`
|
|
64
|
+
);
|
|
36
65
|
}
|
|
37
66
|
|
|
38
|
-
function
|
|
67
|
+
function getHasSandboxesByType(parentAccountConfig, type) {
|
|
39
68
|
const config = getConfig();
|
|
40
|
-
const parentPortalId = parentAccountConfig.portalId;
|
|
69
|
+
const parentPortalId = getAccountId(parentAccountConfig.portalId);
|
|
41
70
|
for (const portal of config.portals) {
|
|
42
71
|
if (
|
|
43
72
|
(portal.parentAccountId !== null ||
|
|
44
73
|
portal.parentAccountId !== undefined) &&
|
|
45
74
|
portal.parentAccountId === parentPortalId &&
|
|
46
75
|
portal.sandboxAccountType &&
|
|
47
|
-
portal.sandboxAccountType ===
|
|
76
|
+
sandboxTypeMap[portal.sandboxAccountType] === type
|
|
48
77
|
) {
|
|
49
78
|
return true;
|
|
50
79
|
}
|
|
@@ -52,7 +81,7 @@ function getHasDevelopmentSandboxes(parentAccountConfig) {
|
|
|
52
81
|
return false;
|
|
53
82
|
}
|
|
54
83
|
|
|
55
|
-
function
|
|
84
|
+
function getSandboxLimit(error) {
|
|
56
85
|
// Error context should contain a limit property with a list of one number. That number is the current limit
|
|
57
86
|
const limit = error.context && error.context.limit && error.context.limit[0];
|
|
58
87
|
return limit ? parseInt(limit, 10) : 1; // Default to 1
|
|
@@ -60,31 +89,186 @@ function getDevSandboxLimit(error) {
|
|
|
60
89
|
|
|
61
90
|
// Fetches available sync types for a given sandbox portal
|
|
62
91
|
async function getAvailableSyncTypes(parentAccountConfig, config) {
|
|
63
|
-
const parentPortalId = parentAccountConfig.portalId;
|
|
64
|
-
const portalId = config.portalId;
|
|
92
|
+
const parentPortalId = getAccountId(parentAccountConfig.portalId);
|
|
93
|
+
const portalId = getAccountId(config.portalId);
|
|
65
94
|
const syncTypes = await fetchTypes(parentPortalId, portalId);
|
|
95
|
+
if (!syncTypes) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
'Unable to fetch available sandbox sync types. Please try again.'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
66
100
|
return syncTypes.map(t => ({ type: t.name }));
|
|
67
101
|
}
|
|
68
102
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
103
|
+
/**
|
|
104
|
+
* @param {Object} accountConfig - Account config of sandbox portal
|
|
105
|
+
* @param {Array} availableSyncTasks - Array of available sync tasks
|
|
106
|
+
* @param {Boolean} skipPrompt - Option to skip contact records prompt and return all available sync tasks
|
|
107
|
+
* @returns {Array} Adjusted available sync task items
|
|
108
|
+
*/
|
|
109
|
+
const getSyncTypesWithContactRecordsPrompt = async (
|
|
110
|
+
accountConfig,
|
|
111
|
+
syncTasks,
|
|
112
|
+
skipPrompt = false
|
|
113
|
+
) => {
|
|
114
|
+
// Fetches sync types based on default account. Parent account required for fetch
|
|
115
|
+
|
|
116
|
+
if (
|
|
117
|
+
syncTasks &&
|
|
118
|
+
syncTasks.some(t => t.type === syncTypes.OBJECT_RECORDS) &&
|
|
119
|
+
!skipPrompt
|
|
120
|
+
) {
|
|
121
|
+
const { contactRecordsSyncPrompt } = await promptUser([
|
|
122
|
+
{
|
|
123
|
+
name: 'contactRecordsSyncPrompt',
|
|
124
|
+
type: 'confirm',
|
|
125
|
+
message: i18n(
|
|
126
|
+
`cli.lib.sandbox.sync.confirm.syncContactRecords.${
|
|
127
|
+
sandboxTypeMap[accountConfig.sandboxAccountType]
|
|
128
|
+
}`
|
|
129
|
+
),
|
|
130
|
+
},
|
|
131
|
+
]);
|
|
132
|
+
if (!contactRecordsSyncPrompt) {
|
|
133
|
+
return syncTasks.filter(t => t.type !== syncTypes.OBJECT_RECORDS);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return syncTasks;
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* @param {Object} accountConfig - Account config of sandbox portal
|
|
141
|
+
* @param {String} sandboxType - Sandbox type for limit validation
|
|
142
|
+
* @param {String} env - Environment
|
|
143
|
+
* @returns {null}
|
|
144
|
+
*/
|
|
145
|
+
const validateSandboxUsageLimits = async (accountConfig, sandboxType, env) => {
|
|
146
|
+
const accountId = getAccountId(accountConfig.portalId);
|
|
147
|
+
const usage = await getSandboxUsageLimits(accountId);
|
|
148
|
+
if (!usage) {
|
|
149
|
+
throw new Error('Unable to fetch sandbox usage limits. Please try again.');
|
|
150
|
+
}
|
|
151
|
+
if (sandboxType === DEVELOPER_SANDBOX) {
|
|
152
|
+
if (usage['DEVELOPER'].available === 0) {
|
|
153
|
+
const devSandboxLimit = usage['DEVELOPER'].limit;
|
|
154
|
+
const plural = devSandboxLimit !== 1;
|
|
155
|
+
const hasDevelopmentSandboxes = getHasSandboxesByType(
|
|
156
|
+
accountConfig,
|
|
157
|
+
DEVELOPER_SANDBOX
|
|
158
|
+
);
|
|
159
|
+
if (hasDevelopmentSandboxes) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
i18n(
|
|
162
|
+
`cli.lib.sandbox.create.failure.alreadyInConfig.developer.${
|
|
163
|
+
plural ? 'other' : 'one'
|
|
164
|
+
}`,
|
|
165
|
+
{
|
|
166
|
+
accountName: accountConfig.name || accountId,
|
|
167
|
+
limit: devSandboxLimit,
|
|
168
|
+
}
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
} else {
|
|
172
|
+
const baseUrl = getHubSpotWebsiteOrigin(env);
|
|
173
|
+
throw new Error(
|
|
174
|
+
i18n(
|
|
175
|
+
`cli.lib.sandbox.create.failure.limit.developer.${
|
|
176
|
+
plural ? 'other' : 'one'
|
|
177
|
+
}`,
|
|
178
|
+
{
|
|
179
|
+
accountName: accountConfig.name || accountId,
|
|
180
|
+
limit: devSandboxLimit,
|
|
181
|
+
link: `${baseUrl}/sandboxes-developer/${accountId}/development`,
|
|
182
|
+
}
|
|
183
|
+
)
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (sandboxType === STANDARD_SANDBOX) {
|
|
189
|
+
if (usage['STANDARD'].available === 0) {
|
|
190
|
+
const standardSandboxLimit = usage['STANDARD'].limit;
|
|
191
|
+
const plural = standardSandboxLimit !== 1;
|
|
192
|
+
const hasStandardSandboxes = getHasSandboxesByType(
|
|
193
|
+
accountConfig,
|
|
194
|
+
STANDARD_SANDBOX
|
|
195
|
+
);
|
|
196
|
+
if (hasStandardSandboxes) {
|
|
197
|
+
throw new Error(
|
|
198
|
+
i18n(
|
|
199
|
+
`cli.lib.sandbox.create.failure.alreadyInConfig.standard.${
|
|
200
|
+
plural ? 'other' : 'one'
|
|
201
|
+
}`,
|
|
202
|
+
{
|
|
203
|
+
accountName: accountConfig.name || accountId,
|
|
204
|
+
limit: standardSandboxLimit,
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
} else {
|
|
209
|
+
const baseUrl = getHubSpotWebsiteOrigin(env);
|
|
210
|
+
throw new Error(
|
|
211
|
+
i18n(
|
|
212
|
+
`cli.lib.sandbox.create.failure.limit.standard.${
|
|
213
|
+
plural ? 'other' : 'one'
|
|
214
|
+
}`,
|
|
215
|
+
{
|
|
216
|
+
accountName: accountConfig.name || accountId,
|
|
217
|
+
limit: standardSandboxLimit,
|
|
218
|
+
link: `${baseUrl}/sandboxes-developer/${accountId}/standard`,
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
72
226
|
|
|
227
|
+
/**
|
|
228
|
+
* @param {String} env - Environment (QA/Prod)
|
|
229
|
+
* @param {Object} result - Sandbox instance returned from API
|
|
230
|
+
* @param {Boolean} force - Force flag to skip prompt
|
|
231
|
+
* @returns {String} validName saved into config
|
|
232
|
+
*/
|
|
233
|
+
const saveSandboxToConfig = async (env, result, force = false) => {
|
|
234
|
+
let configData = { env, personalAccessKey: result.personalAccessKey };
|
|
235
|
+
if (!result.personalAccessKey) {
|
|
236
|
+
configData = await personalAccessKeyPrompt({
|
|
237
|
+
env,
|
|
238
|
+
account: result.sandbox.sandboxHubId,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const updatedConfig = await updateConfigWithPersonalAccessKey(configData);
|
|
73
242
|
if (!updatedConfig) {
|
|
74
|
-
|
|
243
|
+
throw new Error('Failed to update config with personal access key.');
|
|
75
244
|
}
|
|
76
245
|
|
|
77
246
|
let validName = updatedConfig.name;
|
|
78
|
-
|
|
79
247
|
if (!updatedConfig.name) {
|
|
80
|
-
const nameForConfig = name
|
|
248
|
+
const nameForConfig = result.sandbox.name
|
|
81
249
|
.toLowerCase()
|
|
82
250
|
.split(' ')
|
|
83
251
|
.join('-');
|
|
84
|
-
|
|
85
|
-
|
|
252
|
+
validName = nameForConfig;
|
|
253
|
+
const invalidAccountName = accountNameExistsInConfig(nameForConfig);
|
|
254
|
+
if (invalidAccountName) {
|
|
255
|
+
if (!force) {
|
|
256
|
+
logger.log(
|
|
257
|
+
i18n(
|
|
258
|
+
`cli.lib.prompts.enterAccountNamePrompt.errors.accountNameExists`,
|
|
259
|
+
{ name: nameForConfig }
|
|
260
|
+
)
|
|
261
|
+
);
|
|
262
|
+
const { name: promptName } = await enterAccountNamePrompt(
|
|
263
|
+
nameForConfig
|
|
264
|
+
);
|
|
265
|
+
validName = promptName;
|
|
266
|
+
} else {
|
|
267
|
+
// Basic invalid name handling when force flag is passed
|
|
268
|
+
validName = nameForConfig + `_${Date.now()}`;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
86
271
|
}
|
|
87
|
-
|
|
88
272
|
updateAccountConfig({
|
|
89
273
|
...updatedConfig,
|
|
90
274
|
environment: updatedConfig.env,
|
|
@@ -93,35 +277,8 @@ const sandboxCreatePersonalAccessKeyFlow = async (env, account, name) => {
|
|
|
93
277
|
});
|
|
94
278
|
writeConfig();
|
|
95
279
|
|
|
96
|
-
const setAsDefault = await setAsDefaultAccountPrompt(validName);
|
|
97
|
-
|
|
98
280
|
logger.log('');
|
|
99
|
-
|
|
100
|
-
logger.success(
|
|
101
|
-
i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.setAsDefaultAccount`, {
|
|
102
|
-
accountName: validName,
|
|
103
|
-
})
|
|
104
|
-
);
|
|
105
|
-
} else {
|
|
106
|
-
const config = getConfig();
|
|
107
|
-
logger.info(
|
|
108
|
-
i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.keepingCurrentDefault`, {
|
|
109
|
-
accountName: config.defaultPortal,
|
|
110
|
-
})
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
logger.success(
|
|
114
|
-
i18n('cli.commands.sandbox.subcommands.create.success.configFileUpdated', {
|
|
115
|
-
configFilename: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
|
|
116
|
-
authMethod: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
|
|
117
|
-
account: validName,
|
|
118
|
-
})
|
|
119
|
-
);
|
|
120
|
-
uiFeatureHighlight([
|
|
121
|
-
'accountsUseCommand',
|
|
122
|
-
'accountOption',
|
|
123
|
-
'accountsListCommand',
|
|
124
|
-
]);
|
|
281
|
+
return validName;
|
|
125
282
|
};
|
|
126
283
|
|
|
127
284
|
const ACTIVE_TASK_POLL_INTERVAL = 1000;
|
|
@@ -133,46 +290,56 @@ const isTaskComplete = task => {
|
|
|
133
290
|
return task.status === 'COMPLETE';
|
|
134
291
|
};
|
|
135
292
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
293
|
+
const incrementBy = (value, multiplier = 3) => {
|
|
294
|
+
return Math.min(value + Math.floor(Math.random() * multiplier), 99);
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* @param {Number} accountId - Parent portal ID (needs sandbox scopes)
|
|
299
|
+
* @param {String} taksId - Task ID to poll
|
|
300
|
+
* @param {String} syncStatusUrl - Link to UI to check polling status
|
|
301
|
+
* @param {Boolean} allowEarlyTermination - Option to allow a keypress to terminate early
|
|
302
|
+
* @returns {Promise} Interval runs until sync task status is equal to 'COMPLETE'
|
|
303
|
+
*/
|
|
304
|
+
function pollSyncTaskStatus(
|
|
305
|
+
accountId,
|
|
306
|
+
taskId,
|
|
307
|
+
syncStatusUrl,
|
|
308
|
+
allowEarlyTermination = true
|
|
309
|
+
) {
|
|
310
|
+
const i18nKey = 'cli.lib.sandbox.sync.types';
|
|
311
|
+
const progressBar = CliProgressMultibarManager.init();
|
|
147
312
|
const mergeTasks = {
|
|
148
313
|
'lead-flows': 'forms', // lead-flows are a subset of forms. We combine these in the UI as a single item, so we want to merge here for consistency.
|
|
149
314
|
};
|
|
150
|
-
|
|
315
|
+
let progressCounter = {};
|
|
151
316
|
let pollInterval;
|
|
152
317
|
// Handle manual exit for return key and ctrl+c
|
|
153
318
|
const onTerminate = () => {
|
|
154
319
|
clearInterval(pollInterval);
|
|
155
|
-
|
|
320
|
+
progressBar.stop();
|
|
156
321
|
logger.log('');
|
|
157
322
|
logger.log('Exiting, sync will continue in the background.');
|
|
158
323
|
logger.log('');
|
|
159
324
|
logger.log(
|
|
160
|
-
i18n('cli.
|
|
325
|
+
i18n('cli.lib.sandbox.sync.info.syncStatus', {
|
|
161
326
|
url: syncStatusUrl,
|
|
162
327
|
})
|
|
163
328
|
);
|
|
164
329
|
process.exit(EXIT_CODES.SUCCESS);
|
|
165
330
|
};
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
331
|
+
if (allowEarlyTermination) {
|
|
332
|
+
handleExit(onTerminate);
|
|
333
|
+
handleKeypress(key => {
|
|
334
|
+
if (
|
|
335
|
+
(key && key.ctrl && key.name == 'c') ||
|
|
336
|
+
key.name === 'enter' ||
|
|
337
|
+
key.name === 'return'
|
|
338
|
+
) {
|
|
339
|
+
onTerminate();
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
176
343
|
return new Promise((resolve, reject) => {
|
|
177
344
|
pollInterval = setInterval(async () => {
|
|
178
345
|
const taskResult = await fetchTaskStatus(accountId, taskId).catch(reject);
|
|
@@ -181,13 +348,17 @@ function pollSyncTaskStatus(accountId, taskId, syncStatusUrl) {
|
|
|
181
348
|
for (const task of taskResult.tasks) {
|
|
182
349
|
// For each sync task, show a progress bar and increment bar each time we run this interval until status is 'COMPLETE'
|
|
183
350
|
const taskType = task.type;
|
|
184
|
-
if (!
|
|
185
|
-
// skip creation of lead-flows bar because we're combining lead-flows into the forms bar
|
|
186
|
-
|
|
187
|
-
|
|
351
|
+
if (!progressBar.get(taskType) && !mergeTasks[taskType]) {
|
|
352
|
+
// skip creation of lead-flows bar because we're combining lead-flows into the forms bar, otherwise create a bar instance for the type
|
|
353
|
+
progressCounter[taskType] = 0;
|
|
354
|
+
progressBar.create(taskType, 100, 0, {
|
|
355
|
+
label: i18n(`${i18nKey}.${taskType}.label`),
|
|
188
356
|
});
|
|
189
357
|
} else if (mergeTasks[taskType]) {
|
|
190
|
-
//
|
|
358
|
+
// It's a lead-flow here, merge status into the forms progress bar
|
|
359
|
+
if (!progressCounter[mergeTasks[taskType]]) {
|
|
360
|
+
progressCounter[mergeTasks[taskType]] = 0;
|
|
361
|
+
}
|
|
191
362
|
const formsTask = taskResult.tasks.filter(
|
|
192
363
|
t => t.type === mergeTasks[taskType]
|
|
193
364
|
)[0];
|
|
@@ -197,46 +368,66 @@ function pollSyncTaskStatus(accountId, taskId, syncStatusUrl) {
|
|
|
197
368
|
formsTaskStatus !== 'COMPLETE' ||
|
|
198
369
|
leadFlowsTaskStatus !== 'COMPLETE'
|
|
199
370
|
) {
|
|
200
|
-
|
|
201
|
-
|
|
371
|
+
// Randomly increment bar while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
|
|
372
|
+
progressCounter[mergeTasks[taskType]] = incrementBy(
|
|
373
|
+
progressCounter[mergeTasks[taskType]]
|
|
374
|
+
);
|
|
375
|
+
progressBar.update(
|
|
376
|
+
mergeTasks[taskType],
|
|
377
|
+
progressCounter[mergeTasks[taskType]],
|
|
202
378
|
{
|
|
203
|
-
|
|
379
|
+
label: i18n(`${i18nKey}.${mergeTasks[taskType]}.label`),
|
|
204
380
|
}
|
|
205
381
|
);
|
|
206
382
|
}
|
|
207
383
|
}
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
|
|
384
|
+
if (progressBar.get(taskType) && task.status === 'COMPLETE') {
|
|
385
|
+
progressBar.update(taskType, 100, {
|
|
386
|
+
label: i18n(`${i18nKey}.${taskType}.label`),
|
|
211
387
|
});
|
|
212
|
-
} else if (
|
|
213
|
-
// Do not
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
388
|
+
} else if (
|
|
389
|
+
// Do not start incrementing for tasks still in PENDING state
|
|
390
|
+
progressBar.get(taskType) &&
|
|
391
|
+
task.status === 'PROCESSING'
|
|
392
|
+
) {
|
|
393
|
+
// Randomly increment bar while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
|
|
394
|
+
progressCounter[taskType] = incrementBy(
|
|
395
|
+
progressCounter[taskType],
|
|
396
|
+
taskType === syncTypes.OBJECT_RECORDS ? 2 : 3 // slower progress for object-records, sync can take up to a few minutes
|
|
397
|
+
);
|
|
398
|
+
progressBar.update(taskType, progressCounter[taskType], {
|
|
399
|
+
label: i18n(`${i18nKey}.${taskType}.label`),
|
|
217
400
|
});
|
|
218
401
|
}
|
|
219
402
|
}
|
|
220
403
|
} else {
|
|
221
404
|
clearInterval(pollInterval);
|
|
222
405
|
reject();
|
|
223
|
-
|
|
406
|
+
progressBar.stop();
|
|
224
407
|
}
|
|
225
408
|
if (isTaskComplete(taskResult)) {
|
|
226
409
|
clearInterval(pollInterval);
|
|
227
410
|
resolve(taskResult);
|
|
228
|
-
|
|
411
|
+
progressBar.stop();
|
|
229
412
|
}
|
|
230
413
|
}, ACTIVE_TASK_POLL_INTERVAL);
|
|
231
414
|
});
|
|
232
415
|
}
|
|
233
416
|
|
|
234
417
|
module.exports = {
|
|
235
|
-
|
|
418
|
+
STANDARD_SANDBOX,
|
|
419
|
+
DEVELOPER_SANDBOX,
|
|
420
|
+
sandboxTypeMap,
|
|
421
|
+
sandboxApiTypeMap,
|
|
422
|
+
syncTypes,
|
|
423
|
+
isSandbox,
|
|
424
|
+
getSandboxTypeAsString,
|
|
236
425
|
getAccountName,
|
|
237
|
-
|
|
238
|
-
|
|
426
|
+
saveSandboxToConfig,
|
|
427
|
+
getHasSandboxesByType,
|
|
428
|
+
getSandboxLimit,
|
|
429
|
+
validateSandboxUsageLimits,
|
|
239
430
|
getAvailableSyncTypes,
|
|
240
|
-
|
|
431
|
+
getSyncTypesWithContactRecordsPrompt,
|
|
241
432
|
pollSyncTaskStatus,
|
|
242
433
|
};
|
package/lib/ui.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const process = require('process');
|
|
1
2
|
const chalk = require('chalk');
|
|
2
3
|
const supportsHyperlinks = require('../lib/supportHyperlinks');
|
|
3
4
|
const supportsColor = require('../lib/supportsColor');
|
|
@@ -35,7 +36,7 @@ const getTerminalUISupport = () => {
|
|
|
35
36
|
* @param {object} options
|
|
36
37
|
* @returns {string}
|
|
37
38
|
*/
|
|
38
|
-
const uiLink = (linkText, url) => {
|
|
39
|
+
const uiLink = (linkText, url, { inSpinnies = false } = {}) => {
|
|
39
40
|
const terminalUISupport = getTerminalUISupport();
|
|
40
41
|
const encodedUrl = encodeURI(url);
|
|
41
42
|
if (terminalUISupport.hyperlinks) {
|
|
@@ -46,12 +47,20 @@ const uiLink = (linkText, url) => {
|
|
|
46
47
|
linkText,
|
|
47
48
|
'\u001B]8;;\u0007',
|
|
48
49
|
].join('');
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
|
|
51
|
+
// Required b/c spinnies will break long lines
|
|
52
|
+
// See https://github.com/jbcarpanelli/spinnies/blob/d672dedcab8c8ce0f6de0bb26ca5582bf602afd7/utils.js#L68-L74
|
|
53
|
+
const columns = process.stderr.columns || 95;
|
|
54
|
+
const validLength = !inSpinnies || result.length < columns;
|
|
55
|
+
|
|
56
|
+
if (validLength) {
|
|
57
|
+
return terminalUISupport.color ? chalk.cyan(result) : result;
|
|
58
|
+
}
|
|
54
59
|
}
|
|
60
|
+
|
|
61
|
+
return terminalUISupport.color
|
|
62
|
+
? `${linkText}: ${chalk.reset.cyan(encodedUrl)}`
|
|
63
|
+
: `${linkText}: ${encodedUrl}`;
|
|
55
64
|
};
|
|
56
65
|
|
|
57
66
|
/**
|
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.2",
|
|
4
4
|
"description": "CLI for working with HubSpot",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -8,10 +8,11 @@
|
|
|
8
8
|
"url": "https://github.com/HubSpot/hubspot-cms-tools"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@hubspot/cli-lib": "4.1.8-beta.
|
|
12
|
-
"@hubspot/serverless-dev-runtime": "4.1.8-beta.
|
|
11
|
+
"@hubspot/cli-lib": "4.1.8-beta.2",
|
|
12
|
+
"@hubspot/serverless-dev-runtime": "4.1.8-beta.2",
|
|
13
13
|
"archiver": "^5.3.0",
|
|
14
14
|
"chalk": "^4.1.2",
|
|
15
|
+
"chokidar": "^3.0.1",
|
|
15
16
|
"cli-progress": "^3.11.2",
|
|
16
17
|
"express": "^4.17.1",
|
|
17
18
|
"findup-sync": "^4.0.0",
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
"moment": "^2.29.1",
|
|
21
22
|
"open": "^7.0.3",
|
|
22
23
|
"ora": "^4.0.3",
|
|
24
|
+
"p-queue": "^6.0.2",
|
|
23
25
|
"spinnies": "^0.5.1",
|
|
24
26
|
"tmp": "^0.2.1",
|
|
25
27
|
"update-notifier": "^5.1.0",
|
|
@@ -38,5 +40,5 @@
|
|
|
38
40
|
"publishConfig": {
|
|
39
41
|
"access": "public"
|
|
40
42
|
},
|
|
41
|
-
"gitHead": "
|
|
43
|
+
"gitHead": "99dd6099b4d643bd335b1a1c3a28250ff7b0c39d"
|
|
42
44
|
}
|