@hubspot/cli 5.2.1-beta.8 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/auth.js +2 -4
- package/commands/functions/deploy.js +2 -2
- package/commands/init.js +2 -4
- package/commands/module/marketplace-validate.js +1 -1
- package/commands/project/__tests__/deploy.test.js +432 -0
- package/commands/project/cloneApp.js +208 -0
- package/commands/project/deploy.js +82 -27
- package/commands/project/listBuilds.js +1 -1
- package/commands/project/logs.js +1 -1
- package/commands/project/migrateApp.js +85 -30
- package/commands/project/upload.js +1 -1
- package/commands/project.js +15 -12
- package/commands/sandbox/create.js +17 -48
- package/commands/sandbox/delete.js +5 -2
- package/commands/sandbox/sync.js +12 -2
- package/commands/sandbox.js +2 -1
- package/commands/theme/marketplace-validate.js +1 -1
- package/lang/en.lyaml +77 -76
- package/lib/LocalDevManager.js +59 -11
- package/lib/buildAccount.js +3 -3
- package/lib/constants.js +3 -1
- package/lib/interpolationHelpers.js +3 -0
- package/lib/localDev.js +21 -11
- package/lib/marketplace-validate.js +11 -3
- package/lib/polling.js +16 -10
- package/lib/projects.js +143 -100
- package/lib/prompts/accountNamePrompt.js +78 -0
- package/lib/prompts/activeInstallConfirmationPrompt.js +20 -0
- package/lib/prompts/createProjectPrompt.js +12 -2
- package/lib/prompts/deployBuildIdPrompt.js +22 -0
- package/lib/prompts/installPublicAppPrompt.js +13 -4
- package/lib/prompts/personalAccessKeyPrompt.js +2 -2
- package/lib/prompts/sandboxesPrompt.js +12 -41
- package/lib/prompts/selectPublicAppPrompt.js +41 -22
- package/lib/sandboxSync.js +49 -68
- package/lib/sandboxes.js +8 -149
- package/lib/serverlessLogs.js +2 -2
- package/lib/ui/index.js +74 -0
- package/package.json +5 -4
- package/lib/prompts/buildIdPrompt.js +0 -35
- package/lib/prompts/developerTestAccountNamePrompt.js +0 -29
- package/lib/prompts/enterAccountNamePrompt.js +0 -33
- package/lib/ui/CliProgressMultibarManager.js +0 -66
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const { promptUser } = require('./promptUtils');
|
|
2
|
+
const { i18n } = require('../lang');
|
|
3
|
+
|
|
4
|
+
const i18nKey = 'lib.prompts.deployBuildIdPrompt';
|
|
5
|
+
|
|
6
|
+
const deployBuildIdPrompt = (latestBuildId, deployedBuildId, validate) => {
|
|
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,
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
module.exports = {
|
|
21
|
+
deployBuildIdPrompt,
|
|
22
|
+
};
|
|
@@ -12,20 +12,29 @@ const installPublicAppPrompt = async (
|
|
|
12
12
|
targetAccountId,
|
|
13
13
|
clientId,
|
|
14
14
|
scopes,
|
|
15
|
-
redirectUrls
|
|
15
|
+
redirectUrls,
|
|
16
|
+
isReinstall = false
|
|
16
17
|
) => {
|
|
17
18
|
logger.log('');
|
|
18
|
-
|
|
19
|
+
if (isReinstall) {
|
|
20
|
+
logger.log(i18n(`${i18nKey}.reinstallExplanation`));
|
|
21
|
+
} else {
|
|
22
|
+
logger.log(i18n(`${i18nKey}.explanation`));
|
|
23
|
+
}
|
|
19
24
|
|
|
20
25
|
const { shouldOpenBrowser } = await promptUser({
|
|
21
26
|
name: 'shouldOpenBrowser',
|
|
22
27
|
type: 'confirm',
|
|
23
|
-
message: i18n(
|
|
28
|
+
message: i18n(
|
|
29
|
+
isReinstall ? `${i18nKey}.reinstallPrompt` : `${i18nKey}.prompt`
|
|
30
|
+
),
|
|
24
31
|
});
|
|
25
32
|
|
|
26
|
-
if (!shouldOpenBrowser) {
|
|
33
|
+
if (!isReinstall && !shouldOpenBrowser) {
|
|
27
34
|
logger.log(i18n(`${i18nKey}.decline`));
|
|
28
35
|
process.exit(EXIT_CODES.SUCCESS);
|
|
36
|
+
} else if (!shouldOpenBrowser) {
|
|
37
|
+
return;
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
const websiteOrigin = getHubSpotWebsiteOrigin(env);
|
|
@@ -7,7 +7,7 @@ const { deleteEmptyConfigFile } = require('@hubspot/local-dev-lib/config');
|
|
|
7
7
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
|
|
8
8
|
const { logger } = require('@hubspot/local-dev-lib/logger');
|
|
9
9
|
const { promptUser } = require('./promptUtils');
|
|
10
|
-
const {
|
|
10
|
+
const { getCliAccountNamePromptConfig } = require('./accountNamePrompt');
|
|
11
11
|
const { i18n } = require('../lang');
|
|
12
12
|
const { uiInfoSection } = require('../ui');
|
|
13
13
|
const { EXIT_CODES } = require('../enums/exitCodes');
|
|
@@ -124,7 +124,7 @@ const SCOPES = {
|
|
|
124
124
|
};
|
|
125
125
|
|
|
126
126
|
const OAUTH_FLOW = [
|
|
127
|
-
|
|
127
|
+
getCliAccountNamePromptConfig(),
|
|
128
128
|
ACCOUNT_ID,
|
|
129
129
|
CLIENT_ID,
|
|
130
130
|
CLIENT_SECRET,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
const { promptUser } = require('./promptUtils');
|
|
2
2
|
const { i18n } = require('../lang');
|
|
3
|
-
const { accountNameExistsInConfig } = require('@hubspot/local-dev-lib/config');
|
|
4
3
|
const { uiAccountDescription } = require('../ui');
|
|
5
4
|
const {
|
|
6
5
|
HUBSPOT_ACCOUNT_TYPES,
|
|
@@ -29,42 +28,6 @@ const mapNonSandboxAccountChoices = portals =>
|
|
|
29
28
|
};
|
|
30
29
|
});
|
|
31
30
|
|
|
32
|
-
const sandboxNamePrompt = (type = HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX) => {
|
|
33
|
-
const isDevelopmentSandbox =
|
|
34
|
-
type === HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX;
|
|
35
|
-
const namePromptMessage = isDevelopmentSandbox
|
|
36
|
-
? `${i18nKey}.name.developmentSandboxMessage`
|
|
37
|
-
: `${i18nKey}.name.message`;
|
|
38
|
-
return promptUser([
|
|
39
|
-
{
|
|
40
|
-
name: 'name',
|
|
41
|
-
message: i18n(namePromptMessage),
|
|
42
|
-
validate(val) {
|
|
43
|
-
if (typeof val !== 'string') {
|
|
44
|
-
return i18n(`${i18nKey}.name.errors.invalidName`);
|
|
45
|
-
} else if (!val.length) {
|
|
46
|
-
return i18n(`${i18nKey}.name.errors.nameRequired`);
|
|
47
|
-
}
|
|
48
|
-
return accountNameExistsInConfig(val)
|
|
49
|
-
? i18n(`${i18nKey}.name.errors.accountNameExists`, { name: val })
|
|
50
|
-
: true;
|
|
51
|
-
},
|
|
52
|
-
default: `New ${isDevelopmentSandbox ? 'development ' : ''}sandbox`,
|
|
53
|
-
},
|
|
54
|
-
]);
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const sandboxTypeChoices = [
|
|
58
|
-
{
|
|
59
|
-
name: i18n(`${i18nKey}.type.developer`),
|
|
60
|
-
value: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
name: i18n(`${i18nKey}.type.standard`),
|
|
64
|
-
value: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
|
|
65
|
-
},
|
|
66
|
-
];
|
|
67
|
-
|
|
68
31
|
const sandboxTypePrompt = () => {
|
|
69
32
|
return promptUser([
|
|
70
33
|
{
|
|
@@ -72,7 +35,16 @@ const sandboxTypePrompt = () => {
|
|
|
72
35
|
message: i18n(`${i18nKey}.type.message`),
|
|
73
36
|
type: 'list',
|
|
74
37
|
look: false,
|
|
75
|
-
choices:
|
|
38
|
+
choices: [
|
|
39
|
+
{
|
|
40
|
+
name: i18n(`${i18nKey}.type.developer`),
|
|
41
|
+
value: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: i18n(`${i18nKey}.type.standard`),
|
|
45
|
+
value: HUBSPOT_ACCOUNT_TYPES.STANDARD_SANDBOX,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
76
48
|
default: HUBSPOT_ACCOUNT_TYPES.DEVELOPMENT_SANDBOX,
|
|
77
49
|
},
|
|
78
50
|
]);
|
|
@@ -90,8 +62,8 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
|
|
|
90
62
|
name: 'account',
|
|
91
63
|
message: i18n(
|
|
92
64
|
promptParentAccount
|
|
93
|
-
? `${i18nKey}.
|
|
94
|
-
: `${i18nKey}.
|
|
65
|
+
? `${i18nKey}.selectParentAccountName`
|
|
66
|
+
: `${i18nKey}.selectAccountName`
|
|
95
67
|
),
|
|
96
68
|
type: 'list',
|
|
97
69
|
look: false,
|
|
@@ -103,7 +75,6 @@ const deleteSandboxPrompt = (config, promptParentAccount = false) => {
|
|
|
103
75
|
};
|
|
104
76
|
|
|
105
77
|
module.exports = {
|
|
106
|
-
sandboxNamePrompt,
|
|
107
78
|
sandboxTypePrompt,
|
|
108
79
|
deleteSandboxPrompt,
|
|
109
80
|
};
|
|
@@ -10,17 +10,35 @@ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
|
10
10
|
|
|
11
11
|
const i18nKey = 'lib.prompts.selectPublicAppPrompt';
|
|
12
12
|
|
|
13
|
-
const fetchPublicAppOptions = async (
|
|
13
|
+
const fetchPublicAppOptions = async (
|
|
14
|
+
accountId,
|
|
15
|
+
accountName,
|
|
16
|
+
isMigratingApp = false
|
|
17
|
+
) => {
|
|
14
18
|
try {
|
|
15
19
|
const publicApps = await fetchPublicAppsForPortal(accountId);
|
|
16
20
|
const filteredPublicApps = publicApps.filter(
|
|
17
21
|
app => !app.projectId && !app.sourceId
|
|
18
22
|
);
|
|
19
23
|
|
|
20
|
-
if (
|
|
24
|
+
if (
|
|
25
|
+
!filteredPublicApps.length ||
|
|
26
|
+
(isMigratingApp &&
|
|
27
|
+
!filteredPublicApps.some(
|
|
28
|
+
app => !app.preventProjectMigrations || !app.listingInfo
|
|
29
|
+
))
|
|
30
|
+
) {
|
|
31
|
+
const headerTranslationKey = isMigratingApp
|
|
32
|
+
? 'noAppsMigration'
|
|
33
|
+
: 'noAppsClone';
|
|
34
|
+
const messageTranslationKey = isMigratingApp
|
|
35
|
+
? 'noAppsMigrationMessage'
|
|
36
|
+
: 'noAppsCloneMessage';
|
|
21
37
|
uiLine();
|
|
22
|
-
logger.error(i18n(`${i18nKey}.errors
|
|
23
|
-
logger.log(
|
|
38
|
+
logger.error(i18n(`${i18nKey}.errors.${headerTranslationKey}`));
|
|
39
|
+
logger.log(
|
|
40
|
+
i18n(`${i18nKey}.errors.${messageTranslationKey}`, { accountName })
|
|
41
|
+
);
|
|
24
42
|
uiLine();
|
|
25
43
|
process.exit(EXIT_CODES.SUCCESS);
|
|
26
44
|
}
|
|
@@ -35,30 +53,32 @@ const fetchPublicAppOptions = async (accountId, accountName) => {
|
|
|
35
53
|
const selectPublicAppPrompt = async ({
|
|
36
54
|
accountId,
|
|
37
55
|
accountName,
|
|
38
|
-
|
|
39
|
-
migrateApp = false,
|
|
56
|
+
isMigratingApp = false,
|
|
40
57
|
}) => {
|
|
41
|
-
const publicApps = await fetchPublicAppOptions(
|
|
42
|
-
|
|
58
|
+
const publicApps = await fetchPublicAppOptions(
|
|
59
|
+
accountId,
|
|
60
|
+
accountName,
|
|
61
|
+
isMigratingApp
|
|
62
|
+
);
|
|
63
|
+
const translationKey = isMigratingApp
|
|
64
|
+
? 'selectAppIdMigrate'
|
|
65
|
+
: 'selectAppIdClone';
|
|
43
66
|
|
|
44
67
|
return promptUser([
|
|
45
68
|
{
|
|
46
69
|
name: 'appId',
|
|
47
|
-
message: (
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
? i18n(`${i18nKey}.errors.invalidAppId`, {
|
|
51
|
-
appId: promptOptions.appId,
|
|
52
|
-
})
|
|
53
|
-
: i18n(`${i18nKey}.${translationKey}`, {
|
|
54
|
-
accountName,
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
when:
|
|
58
|
-
!promptOptions.appId ||
|
|
59
|
-
!publicApps.find(a => a.id === promptOptions.appId),
|
|
70
|
+
message: i18n(`${i18nKey}.${translationKey}`, {
|
|
71
|
+
accountName,
|
|
72
|
+
}),
|
|
60
73
|
type: 'list',
|
|
61
74
|
choices: publicApps.map(app => {
|
|
75
|
+
const { preventProjectMigrations, listingInfo } = app;
|
|
76
|
+
if (isMigratingApp && preventProjectMigrations && listingInfo) {
|
|
77
|
+
return {
|
|
78
|
+
name: `${app.name} (${app.id})`,
|
|
79
|
+
disabled: i18n(`${i18nKey}.errors.cannotBeMigrated`),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
62
82
|
return {
|
|
63
83
|
name: `${app.name} (${app.id})`,
|
|
64
84
|
value: app.id,
|
|
@@ -69,6 +89,5 @@ const selectPublicAppPrompt = async ({
|
|
|
69
89
|
};
|
|
70
90
|
|
|
71
91
|
module.exports = {
|
|
72
|
-
fetchPublicAppOptions,
|
|
73
92
|
selectPublicAppPrompt,
|
|
74
93
|
};
|
package/lib/sandboxSync.js
CHANGED
|
@@ -2,12 +2,7 @@ const SpinniesManager = require('./ui/SpinniesManager');
|
|
|
2
2
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
|
|
3
3
|
const { logger } = require('@hubspot/local-dev-lib/logger');
|
|
4
4
|
const { i18n } = require('./lang');
|
|
5
|
-
const {
|
|
6
|
-
const {
|
|
7
|
-
getAvailableSyncTypes,
|
|
8
|
-
pollSyncTaskStatus,
|
|
9
|
-
syncTypes,
|
|
10
|
-
} = require('./sandboxes');
|
|
5
|
+
const { getAvailableSyncTypes } = require('./sandboxes');
|
|
11
6
|
const { initiateSync } = require('@hubspot/local-dev-lib/sandboxes');
|
|
12
7
|
const {
|
|
13
8
|
debugErrorAndContext,
|
|
@@ -19,7 +14,13 @@ const {
|
|
|
19
14
|
} = require('@hubspot/local-dev-lib/errors/apiErrors');
|
|
20
15
|
const { getSandboxTypeAsString } = require('./sandboxes');
|
|
21
16
|
const { getAccountId } = require('@hubspot/local-dev-lib/config');
|
|
22
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
uiAccountDescription,
|
|
19
|
+
uiLine,
|
|
20
|
+
uiLink,
|
|
21
|
+
uiCommandDisabledBanner,
|
|
22
|
+
} = require('./ui');
|
|
23
|
+
const { isDevelopmentSandbox } = require('./accountTypes');
|
|
23
24
|
|
|
24
25
|
const i18nKey = 'lib.sandbox.sync';
|
|
25
26
|
|
|
@@ -28,8 +29,6 @@ const i18nKey = 'lib.sandbox.sync';
|
|
|
28
29
|
* @param {Object} parentAccountConfig - Account config of parent portal
|
|
29
30
|
* @param {String} env - Environment (QA/Prod)
|
|
30
31
|
* @param {Array} syncTasks - Array of available sync tasks
|
|
31
|
-
* @param {Boolean} allowEarlyTermination - Option to allow a keypress to terminate early
|
|
32
|
-
* @param {Boolean} skipPolling - Option to skip progress bars for polling and let sync run in background
|
|
33
32
|
* @returns
|
|
34
33
|
*/
|
|
35
34
|
const syncSandbox = async ({
|
|
@@ -37,15 +36,14 @@ const syncSandbox = async ({
|
|
|
37
36
|
parentAccountConfig,
|
|
38
37
|
env,
|
|
39
38
|
syncTasks,
|
|
40
|
-
|
|
41
|
-
skipPolling = false,
|
|
39
|
+
slimInfoMessage = false,
|
|
42
40
|
}) => {
|
|
43
41
|
const accountId = getAccountId(accountConfig.portalId);
|
|
44
42
|
const parentAccountId = getAccountId(parentAccountConfig.portalId);
|
|
43
|
+
const isDevSandbox = isDevelopmentSandbox(accountConfig);
|
|
45
44
|
SpinniesManager.init({
|
|
46
45
|
succeedColor: 'white',
|
|
47
46
|
});
|
|
48
|
-
let initiateSyncResponse;
|
|
49
47
|
let availableSyncTasks = syncTasks;
|
|
50
48
|
|
|
51
49
|
const baseUrl = getHubSpotWebsiteOrigin(env);
|
|
@@ -70,29 +68,27 @@ const syncSandbox = async ({
|
|
|
70
68
|
text: i18n(`${i18nKey}.loading.startSync`),
|
|
71
69
|
});
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
await initiateSync(
|
|
74
72
|
parentAccountId,
|
|
75
73
|
accountId,
|
|
76
74
|
availableSyncTasks,
|
|
77
75
|
accountId
|
|
78
76
|
);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
77
|
+
let spinniesText = isDevSandbox
|
|
78
|
+
? `${i18nKey}.loading.succeedDevSb`
|
|
79
|
+
: `${i18nKey}.loading.succeed`;
|
|
83
80
|
SpinniesManager.succeed('sandboxSync', {
|
|
84
|
-
text: i18n(
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
text: i18n(
|
|
82
|
+
slimInfoMessage ? `${i18nKey}.loading.successDevSbInfo` : spinniesText,
|
|
83
|
+
{
|
|
84
|
+
accountName: uiAccountDescription(accountId),
|
|
85
|
+
url: uiLink(
|
|
86
|
+
i18n(`${i18nKey}.info.syncStatusDetailsLinkText`),
|
|
87
|
+
syncStatusUrl
|
|
88
|
+
),
|
|
89
|
+
}
|
|
90
|
+
),
|
|
87
91
|
});
|
|
88
|
-
if (skipPolling && isDevelopmentSandbox(accountConfig)) {
|
|
89
|
-
if (syncTasks.some(t => t.type === syncTypes.OBJECT_RECORDS)) {
|
|
90
|
-
logger.log(i18n(`${i18nKey}.loading.skipPollingWithContacts`));
|
|
91
|
-
} else {
|
|
92
|
-
logger.log(i18n(`${i18nKey}.loading.skipPolling`));
|
|
93
|
-
}
|
|
94
|
-
logger.log('');
|
|
95
|
-
}
|
|
96
92
|
} catch (err) {
|
|
97
93
|
debugErrorAndContext(err);
|
|
98
94
|
|
|
@@ -157,6 +153,15 @@ const syncSandbox = async ({
|
|
|
157
153
|
account: uiAccountDescription(accountId),
|
|
158
154
|
})
|
|
159
155
|
);
|
|
156
|
+
} else if (
|
|
157
|
+
isSpecifiedError(err, {
|
|
158
|
+
statusCode: 404,
|
|
159
|
+
})
|
|
160
|
+
) {
|
|
161
|
+
uiCommandDisabledBanner(
|
|
162
|
+
'hs sandbox sync',
|
|
163
|
+
'https://app.hubspot.com/l/docs/guides/crm/project-cli-commands#developer-projects-cli-commands-beta'
|
|
164
|
+
);
|
|
160
165
|
} else {
|
|
161
166
|
logErrorInstance(err);
|
|
162
167
|
}
|
|
@@ -164,46 +169,22 @@ const syncSandbox = async ({
|
|
|
164
169
|
throw err;
|
|
165
170
|
}
|
|
166
171
|
|
|
167
|
-
if (!
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
SpinniesManager.succeed('syncComplete', {
|
|
184
|
-
text: i18n(`${i18nKey}.polling.succeed`),
|
|
185
|
-
});
|
|
186
|
-
logger.log('');
|
|
187
|
-
logger.log(
|
|
188
|
-
i18n(`${i18nKey}.info.syncStatus`, {
|
|
189
|
-
url: syncStatusUrl,
|
|
190
|
-
})
|
|
191
|
-
);
|
|
192
|
-
} catch (err) {
|
|
193
|
-
// If polling fails at this point, we do not track a failed sync since it is running in the background.
|
|
194
|
-
logErrorInstance(err);
|
|
195
|
-
|
|
196
|
-
SpinniesManager.add('syncComplete', {
|
|
197
|
-
text: i18n(`${i18nKey}.polling.syncing`),
|
|
198
|
-
});
|
|
199
|
-
SpinniesManager.fail('syncComplete', {
|
|
200
|
-
text: i18n(`${i18nKey}.polling.fail`, {
|
|
201
|
-
url: syncStatusUrl,
|
|
202
|
-
}),
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
throw err;
|
|
206
|
-
}
|
|
172
|
+
if (!slimInfoMessage) {
|
|
173
|
+
logger.log();
|
|
174
|
+
uiLine();
|
|
175
|
+
logger.info(
|
|
176
|
+
i18n(
|
|
177
|
+
`${i18nKey}.info.${isDevSandbox ? 'syncMessageDevSb' : 'syncMessage'}`,
|
|
178
|
+
{
|
|
179
|
+
url: uiLink(
|
|
180
|
+
i18n(`${i18nKey}.info.syncStatusDetailsLinkText`),
|
|
181
|
+
syncStatusUrl
|
|
182
|
+
),
|
|
183
|
+
}
|
|
184
|
+
)
|
|
185
|
+
);
|
|
186
|
+
uiLine();
|
|
187
|
+
logger.log();
|
|
207
188
|
}
|
|
208
189
|
};
|
|
209
190
|
|
package/lib/sandboxes.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
const { i18n
|
|
2
|
-
const { handleExit, handleKeypress } = require('./process');
|
|
1
|
+
const { i18n } = require('./lang');
|
|
3
2
|
const { logger } = require('@hubspot/local-dev-lib/logger');
|
|
4
|
-
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
5
3
|
const {
|
|
6
|
-
fetchTaskStatus,
|
|
7
4
|
fetchTypes,
|
|
8
5
|
getSandboxUsageLimits,
|
|
9
6
|
} = require('@hubspot/local-dev-lib/sandboxes');
|
|
@@ -12,7 +9,6 @@ const {
|
|
|
12
9
|
getAccountId,
|
|
13
10
|
getEnv,
|
|
14
11
|
} = require('@hubspot/local-dev-lib/config');
|
|
15
|
-
const CliProgressMultibarManager = require('./ui/CliProgressMultibarManager');
|
|
16
12
|
const { promptUser } = require('./prompts/promptUtils');
|
|
17
13
|
const { isDevelopmentSandbox } = require('./accountTypes');
|
|
18
14
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
|
|
@@ -97,21 +93,22 @@ const getSyncTypesWithContactRecordsPrompt = async (
|
|
|
97
93
|
syncTasks,
|
|
98
94
|
skipPrompt = false
|
|
99
95
|
) => {
|
|
100
|
-
//
|
|
101
|
-
|
|
96
|
+
// TODO: remove this entire helper once hs sandbox sync is fully deprecated
|
|
97
|
+
const isDevSandbox = isDevelopmentSandbox(accountConfig);
|
|
98
|
+
if (isDevSandbox) {
|
|
99
|
+
// Disable dev sandbox from syncing contacts
|
|
100
|
+
return syncTasks.filter(t => t.type !== syncTypes.OBJECT_RECORDS);
|
|
101
|
+
}
|
|
102
102
|
if (
|
|
103
103
|
syncTasks &&
|
|
104
104
|
syncTasks.some(t => t.type === syncTypes.OBJECT_RECORDS) &&
|
|
105
105
|
!skipPrompt
|
|
106
106
|
) {
|
|
107
|
-
const langKey = isDevelopmentSandbox(accountConfig)
|
|
108
|
-
? 'developer'
|
|
109
|
-
: 'standard';
|
|
110
107
|
const { contactRecordsSyncPrompt } = await promptUser([
|
|
111
108
|
{
|
|
112
109
|
name: 'contactRecordsSyncPrompt',
|
|
113
110
|
type: 'confirm',
|
|
114
|
-
message: i18n(
|
|
111
|
+
message: i18n('lib.sandbox.sync.confirm.syncContactRecords.standard'),
|
|
115
112
|
},
|
|
116
113
|
]);
|
|
117
114
|
if (!contactRecordsSyncPrompt) {
|
|
@@ -356,143 +353,6 @@ function handleSandboxCreateError({
|
|
|
356
353
|
throw err;
|
|
357
354
|
}
|
|
358
355
|
|
|
359
|
-
const ACTIVE_TASK_POLL_INTERVAL = 1000;
|
|
360
|
-
|
|
361
|
-
const isTaskComplete = task => {
|
|
362
|
-
if (!task) {
|
|
363
|
-
return false;
|
|
364
|
-
}
|
|
365
|
-
return task.status === 'COMPLETE';
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
const incrementBy = (value, multiplier = 3) => {
|
|
369
|
-
return Math.min(value + Math.floor(Math.random() * multiplier), 99);
|
|
370
|
-
};
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* @param {Number} accountId - Parent portal ID (needs sandbox scopes)
|
|
374
|
-
* @param {String} taksId - Task ID to poll
|
|
375
|
-
* @param {String} syncStatusUrl - Link to UI to check polling status
|
|
376
|
-
* @param {Boolean} allowEarlyTermination - Option to allow a keypress to terminate early
|
|
377
|
-
* @returns {Promise} Interval runs until sync task status is equal to 'COMPLETE'
|
|
378
|
-
*/
|
|
379
|
-
function pollSyncTaskStatus(
|
|
380
|
-
accountId,
|
|
381
|
-
taskId,
|
|
382
|
-
syncStatusUrl,
|
|
383
|
-
allowEarlyTermination = true
|
|
384
|
-
) {
|
|
385
|
-
const i18nKey = 'lib.sandbox.sync.types';
|
|
386
|
-
const progressBar = CliProgressMultibarManager.init();
|
|
387
|
-
const mergeTasks = {
|
|
388
|
-
'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.
|
|
389
|
-
};
|
|
390
|
-
let progressCounter = {};
|
|
391
|
-
let pollInterval;
|
|
392
|
-
// Handle manual exit for return key and ctrl+c
|
|
393
|
-
const onTerminate = () => {
|
|
394
|
-
clearInterval(pollInterval);
|
|
395
|
-
progressBar.stop();
|
|
396
|
-
logger.log('');
|
|
397
|
-
logger.log('Exiting, sync will continue in the background.');
|
|
398
|
-
logger.log('');
|
|
399
|
-
logger.log(
|
|
400
|
-
i18n('lib.sandbox.sync.info.syncStatus', {
|
|
401
|
-
url: syncStatusUrl,
|
|
402
|
-
})
|
|
403
|
-
);
|
|
404
|
-
process.exit(EXIT_CODES.SUCCESS);
|
|
405
|
-
};
|
|
406
|
-
if (allowEarlyTermination) {
|
|
407
|
-
handleExit(onTerminate);
|
|
408
|
-
handleKeypress(key => {
|
|
409
|
-
if (
|
|
410
|
-
(key && key.ctrl && key.name == 'c') ||
|
|
411
|
-
key.name === 'enter' ||
|
|
412
|
-
key.name === 'return'
|
|
413
|
-
) {
|
|
414
|
-
onTerminate();
|
|
415
|
-
}
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
return new Promise((resolve, reject) => {
|
|
419
|
-
pollInterval = setInterval(async () => {
|
|
420
|
-
const taskResult = await fetchTaskStatus(accountId, taskId).catch(reject);
|
|
421
|
-
if (taskResult.tasks) {
|
|
422
|
-
// Array of sync tasks, eg: workflows, pipelines, object-schemas, etc. with each task containing a status of 'PENDING', 'IN_PROGRESS', 'COMPLETE', and 'FAILURE'
|
|
423
|
-
for (const task of taskResult.tasks) {
|
|
424
|
-
// For each sync task, show a progress bar and increment bar each time we run this interval until status is 'COMPLETE'
|
|
425
|
-
let taskType = task.type;
|
|
426
|
-
const taskTypeLabel = i18n(`${i18nKey}.${taskType}.label`);
|
|
427
|
-
if (taskTypeLabel.startsWith(MISSING_LANGUAGE_DATA_PREFIX)) {
|
|
428
|
-
continue;
|
|
429
|
-
}
|
|
430
|
-
if (!progressBar.get(taskType) && !mergeTasks[taskType]) {
|
|
431
|
-
// skip creation of lead-flows bar because we're combining lead-flows into the forms bar, otherwise create a bar instance for the type
|
|
432
|
-
progressCounter[taskType] = 0;
|
|
433
|
-
progressBar.create(taskType, 100, 0, {
|
|
434
|
-
label: taskTypeLabel,
|
|
435
|
-
});
|
|
436
|
-
} else if (mergeTasks[taskType]) {
|
|
437
|
-
// It's a lead-flow here, merge status into the forms progress bar
|
|
438
|
-
if (!progressCounter[mergeTasks[taskType]]) {
|
|
439
|
-
progressCounter[mergeTasks[taskType]] = 0;
|
|
440
|
-
}
|
|
441
|
-
const formsTask = taskResult.tasks.filter(
|
|
442
|
-
t => t.type === mergeTasks[taskType]
|
|
443
|
-
)[0];
|
|
444
|
-
const formsTaskStatus = formsTask.status;
|
|
445
|
-
const leadFlowsTaskStatus = task.status;
|
|
446
|
-
if (
|
|
447
|
-
formsTaskStatus !== 'COMPLETE' ||
|
|
448
|
-
leadFlowsTaskStatus !== 'COMPLETE'
|
|
449
|
-
) {
|
|
450
|
-
// Randomly increment bar while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
|
|
451
|
-
progressCounter[mergeTasks[taskType]] = incrementBy(
|
|
452
|
-
progressCounter[mergeTasks[taskType]]
|
|
453
|
-
);
|
|
454
|
-
progressBar.update(
|
|
455
|
-
mergeTasks[taskType],
|
|
456
|
-
progressCounter[mergeTasks[taskType]],
|
|
457
|
-
{
|
|
458
|
-
label: i18n(`${i18nKey}.${mergeTasks[taskType]}.label`),
|
|
459
|
-
}
|
|
460
|
-
);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
if (progressBar.get(taskType) && task.status === 'COMPLETE') {
|
|
464
|
-
progressBar.update(taskType, 100, {
|
|
465
|
-
label: taskTypeLabel,
|
|
466
|
-
});
|
|
467
|
-
} else if (
|
|
468
|
-
// Do not start incrementing for tasks still in PENDING state
|
|
469
|
-
progressBar.get(taskType) &&
|
|
470
|
-
task.status === 'PROCESSING'
|
|
471
|
-
) {
|
|
472
|
-
// Randomly increment bar while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
|
|
473
|
-
progressCounter[taskType] = incrementBy(
|
|
474
|
-
progressCounter[taskType],
|
|
475
|
-
taskType === syncTypes.OBJECT_RECORDS ? 2 : 3 // slower progress for object-records, sync can take up to a few minutes
|
|
476
|
-
);
|
|
477
|
-
progressBar.update(taskType, progressCounter[taskType], {
|
|
478
|
-
label: taskTypeLabel,
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
} else {
|
|
483
|
-
clearInterval(pollInterval);
|
|
484
|
-
reject();
|
|
485
|
-
progressBar.stop();
|
|
486
|
-
}
|
|
487
|
-
if (isTaskComplete(taskResult)) {
|
|
488
|
-
clearInterval(pollInterval);
|
|
489
|
-
resolve(taskResult);
|
|
490
|
-
progressBar.stop();
|
|
491
|
-
}
|
|
492
|
-
}, ACTIVE_TASK_POLL_INTERVAL);
|
|
493
|
-
});
|
|
494
|
-
}
|
|
495
|
-
|
|
496
356
|
module.exports = {
|
|
497
357
|
sandboxTypeMap,
|
|
498
358
|
sandboxApiTypeMap,
|
|
@@ -503,6 +363,5 @@ module.exports = {
|
|
|
503
363
|
validateSandboxUsageLimits,
|
|
504
364
|
getAvailableSyncTypes,
|
|
505
365
|
getSyncTypesWithContactRecordsPrompt,
|
|
506
|
-
pollSyncTaskStatus,
|
|
507
366
|
handleSandboxCreateError,
|
|
508
367
|
};
|
package/lib/serverlessLogs.js
CHANGED
|
@@ -52,7 +52,7 @@ const tailLogs = async ({
|
|
|
52
52
|
initialAfter = latestLog && base64EncodeString(latestLog.id);
|
|
53
53
|
} catch (e) {
|
|
54
54
|
// A 404 means no latest log exists(never executed)
|
|
55
|
-
if (e.
|
|
55
|
+
if (e.response && e.response.status !== 404) {
|
|
56
56
|
await logServerlessFunctionApiErrorInstance(
|
|
57
57
|
accountId,
|
|
58
58
|
e,
|
|
@@ -68,7 +68,7 @@ const tailLogs = async ({
|
|
|
68
68
|
latestLog = await tailCall(after);
|
|
69
69
|
nextAfter = latestLog.paging.next.after;
|
|
70
70
|
} catch (e) {
|
|
71
|
-
if (e.
|
|
71
|
+
if (e.response && e.response.status !== 404) {
|
|
72
72
|
logApiErrorInstance(
|
|
73
73
|
e,
|
|
74
74
|
new ApiErrorContext({
|