@hubspot/cli 4.1.7-beta.0 → 4.1.7-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/commands/accounts/list.js +1 -1
- package/commands/module/marketplace-validate.js +27 -37
- package/commands/sandbox/create.js +24 -86
- package/commands/sandbox/delete.js +11 -8
- package/commands/sandbox/sync.js +300 -0
- package/commands/sandbox.js +2 -0
- package/commands/theme/marketplace-validate.js +15 -125
- package/lib/marketplace-validate.js +137 -0
- package/lib/prompts/accountsPrompt.js +1 -3
- package/lib/prompts/sandboxesPrompt.js +1 -3
- package/lib/sandboxes.js +217 -0
- package/package.json +5 -4
- package/lib/validators/__tests__/ModuleDependencyValidator.js +0 -79
- package/lib/validators/__tests__/ModuleValidator.js +0 -51
- package/lib/validators/__tests__/RelativeValidator.js +0 -28
- package/lib/validators/__tests__/validatorTestUtils.js +0 -8
- package/lib/validators/applyValidators.js +0 -17
- package/lib/validators/constants.js +0 -11
- package/lib/validators/index.js +0 -8
- package/lib/validators/logValidatorResults.js +0 -59
- package/lib/validators/marketplaceValidators/RelativeValidator.js +0 -46
- package/lib/validators/marketplaceValidators/module/ModuleDependencyValidator.js +0 -101
- package/lib/validators/marketplaceValidators/module/ModuleValidator.js +0 -102
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
const Spinnies = require('spinnies');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
|
-
|
|
4
|
-
const { logger } = require('@hubspot/cli-lib/logger');
|
|
5
2
|
|
|
6
3
|
const {
|
|
7
4
|
addConfigOptions,
|
|
@@ -12,16 +9,15 @@ const {
|
|
|
12
9
|
const { loadAndValidateOptions } = require('../../lib/validation');
|
|
13
10
|
const { trackCommandUsage } = require('../../lib/usageTracking');
|
|
14
11
|
const {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
kickOffValidation,
|
|
13
|
+
pollForValidationFinish,
|
|
14
|
+
fetchValidationResults,
|
|
15
|
+
processValidationErrors,
|
|
16
|
+
displayValidationResults,
|
|
17
|
+
} = require('../../lib/marketplace-validate');
|
|
20
18
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
21
19
|
|
|
22
20
|
const i18nKey = 'cli.commands.theme.subcommands.marketplaceValidate';
|
|
23
|
-
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
24
|
-
const SLEEP_TIME = 2000;
|
|
25
21
|
|
|
26
22
|
exports.command = 'marketplace-validate <src>';
|
|
27
23
|
exports.describe = i18n(`${i18nKey}.describe`);
|
|
@@ -36,130 +32,24 @@ exports.handler = async options => {
|
|
|
36
32
|
trackCommandUsage('validate', null, accountId);
|
|
37
33
|
|
|
38
34
|
const spinnies = new Spinnies();
|
|
39
|
-
|
|
40
35
|
spinnies.add('marketplaceValidation', {
|
|
41
36
|
text: i18n(`${i18nKey}.logs.validatingTheme`, {
|
|
42
37
|
path: src,
|
|
43
38
|
}),
|
|
44
39
|
});
|
|
45
40
|
|
|
46
|
-
// Kick off validation
|
|
47
|
-
let requestResult;
|
|
48
41
|
const assetType = 'THEME';
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
requestResult = await requestValidation(accountId, {
|
|
52
|
-
path: src,
|
|
53
|
-
assetType,
|
|
54
|
-
requestGroup,
|
|
55
|
-
});
|
|
56
|
-
} catch (err) {
|
|
57
|
-
logger.debug(err);
|
|
58
|
-
process.exit(EXIT_CODES.ERROR);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Poll till validation is finished
|
|
62
|
-
try {
|
|
63
|
-
const checkValidationStatus = async () => {
|
|
64
|
-
const validationStatus = await getValidationStatus(accountId, {
|
|
65
|
-
validationId: requestResult,
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
if (validationStatus === 'REQUESTED') {
|
|
69
|
-
await new Promise(resolve => setTimeout(resolve, SLEEP_TIME));
|
|
70
|
-
await checkValidationStatus();
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
await checkValidationStatus();
|
|
75
|
-
|
|
76
|
-
spinnies.remove('marketplaceValidation');
|
|
77
|
-
} catch (err) {
|
|
78
|
-
logger.debug(err);
|
|
79
|
-
process.exit(EXIT_CODES.ERROR);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Fetch the validation results
|
|
83
|
-
let validationResults;
|
|
84
|
-
try {
|
|
85
|
-
validationResults = await getValidationResults(accountId, {
|
|
86
|
-
validationId: requestResult,
|
|
87
|
-
});
|
|
88
|
-
} catch (err) {
|
|
89
|
-
logger.debug(err);
|
|
90
|
-
process.exit(EXIT_CODES.ERROR);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (validationResults.errors.length) {
|
|
94
|
-
const { errors } = validationResults;
|
|
95
|
-
|
|
96
|
-
errors.forEach(err => {
|
|
97
|
-
logger.error(`${err.context}`);
|
|
98
|
-
});
|
|
99
|
-
process.exit(EXIT_CODES.ERROR);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const displayResults = checks => {
|
|
103
|
-
const displayFileInfo = (file, line) => {
|
|
104
|
-
if (file) {
|
|
105
|
-
logger.log(
|
|
106
|
-
i18n(`${i18nKey}.results.warnings.file`, {
|
|
107
|
-
file,
|
|
108
|
-
})
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
if (line) {
|
|
112
|
-
logger.log(
|
|
113
|
-
i18n(`${i18nKey}.results.warnings.lineNumber`, {
|
|
114
|
-
line,
|
|
115
|
-
})
|
|
116
|
-
);
|
|
117
|
-
}
|
|
118
|
-
return null;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
if (checks) {
|
|
122
|
-
const { status, results } = checks;
|
|
123
|
-
|
|
124
|
-
if (status === 'FAIL') {
|
|
125
|
-
const failedValidations = results.filter(
|
|
126
|
-
test => test.status === 'FAIL'
|
|
127
|
-
);
|
|
128
|
-
const warningValidations = results.filter(
|
|
129
|
-
test => test.status === 'WARN'
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
failedValidations.forEach(val => {
|
|
133
|
-
logger.error(`${val.message}`);
|
|
134
|
-
displayFileInfo(val.file, val.line);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
warningValidations.forEach(val => {
|
|
138
|
-
logger.warn(`${val.message}`);
|
|
139
|
-
displayFileInfo(val.file, val.line);
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (status === 'PASS') {
|
|
144
|
-
logger.success(i18n(`${i18nKey}.results.noErrors`));
|
|
42
|
+
const validationId = await kickOffValidation(accountId, assetType, src);
|
|
43
|
+
await pollForValidationFinish(accountId, validationId);
|
|
145
44
|
|
|
146
|
-
|
|
147
|
-
if (test.status === 'WARN') {
|
|
148
|
-
logger.warn(`${test.message}`);
|
|
149
|
-
displayFileInfo(test.file, test.line);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
return null;
|
|
155
|
-
};
|
|
45
|
+
spinnies.remove('marketplaceValidation');
|
|
156
46
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
47
|
+
const validationResults = await fetchValidationResults(
|
|
48
|
+
accountId,
|
|
49
|
+
validationId
|
|
50
|
+
);
|
|
51
|
+
processValidationErrors(validationResults);
|
|
52
|
+
displayValidationResults(i18nKey, validationResults);
|
|
163
53
|
|
|
164
54
|
process.exit();
|
|
165
55
|
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const chalk = require('chalk');
|
|
2
|
+
|
|
3
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
4
|
+
const {
|
|
5
|
+
requestValidation,
|
|
6
|
+
getValidationStatus,
|
|
7
|
+
getValidationResults,
|
|
8
|
+
} = require('@hubspot/cli-lib/api/marketplaceValidation');
|
|
9
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
10
|
+
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
11
|
+
|
|
12
|
+
const SLEEP_TIME = 2000;
|
|
13
|
+
|
|
14
|
+
const kickOffValidation = async (accountId, assetType, src) => {
|
|
15
|
+
const requestGroup = 'EXTERNAL_DEVELOPER';
|
|
16
|
+
try {
|
|
17
|
+
const requestResult = await requestValidation(accountId, {
|
|
18
|
+
path: src,
|
|
19
|
+
assetType,
|
|
20
|
+
requestGroup,
|
|
21
|
+
});
|
|
22
|
+
return requestResult;
|
|
23
|
+
} catch (err) {
|
|
24
|
+
logger.debug(err);
|
|
25
|
+
process.exit(EXIT_CODES.ERROR);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const pollForValidationFinish = async (accountId, validationId) => {
|
|
30
|
+
try {
|
|
31
|
+
const checkValidationStatus = async () => {
|
|
32
|
+
const validationStatus = await getValidationStatus(accountId, {
|
|
33
|
+
validationId,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (validationStatus === 'REQUESTED') {
|
|
37
|
+
await new Promise(resolve => setTimeout(resolve, SLEEP_TIME));
|
|
38
|
+
await checkValidationStatus();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
await checkValidationStatus();
|
|
42
|
+
} catch (err) {
|
|
43
|
+
logger.debug(err);
|
|
44
|
+
process.exit(EXIT_CODES.ERROR);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const fetchValidationResults = async (accountId, validationId) => {
|
|
49
|
+
try {
|
|
50
|
+
const validationResults = await getValidationResults(accountId, {
|
|
51
|
+
validationId,
|
|
52
|
+
});
|
|
53
|
+
return validationResults;
|
|
54
|
+
} catch (err) {
|
|
55
|
+
logger.debug(err);
|
|
56
|
+
process.exit(EXIT_CODES.ERROR);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const processValidationErrors = validationResults => {
|
|
61
|
+
if (validationResults.errors.length) {
|
|
62
|
+
const { errors } = validationResults;
|
|
63
|
+
|
|
64
|
+
errors.forEach(err => {
|
|
65
|
+
logger.error(`${err.context}`);
|
|
66
|
+
});
|
|
67
|
+
process.exit(EXIT_CODES.ERROR);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const displayValidationResults = (i18nKey, validationResults) => {
|
|
72
|
+
const displayResults = checks => {
|
|
73
|
+
const displayFileInfo = (file, line) => {
|
|
74
|
+
if (file) {
|
|
75
|
+
logger.log(
|
|
76
|
+
i18n(`${i18nKey}.results.warnings.file`, {
|
|
77
|
+
file,
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
if (line) {
|
|
82
|
+
logger.log(
|
|
83
|
+
i18n(`${i18nKey}.results.warnings.lineNumber`, {
|
|
84
|
+
line,
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
if (checks) {
|
|
92
|
+
const { status, results } = checks;
|
|
93
|
+
|
|
94
|
+
if (status === 'FAIL') {
|
|
95
|
+
const failedValidations = results.filter(
|
|
96
|
+
test => test.status === 'FAIL'
|
|
97
|
+
);
|
|
98
|
+
const warningValidations = results.filter(
|
|
99
|
+
test => test.status === 'WARN'
|
|
100
|
+
);
|
|
101
|
+
failedValidations.forEach(val => {
|
|
102
|
+
logger.error(`${val.message}`);
|
|
103
|
+
displayFileInfo(val.file, val.line);
|
|
104
|
+
});
|
|
105
|
+
warningValidations.forEach(val => {
|
|
106
|
+
logger.warn(`${val.message}`);
|
|
107
|
+
displayFileInfo(val.file, val.line);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
if (status === 'PASS') {
|
|
111
|
+
logger.success(i18n(`${i18nKey}.results.noErrors`));
|
|
112
|
+
|
|
113
|
+
results.forEach(test => {
|
|
114
|
+
if (test.status === 'WARN') {
|
|
115
|
+
logger.warn(`${test.message}`);
|
|
116
|
+
displayFileInfo(test.file, test.line);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
Object.keys(validationResults.results).forEach(type => {
|
|
125
|
+
logger.log(chalk.bold(i18n(`${i18nKey}.results.${type.toLowerCase()}`)));
|
|
126
|
+
displayResults(validationResults.results[type]);
|
|
127
|
+
logger.log();
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
module.exports = {
|
|
132
|
+
kickOffValidation,
|
|
133
|
+
pollForValidationFinish,
|
|
134
|
+
fetchValidationResults,
|
|
135
|
+
processValidationErrors,
|
|
136
|
+
displayValidationResults,
|
|
137
|
+
};
|
|
@@ -1,9 +1,7 @@
|
|
|
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
|
-
|
|
5
|
-
const getSandboxType = type =>
|
|
6
|
-
type === 'DEVELOPER' ? 'development' : 'standard';
|
|
4
|
+
const { getSandboxType } = require('../sandboxes');
|
|
7
5
|
|
|
8
6
|
const mapAccountChoices = portals =>
|
|
9
7
|
portals.map(p => {
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
const { promptUser } = require('./promptUtils');
|
|
2
2
|
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
3
|
+
const { getSandboxType } = require('../sandboxes');
|
|
3
4
|
|
|
4
5
|
const i18nKey = 'cli.lib.prompts.sandboxesPrompt';
|
|
5
6
|
|
|
6
|
-
const getSandboxType = type =>
|
|
7
|
-
type === 'DEVELOPER' ? 'development' : 'standard';
|
|
8
|
-
|
|
9
7
|
const mapSandboxAccountChoices = portals =>
|
|
10
8
|
portals
|
|
11
9
|
.filter(p => p.sandboxAccountType && p.sandboxAccountType !== null)
|
package/lib/sandboxes.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
const cliProgress = require('cli-progress');
|
|
2
|
+
const {
|
|
3
|
+
getConfig,
|
|
4
|
+
writeConfig,
|
|
5
|
+
updateAccountConfig,
|
|
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');
|
|
11
|
+
const { i18n } = require('@hubspot/cli-lib/lib/lang');
|
|
12
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
13
|
+
const {
|
|
14
|
+
updateConfigWithPersonalAccessKey,
|
|
15
|
+
} = require('@hubspot/cli-lib/personalAccessKey');
|
|
16
|
+
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
17
|
+
const { enterAccountNamePrompt } = require('./prompts/enterAccountNamePrompt');
|
|
18
|
+
const {
|
|
19
|
+
personalAccessKeyPrompt,
|
|
20
|
+
} = 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
|
+
const getSandboxType = type =>
|
|
29
|
+
type === 'DEVELOPER' ? 'development' : 'standard';
|
|
30
|
+
|
|
31
|
+
function getAccountName(config) {
|
|
32
|
+
const isSandbox =
|
|
33
|
+
config.sandboxAccountType && config.sandboxAccountType !== null;
|
|
34
|
+
const sandboxName = `[${getSandboxType(config.sandboxAccountType)} sandbox] `;
|
|
35
|
+
return `${config.name} ${isSandbox ? sandboxName : ''}(${config.portalId})`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Fetches available sync types for a given sandbox portal
|
|
39
|
+
async function getAvailableSyncTypes(parentAccountConfig, config) {
|
|
40
|
+
const parentPortalId = parentAccountConfig.portalId;
|
|
41
|
+
const portalId = config.portalId;
|
|
42
|
+
const syncTypes = await fetchTypes(parentPortalId, portalId);
|
|
43
|
+
return syncTypes.map(t => ({ type: t.name }));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const sandboxCreatePersonalAccessKeyFlow = async (env, account, name) => {
|
|
47
|
+
const configData = await personalAccessKeyPrompt({ env, account });
|
|
48
|
+
const updatedConfig = await updateConfigWithPersonalAccessKey(configData);
|
|
49
|
+
|
|
50
|
+
if (!updatedConfig) {
|
|
51
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let validName = updatedConfig.name;
|
|
55
|
+
|
|
56
|
+
if (!updatedConfig.name) {
|
|
57
|
+
const nameForConfig = name
|
|
58
|
+
.toLowerCase()
|
|
59
|
+
.split(' ')
|
|
60
|
+
.join('-');
|
|
61
|
+
const { name: promptName } = await enterAccountNamePrompt(nameForConfig);
|
|
62
|
+
validName = promptName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
updateAccountConfig({
|
|
66
|
+
...updatedConfig,
|
|
67
|
+
environment: updatedConfig.env,
|
|
68
|
+
tokenInfo: updatedConfig.auth.tokenInfo,
|
|
69
|
+
name: validName,
|
|
70
|
+
});
|
|
71
|
+
writeConfig();
|
|
72
|
+
|
|
73
|
+
const setAsDefault = await setAsDefaultAccountPrompt(validName);
|
|
74
|
+
|
|
75
|
+
logger.log('');
|
|
76
|
+
if (setAsDefault) {
|
|
77
|
+
logger.success(
|
|
78
|
+
i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.setAsDefaultAccount`, {
|
|
79
|
+
accountName: validName,
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
} else {
|
|
83
|
+
const config = getConfig();
|
|
84
|
+
logger.info(
|
|
85
|
+
i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.keepingCurrentDefault`, {
|
|
86
|
+
accountName: config.defaultPortal,
|
|
87
|
+
})
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
logger.success(
|
|
91
|
+
i18n('cli.commands.sandbox.subcommands.create.success.configFileUpdated', {
|
|
92
|
+
configFilename: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
|
|
93
|
+
authMethod: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
|
|
94
|
+
account: validName,
|
|
95
|
+
})
|
|
96
|
+
);
|
|
97
|
+
uiFeatureHighlight([
|
|
98
|
+
'accountsUseCommand',
|
|
99
|
+
'accountOption',
|
|
100
|
+
'accountsListCommand',
|
|
101
|
+
]);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const ACTIVE_TASK_POLL_INTERVAL = 1000;
|
|
105
|
+
|
|
106
|
+
const isTaskComplete = task => {
|
|
107
|
+
if (!task) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return task.status === 'COMPLETE';
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Returns a promise to poll a sync task with taskId. Interval runs until sync task status is equal to 'COMPLETE'
|
|
114
|
+
function pollSyncTaskStatus(accountId, taskId, syncStatusUrl) {
|
|
115
|
+
const i18nKey = 'cli.commands.sandbox.subcommands.sync.types';
|
|
116
|
+
const multibar = new cliProgress.MultiBar(
|
|
117
|
+
{
|
|
118
|
+
hideCursor: true,
|
|
119
|
+
format: '[{bar}] {percentage}% | {taskType}',
|
|
120
|
+
gracefulExit: true,
|
|
121
|
+
},
|
|
122
|
+
cliProgress.Presets.rect
|
|
123
|
+
);
|
|
124
|
+
const mergeTasks = {
|
|
125
|
+
'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.
|
|
126
|
+
};
|
|
127
|
+
const barInstances = {};
|
|
128
|
+
let pollInterval;
|
|
129
|
+
// Handle manual exit for return key and ctrl+c
|
|
130
|
+
const onTerminate = () => {
|
|
131
|
+
clearInterval(pollInterval);
|
|
132
|
+
multibar.stop();
|
|
133
|
+
logger.log('');
|
|
134
|
+
logger.log('Exiting, sync will continue in the background.');
|
|
135
|
+
logger.log('');
|
|
136
|
+
logger.log(
|
|
137
|
+
i18n('cli.commands.sandbox.subcommands.sync.info.syncStatus', {
|
|
138
|
+
url: syncStatusUrl,
|
|
139
|
+
})
|
|
140
|
+
);
|
|
141
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
142
|
+
};
|
|
143
|
+
handleExit(onTerminate);
|
|
144
|
+
handleKeypress(key => {
|
|
145
|
+
if (
|
|
146
|
+
(key && key.ctrl && key.name == 'c') ||
|
|
147
|
+
key.name === 'enter' ||
|
|
148
|
+
key.name === 'return'
|
|
149
|
+
) {
|
|
150
|
+
onTerminate();
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return new Promise((resolve, reject) => {
|
|
154
|
+
pollInterval = setInterval(async () => {
|
|
155
|
+
const taskResult = await fetchTaskStatus(accountId, taskId).catch(reject);
|
|
156
|
+
if (taskResult.tasks) {
|
|
157
|
+
// Array of sync tasks, eg: workflows, pipelines, object-schemas, etc. with each task containing a status of 'PENDING', 'IN_PROGRESS', 'COMPLETE', and 'FAILURE'
|
|
158
|
+
for (const task of taskResult.tasks) {
|
|
159
|
+
// For each sync task, show a progress bar and increment bar each time we run this interval until status is 'COMPLETE'
|
|
160
|
+
const taskType = task.type;
|
|
161
|
+
if (!barInstances[taskType] && !mergeTasks[taskType]) {
|
|
162
|
+
// skip creation of lead-flows bar because we're combining lead-flows into the forms bar
|
|
163
|
+
barInstances[taskType] = multibar.create(100, 0, {
|
|
164
|
+
taskType: i18n(`${i18nKey}.${taskType}.label`),
|
|
165
|
+
});
|
|
166
|
+
} else if (mergeTasks[taskType]) {
|
|
167
|
+
// If its a lead-flow, merge status into the forms progress bar
|
|
168
|
+
const formsTask = taskResult.tasks.filter(
|
|
169
|
+
t => t.type === mergeTasks[taskType]
|
|
170
|
+
)[0];
|
|
171
|
+
const formsTaskStatus = formsTask.status;
|
|
172
|
+
const leadFlowsTaskStatus = task.status;
|
|
173
|
+
if (
|
|
174
|
+
formsTaskStatus !== 'COMPLETE' ||
|
|
175
|
+
leadFlowsTaskStatus !== 'COMPLETE'
|
|
176
|
+
) {
|
|
177
|
+
barInstances[mergeTasks[taskType]].increment(
|
|
178
|
+
Math.floor(Math.random() * 3),
|
|
179
|
+
{
|
|
180
|
+
taskType: i18n(`${i18nKey}.${mergeTasks[taskType]}.label`),
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (barInstances[taskType] && task.status === 'COMPLETE') {
|
|
186
|
+
barInstances[taskType].update(100, {
|
|
187
|
+
taskType: i18n(`${i18nKey}.${taskType}.label`),
|
|
188
|
+
});
|
|
189
|
+
} else if (barInstances[taskType] && task.status === 'PROCESSING') {
|
|
190
|
+
// Do not increment for tasks still in PENDING state
|
|
191
|
+
barInstances[taskType].increment(Math.floor(Math.random() * 3), {
|
|
192
|
+
// Randomly increment bar by 0 - 2 while sync is in progress. Sandboxes currently does not have an accurate measurement for progress.
|
|
193
|
+
taskType: i18n(`${i18nKey}.${taskType}.label`),
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
clearInterval(pollInterval);
|
|
199
|
+
reject();
|
|
200
|
+
multibar.stop();
|
|
201
|
+
}
|
|
202
|
+
if (isTaskComplete(taskResult)) {
|
|
203
|
+
clearInterval(pollInterval);
|
|
204
|
+
resolve(taskResult);
|
|
205
|
+
multibar.stop();
|
|
206
|
+
}
|
|
207
|
+
}, ACTIVE_TASK_POLL_INTERVAL);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
module.exports = {
|
|
212
|
+
getSandboxType,
|
|
213
|
+
getAccountName,
|
|
214
|
+
getAvailableSyncTypes,
|
|
215
|
+
sandboxCreatePersonalAccessKeyFlow,
|
|
216
|
+
pollSyncTaskStatus,
|
|
217
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "4.1.7-beta.
|
|
3
|
+
"version": "4.1.7-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.7-beta.
|
|
12
|
-
"@hubspot/serverless-dev-runtime": "4.1.7-beta.
|
|
11
|
+
"@hubspot/cli-lib": "4.1.7-beta.2",
|
|
12
|
+
"@hubspot/serverless-dev-runtime": "4.1.7-beta.2",
|
|
13
13
|
"archiver": "^5.3.0",
|
|
14
14
|
"chalk": "^4.1.2",
|
|
15
|
+
"cli-progress": "^3.11.2",
|
|
15
16
|
"express": "^4.17.1",
|
|
16
17
|
"findup-sync": "^4.0.0",
|
|
17
18
|
"fs-extra": "^8.1.0",
|
|
@@ -37,5 +38,5 @@
|
|
|
37
38
|
"publishConfig": {
|
|
38
39
|
"access": "public"
|
|
39
40
|
},
|
|
40
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "ce9f106f0da7a39fbf14ff0ead2d5d750adee428"
|
|
41
42
|
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
const marketplace = require('@hubspot/cli-lib/api/marketplace');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
|
|
4
|
-
const ModuleDependencyValidator = require('../marketplaceValidators/module/ModuleDependencyValidator');
|
|
5
|
-
const { VALIDATION_RESULT } = require('../constants');
|
|
6
|
-
const { MODULE_PATH } = require('./validatorTestUtils');
|
|
7
|
-
|
|
8
|
-
jest.mock('@hubspot/cli-lib/api/marketplace');
|
|
9
|
-
|
|
10
|
-
const getMockDependencyResult = (customPaths = []) => {
|
|
11
|
-
const result = {
|
|
12
|
-
dependencies: [...customPaths],
|
|
13
|
-
};
|
|
14
|
-
return Promise.resolve(result);
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
describe('validators/marketplaceValidators/module/ModuleDependencyValidator', () => {
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
ModuleDependencyValidator.setRelativePath(MODULE_PATH);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('isExternalDep', () => {
|
|
23
|
-
beforeEach(() => {
|
|
24
|
-
ModuleDependencyValidator.setRelativePath(MODULE_PATH);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('returns true if dep is external to the provided absolute path', () => {
|
|
28
|
-
const isExternal = ModuleDependencyValidator.isExternalDep(
|
|
29
|
-
MODULE_PATH,
|
|
30
|
-
'SomeOtherFolder/In/The/DesignManager.css'
|
|
31
|
-
);
|
|
32
|
-
expect(isExternal).toBe(true);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('returns false if dep is not external to the provided absolute path', () => {
|
|
36
|
-
const isExternal = ModuleDependencyValidator.isExternalDep(
|
|
37
|
-
MODULE_PATH,
|
|
38
|
-
`${path.parse(MODULE_PATH).dir}/Internal/Folder/style.css`
|
|
39
|
-
);
|
|
40
|
-
expect(isExternal).toBe(false);
|
|
41
|
-
});
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
describe('validate', () => {
|
|
45
|
-
it('returns error if any referenced path is absolute', async () => {
|
|
46
|
-
marketplace.fetchModuleDependencies.mockReturnValue(
|
|
47
|
-
getMockDependencyResult(['/absolute/path'])
|
|
48
|
-
);
|
|
49
|
-
const validationErrors = await ModuleDependencyValidator.validate(
|
|
50
|
-
MODULE_PATH
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
expect(validationErrors.length).toBe(1);
|
|
54
|
-
expect(validationErrors[0].result).toBe(VALIDATION_RESULT.FATAL);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('returns error if any referenced path is external to the theme', async () => {
|
|
58
|
-
marketplace.fetchModuleDependencies.mockReturnValue(
|
|
59
|
-
getMockDependencyResult(['../../external/file-3.js'])
|
|
60
|
-
);
|
|
61
|
-
const validationErrors = await ModuleDependencyValidator.validate(
|
|
62
|
-
MODULE_PATH
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
expect(validationErrors.length).toBe(1);
|
|
66
|
-
expect(validationErrors[0].result).toBe(VALIDATION_RESULT.FATAL);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('returns no errors if paths are relative and internal', async () => {
|
|
70
|
-
marketplace.fetchModuleDependencies.mockReturnValue(
|
|
71
|
-
getMockDependencyResult(['module/style.css', 'module/another/test.js'])
|
|
72
|
-
);
|
|
73
|
-
const validationErrors = await ModuleDependencyValidator.validate(
|
|
74
|
-
MODULE_PATH
|
|
75
|
-
);
|
|
76
|
-
expect(validationErrors.length).toBe(0);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|