@hubspot/cli 5.2.1-beta.10 → 5.2.1-beta.11
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/functions/deploy.js +2 -2
- package/commands/project/__tests__/deploy.test.js +431 -0
- package/commands/project/cloneApp.js +161 -0
- package/commands/project/deploy.js +75 -21
- package/commands/project/listBuilds.js +1 -1
- package/commands/project/logs.js +1 -1
- package/commands/project/migrateApp.js +43 -26
- package/commands/project.js +2 -0
- package/commands/sandbox/create.js +6 -2
- package/commands/sandbox/delete.js +5 -2
- package/commands/sandbox/sync.js +2 -2
- package/commands/sandbox.js +2 -1
- package/lang/en.lyaml +36 -11
- package/lib/LocalDevManager.js +7 -3
- package/lib/constants.js +2 -1
- package/lib/polling.js +16 -11
- package/lib/projects.js +3 -3
- package/lib/prompts/buildIdPrompt.js +7 -16
- package/lib/prompts/cloneAppLocationPrompt.js +32 -0
- package/lib/prompts/installPublicAppPrompt.js +13 -4
- package/lib/prompts/selectPublicAppPrompt.js +23 -8
- package/lib/serverlessLogs.js +2 -2
- package/package.json +5 -4
|
@@ -16,20 +16,53 @@ const {
|
|
|
16
16
|
fetchProject,
|
|
17
17
|
} = require('@hubspot/local-dev-lib/api/projects');
|
|
18
18
|
const { loadAndValidateOptions } = require('../../lib/validation');
|
|
19
|
-
const {
|
|
19
|
+
const {
|
|
20
|
+
getProjectConfig,
|
|
21
|
+
pollDeployStatus,
|
|
22
|
+
getProjectDetailUrl,
|
|
23
|
+
} = require('../../lib/projects');
|
|
20
24
|
const { projectNamePrompt } = require('../../lib/prompts/projectNamePrompt');
|
|
21
25
|
const { buildIdPrompt } = require('../../lib/prompts/buildIdPrompt');
|
|
22
26
|
const { i18n } = require('../../lib/lang');
|
|
23
|
-
const { uiBetaTag } = require('../../lib/ui');
|
|
27
|
+
const { uiBetaTag, uiLink } = require('../../lib/ui');
|
|
24
28
|
const { getAccountConfig } = require('@hubspot/local-dev-lib/config');
|
|
25
29
|
|
|
26
30
|
const i18nKey = 'commands.project.subcommands.deploy';
|
|
27
31
|
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
28
32
|
const { uiCommandReference, uiAccountDescription } = require('../../lib/ui');
|
|
29
33
|
|
|
30
|
-
exports.command = 'deploy
|
|
34
|
+
exports.command = 'deploy';
|
|
31
35
|
exports.describe = uiBetaTag(i18n(`${i18nKey}.describe`), false);
|
|
32
36
|
|
|
37
|
+
const validateBuildId = (
|
|
38
|
+
buildId,
|
|
39
|
+
deployedBuildId,
|
|
40
|
+
latestBuildId,
|
|
41
|
+
projectName,
|
|
42
|
+
accountId
|
|
43
|
+
) => {
|
|
44
|
+
if (Number(buildId) > latestBuildId) {
|
|
45
|
+
return i18n(`${i18nKey}.errors.buildIdDoesNotExist`, {
|
|
46
|
+
buildId: buildId,
|
|
47
|
+
projectName,
|
|
48
|
+
linkToProject: uiLink(
|
|
49
|
+
i18n(`${i18nKey}.errors.viewProjectsBuilds`),
|
|
50
|
+
getProjectDetailUrl(projectName, accountId)
|
|
51
|
+
),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
if (Number(buildId) === deployedBuildId) {
|
|
55
|
+
return i18n(`${i18nKey}.errors.buildAlreadyDeployed`, {
|
|
56
|
+
buildId: buildId,
|
|
57
|
+
linkToProject: uiLink(
|
|
58
|
+
i18n(`${i18nKey}.errors.viewProjectsBuilds`),
|
|
59
|
+
getProjectDetailUrl(projectName, accountId)
|
|
60
|
+
),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
};
|
|
65
|
+
|
|
33
66
|
exports.handler = async options => {
|
|
34
67
|
await loadAndValidateOptions(options);
|
|
35
68
|
|
|
@@ -59,27 +92,48 @@ exports.handler = async options => {
|
|
|
59
92
|
let buildIdToDeploy = buildIdOption;
|
|
60
93
|
|
|
61
94
|
try {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
95
|
+
const { latestBuild, deployedBuildId } = await fetchProject(
|
|
96
|
+
accountId,
|
|
97
|
+
projectName
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (!latestBuild || !latestBuild.buildId) {
|
|
101
|
+
logger.error(i18n(`${i18nKey}.errors.noBuilds`));
|
|
102
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (buildIdToDeploy) {
|
|
106
|
+
const validationResult = validateBuildId(
|
|
107
|
+
buildIdToDeploy,
|
|
108
|
+
deployedBuildId,
|
|
109
|
+
latestBuild.buildId,
|
|
110
|
+
projectName,
|
|
111
|
+
accountId
|
|
66
112
|
);
|
|
67
|
-
if (
|
|
68
|
-
logger.error(
|
|
69
|
-
process.exit(EXIT_CODES.ERROR);
|
|
113
|
+
if (validationResult !== true) {
|
|
114
|
+
logger.error(validationResult);
|
|
115
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
70
116
|
}
|
|
117
|
+
} else {
|
|
71
118
|
const buildIdPromptResponse = await buildIdPrompt(
|
|
72
119
|
latestBuild.buildId,
|
|
73
120
|
deployedBuildId,
|
|
74
|
-
projectName
|
|
121
|
+
projectName,
|
|
122
|
+
buildId =>
|
|
123
|
+
validateBuildId(
|
|
124
|
+
buildId,
|
|
125
|
+
latestBuild.buildId,
|
|
126
|
+
deployedBuildId,
|
|
127
|
+
projectName,
|
|
128
|
+
accountId
|
|
129
|
+
)
|
|
75
130
|
);
|
|
76
|
-
|
|
77
131
|
buildIdToDeploy = buildIdPromptResponse.buildId;
|
|
78
132
|
}
|
|
79
133
|
|
|
80
134
|
if (!buildIdToDeploy) {
|
|
81
135
|
logger.error(i18n(`${i18nKey}.errors.noBuildId`));
|
|
82
|
-
process.exit(EXIT_CODES.ERROR);
|
|
136
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
83
137
|
}
|
|
84
138
|
|
|
85
139
|
const deployResp = await deployProject(
|
|
@@ -88,13 +142,13 @@ exports.handler = async options => {
|
|
|
88
142
|
buildIdToDeploy
|
|
89
143
|
);
|
|
90
144
|
|
|
91
|
-
if (deployResp.error) {
|
|
145
|
+
if (!deployResp || deployResp.error) {
|
|
92
146
|
logger.error(
|
|
93
147
|
i18n(`${i18nKey}.errors.deploy`, {
|
|
94
148
|
details: deployResp.error.message,
|
|
95
149
|
})
|
|
96
150
|
);
|
|
97
|
-
return;
|
|
151
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
98
152
|
}
|
|
99
153
|
|
|
100
154
|
await pollDeployStatus(
|
|
@@ -104,7 +158,7 @@ exports.handler = async options => {
|
|
|
104
158
|
buildIdToDeploy
|
|
105
159
|
);
|
|
106
160
|
} catch (e) {
|
|
107
|
-
if (e.
|
|
161
|
+
if (e.response && e.response.status === 404) {
|
|
108
162
|
logger.error(
|
|
109
163
|
i18n(`${i18nKey}.errors.projectNotFound`, {
|
|
110
164
|
projectName: chalk.bold(projectName),
|
|
@@ -112,13 +166,12 @@ exports.handler = async options => {
|
|
|
112
166
|
command: uiCommandReference('hs project upload'),
|
|
113
167
|
})
|
|
114
168
|
);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
logger.error(e.error.message);
|
|
169
|
+
} else if (e.response && e.response.status === 400) {
|
|
170
|
+
logger.error(e.message);
|
|
118
171
|
} else {
|
|
119
172
|
logApiErrorInstance(e, new ApiErrorContext({ accountId, projectName }));
|
|
120
173
|
}
|
|
121
|
-
process.exit(EXIT_CODES.ERROR);
|
|
174
|
+
return process.exit(EXIT_CODES.ERROR);
|
|
122
175
|
}
|
|
123
176
|
};
|
|
124
177
|
|
|
@@ -129,13 +182,14 @@ exports.builder = yargs => {
|
|
|
129
182
|
type: 'string',
|
|
130
183
|
},
|
|
131
184
|
buildId: {
|
|
185
|
+
alias: ['build'],
|
|
132
186
|
describe: i18n(`${i18nKey}.options.buildId.describe`),
|
|
133
187
|
type: 'number',
|
|
134
188
|
},
|
|
135
189
|
});
|
|
136
190
|
|
|
137
|
-
yargs.example([['$0 project deploy', i18n(`${i18nKey}.examples.default`)]]);
|
|
138
191
|
yargs.example([
|
|
192
|
+
['$0 project deploy', i18n(`${i18nKey}.examples.default`)],
|
|
139
193
|
[
|
|
140
194
|
'$0 project deploy --project="my-project" --buildId=5',
|
|
141
195
|
i18n(`${i18nKey}.examples.withOptions`),
|
|
@@ -124,7 +124,7 @@ exports.handler = async options => {
|
|
|
124
124
|
|
|
125
125
|
await fetchAndDisplayBuilds(project, { limit });
|
|
126
126
|
} catch (e) {
|
|
127
|
-
if (e.
|
|
127
|
+
if (e.response && e.response.status === 404) {
|
|
128
128
|
logger.error(`Project ${projectConfig.name} not found. `);
|
|
129
129
|
} else {
|
|
130
130
|
logApiErrorInstance(
|
package/commands/project/logs.js
CHANGED
|
@@ -49,7 +49,7 @@ const getPrivateAppsUrl = accountId => {
|
|
|
49
49
|
// We currently cannot fetch logs directly to the CLI. See internal CLI issue #413 for more information.
|
|
50
50
|
|
|
51
51
|
// const handleLogsError = (e, name, projectName) => {
|
|
52
|
-
// if (e.
|
|
52
|
+
// if (e.response && e.response.status === 404) {
|
|
53
53
|
// logger.debug(`Log fetch error: ${e.message}`);
|
|
54
54
|
// logger.log(i18n(`${i18nKey}.logs.noLogsFound`, { name }));
|
|
55
55
|
// } else {
|
|
@@ -12,7 +12,6 @@ const {
|
|
|
12
12
|
} = require('../../lib/prompts/createProjectPrompt');
|
|
13
13
|
const { i18n } = require('../../lib/lang');
|
|
14
14
|
const {
|
|
15
|
-
fetchPublicAppOptions,
|
|
16
15
|
selectPublicAppPrompt,
|
|
17
16
|
} = require('../../lib/prompts/selectPublicAppPrompt');
|
|
18
17
|
const { poll } = require('../../lib/polling');
|
|
@@ -43,6 +42,9 @@ const { getAccountConfig } = require('@hubspot/local-dev-lib/config');
|
|
|
43
42
|
const { downloadProject } = require('@hubspot/local-dev-lib/api/projects');
|
|
44
43
|
const { extractZipArchive } = require('@hubspot/local-dev-lib/archive');
|
|
45
44
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
|
|
45
|
+
const {
|
|
46
|
+
fetchPublicAppMetadata,
|
|
47
|
+
} = require('@hubspot/local-dev-lib/api/appsDev');
|
|
46
48
|
|
|
47
49
|
const i18nKey = 'commands.project.subcommands.migrateApp';
|
|
48
50
|
|
|
@@ -68,7 +70,7 @@ exports.handler = async options => {
|
|
|
68
70
|
})
|
|
69
71
|
);
|
|
70
72
|
uiLine();
|
|
71
|
-
process.exit(EXIT_CODES.
|
|
73
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
const { appId } =
|
|
@@ -80,34 +82,46 @@ exports.handler = async options => {
|
|
|
80
82
|
migrateApp: true,
|
|
81
83
|
});
|
|
82
84
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
let appName;
|
|
86
|
+
try {
|
|
87
|
+
const selectedApp = await fetchPublicAppMetadata(appId, accountId);
|
|
88
|
+
if (selectedApp.preventProjectMigrations) {
|
|
89
|
+
logger.error(i18n(`${i18nKey}.errors.invalidApp`, { appId }));
|
|
90
|
+
process.exit(EXIT_CODES.ERROR);
|
|
91
|
+
}
|
|
92
|
+
appName = selectedApp.name;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
logApiErrorInstance(error, new ApiErrorContext({ accountId }));
|
|
92
95
|
process.exit(EXIT_CODES.ERROR);
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
|
|
98
|
+
let projectName;
|
|
99
|
+
let projectLocation;
|
|
100
|
+
try {
|
|
101
|
+
const { name, location } = await createProjectPrompt('', options, true);
|
|
96
102
|
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
projectName = options.name || name;
|
|
104
|
+
projectLocation = options.location || location;
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
i18n(`${i18nKey}.errors.projectAlreadyExists`, {
|
|
108
|
-
projectName,
|
|
109
|
-
})
|
|
106
|
+
const { projectExists } = await ensureProjectExists(
|
|
107
|
+
accountId,
|
|
108
|
+
projectName,
|
|
109
|
+
{
|
|
110
|
+
allowCreate: false,
|
|
111
|
+
noLogs: true,
|
|
112
|
+
}
|
|
110
113
|
);
|
|
114
|
+
|
|
115
|
+
if (projectExists) {
|
|
116
|
+
logger.error(
|
|
117
|
+
i18n(`${i18nKey}.errors.projectAlreadyExists`, {
|
|
118
|
+
projectName,
|
|
119
|
+
})
|
|
120
|
+
);
|
|
121
|
+
process.exit(EXIT_CODES.ERROR);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logApiErrorInstance(error, new ApiErrorContext({ accountId }));
|
|
111
125
|
process.exit(EXIT_CODES.ERROR);
|
|
112
126
|
}
|
|
113
127
|
|
|
@@ -189,8 +203,11 @@ exports.handler = async options => {
|
|
|
189
203
|
text: i18n(`${i18nKey}.migrationStatus.failure`),
|
|
190
204
|
failColor: 'white',
|
|
191
205
|
});
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
// Migrations endpoints return a response object with an errors property. The errors property contains an array of errors.
|
|
207
|
+
if (error.errors && Array.isArray(error.errors)) {
|
|
208
|
+
error.errors.forEach(e =>
|
|
209
|
+
logApiErrorInstance(e, new ApiErrorContext({ accountId }))
|
|
210
|
+
);
|
|
194
211
|
} else {
|
|
195
212
|
logApiErrorInstance(error, new ApiErrorContext({ accountId }));
|
|
196
213
|
}
|
package/commands/project.js
CHANGED
|
@@ -12,6 +12,7 @@ const open = require('./project/open');
|
|
|
12
12
|
const dev = require('./project/dev');
|
|
13
13
|
const add = require('./project/add');
|
|
14
14
|
const migrateApp = require('./project/migrateApp');
|
|
15
|
+
const cloneApp = require('./project/cloneApp');
|
|
15
16
|
|
|
16
17
|
const i18nKey = 'commands.project';
|
|
17
18
|
|
|
@@ -34,6 +35,7 @@ exports.builder = yargs => {
|
|
|
34
35
|
yargs.command(download).demandCommand(0, '');
|
|
35
36
|
yargs.command(open).demandCommand(0, '');
|
|
36
37
|
yargs.command(migrateApp).demandCommand(0, '');
|
|
38
|
+
yargs.command(cloneApp).demandCommand(0, '');
|
|
37
39
|
|
|
38
40
|
return yargs;
|
|
39
41
|
};
|
|
@@ -9,7 +9,11 @@ const { loadAndValidateOptions } = require('../../lib/validation');
|
|
|
9
9
|
const { i18n } = require('../../lib/lang');
|
|
10
10
|
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
11
11
|
const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
|
|
12
|
-
const {
|
|
12
|
+
const {
|
|
13
|
+
uiFeatureHighlight,
|
|
14
|
+
uiAccountDescription,
|
|
15
|
+
uiBetaTag,
|
|
16
|
+
} = require('../../lib/ui');
|
|
13
17
|
const {
|
|
14
18
|
sandboxTypeMap,
|
|
15
19
|
getAvailableSyncTypes,
|
|
@@ -42,7 +46,7 @@ const {
|
|
|
42
46
|
const i18nKey = 'commands.sandbox.subcommands.create';
|
|
43
47
|
|
|
44
48
|
exports.command = 'create [--name] [--type]';
|
|
45
|
-
exports.describe = i18n(`${i18nKey}.describe`);
|
|
49
|
+
exports.describe = uiBetaTag(i18n(`${i18nKey}.describe`), false);
|
|
46
50
|
|
|
47
51
|
exports.handler = async options => {
|
|
48
52
|
await loadAndValidateOptions(options);
|
|
@@ -33,12 +33,15 @@ const { promptUser } = require('../../lib/prompts/promptUtils');
|
|
|
33
33
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
|
|
34
34
|
|
|
35
35
|
const { getValidEnv } = require('@hubspot/local-dev-lib/environment');
|
|
36
|
-
const { uiAccountDescription } = require('../../lib/ui');
|
|
36
|
+
const { uiAccountDescription, uiBetaTag } = require('../../lib/ui');
|
|
37
37
|
|
|
38
38
|
const i18nKey = 'commands.sandbox.subcommands.delete';
|
|
39
39
|
|
|
40
40
|
exports.command = 'delete [--account]';
|
|
41
|
-
exports.describe =
|
|
41
|
+
exports.describe = exports.describe = uiBetaTag(
|
|
42
|
+
i18n(`${i18nKey}.describe`),
|
|
43
|
+
false
|
|
44
|
+
);
|
|
42
45
|
|
|
43
46
|
exports.handler = async options => {
|
|
44
47
|
await loadAndValidateOptions(options, false);
|
package/commands/sandbox/sync.js
CHANGED
|
@@ -13,7 +13,7 @@ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
|
13
13
|
const { getAccountConfig, getEnv } = require('@hubspot/local-dev-lib/config');
|
|
14
14
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
|
|
15
15
|
const { promptUser } = require('../../lib/prompts/promptUtils');
|
|
16
|
-
const { uiLine, uiAccountDescription } = require('../../lib/ui');
|
|
16
|
+
const { uiLine, uiAccountDescription, uiBetaTag } = require('../../lib/ui');
|
|
17
17
|
const {
|
|
18
18
|
isSandbox,
|
|
19
19
|
isStandardSandbox,
|
|
@@ -35,7 +35,7 @@ const {
|
|
|
35
35
|
const i18nKey = 'commands.sandbox.subcommands.sync';
|
|
36
36
|
|
|
37
37
|
exports.command = 'sync';
|
|
38
|
-
exports.describe = i18n(`${i18nKey}.describe`);
|
|
38
|
+
exports.describe = uiBetaTag(i18n(`${i18nKey}.describe`), false);
|
|
39
39
|
|
|
40
40
|
exports.handler = async options => {
|
|
41
41
|
await loadAndValidateOptions(options);
|
package/commands/sandbox.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const { addConfigOptions, addAccountOptions } = require('../lib/commonOpts');
|
|
2
2
|
const { i18n } = require('../lib/lang');
|
|
3
|
+
const { uiBetaTag } = require('../lib/ui');
|
|
3
4
|
const create = require('./sandbox/create');
|
|
4
5
|
const del = require('./sandbox/delete');
|
|
5
6
|
const sync = require('./sandbox/sync');
|
|
@@ -7,7 +8,7 @@ const sync = require('./sandbox/sync');
|
|
|
7
8
|
const i18nKey = 'commands.sandbox';
|
|
8
9
|
|
|
9
10
|
exports.command = 'sandbox';
|
|
10
|
-
exports.describe = i18n(`${i18nKey}.describe`);
|
|
11
|
+
exports.describe = uiBetaTag(i18n(`${i18nKey}.describe`), false);
|
|
11
12
|
|
|
12
13
|
exports.builder = yargs => {
|
|
13
14
|
addConfigOptions(yargs);
|
package/lang/en.lyaml
CHANGED
|
@@ -527,8 +527,24 @@ en:
|
|
|
527
527
|
invalidAccountTypeTitle: "{{#bold}}Developer account not targeted{{/bold}}"
|
|
528
528
|
invalidAccountTypeDescription: "Only public apps created in a developer account can be converted to a project component. Select a connected developer account with {{useCommand}} or {{authCommand}} and try again."
|
|
529
529
|
projectAlreadyExists: "A project with name {{ projectName }} already exists. Please choose another name."
|
|
530
|
-
|
|
531
|
-
|
|
530
|
+
invalidApp: "Could not migrate appId {{ appId }}. This app cannot be migrated at this time. Please choose another public app."
|
|
531
|
+
cloneApp:
|
|
532
|
+
describe: "Clone a public app using the projects framework"
|
|
533
|
+
examples:
|
|
534
|
+
default: "Clone a public app using the projects framework"
|
|
535
|
+
options:
|
|
536
|
+
appId:
|
|
537
|
+
describe: "The ID for the public app being cloned"
|
|
538
|
+
location:
|
|
539
|
+
describe: "Directory where project should be created"
|
|
540
|
+
cloneStatus:
|
|
541
|
+
inProgress: "Cloning app configuration to {{#bold}}public-app.json{{/bold}} component definition ..."
|
|
542
|
+
done: "Cloning app configuration to public-app.json component definition ... DONE"
|
|
543
|
+
success: "Your cloned project was created in {{ location }}"
|
|
544
|
+
failure: "Cloning app configuration to public-app.json component definition ... FAILED"
|
|
545
|
+
errors:
|
|
546
|
+
invalidAccountTypeTitle: "{{#bold}}Developer account not targeted{{/bold}}"
|
|
547
|
+
invalidAccountTypeDescription: "Only public apps created in a developer account can be converted to a project component. Select a connected developer account with {{useCommand}} or {{authCommand}} and try again."
|
|
532
548
|
add:
|
|
533
549
|
describe: "Create a new component within a project"
|
|
534
550
|
options:
|
|
@@ -552,7 +568,10 @@ en:
|
|
|
552
568
|
deploy: "Deploy error: {{ details }}"
|
|
553
569
|
noBuilds: "Deploy error: no builds for this project were found."
|
|
554
570
|
noBuildId: "You must specify a build to deploy"
|
|
555
|
-
projectNotFound: "{{ projectName }} does not exist in account {{ accountIdentifier }}. Run {{ command }} to upload your project files to HubSpot."
|
|
571
|
+
projectNotFound: "The project {{ projectName }} does not exist in account {{ accountIdentifier }}. Run {{ command }} to upload your project files to HubSpot."
|
|
572
|
+
buildIdDoesNotExist: "Build {{ buildId }} does not exist for project {{ projectName }}. {{ linkToProject }}"
|
|
573
|
+
buildAlreadyDeployed: "Build {{ buildId }} is already deployed. {{ linkToProject}}"
|
|
574
|
+
viewProjectsBuilds: 'View project builds in HubSpot'
|
|
556
575
|
examples:
|
|
557
576
|
default: "Deploy the latest build of the current project"
|
|
558
577
|
withOptions: "Deploy build 5 of the project my-project"
|
|
@@ -1219,11 +1238,18 @@ en:
|
|
|
1219
1238
|
selectAppIdMigrate: "[--appId] Choose an app under {{ accountName }} to migrate:"
|
|
1220
1239
|
selectAppIdClone: "[--appId] Choose an app under {{ accountName }} to clone:"
|
|
1221
1240
|
errors:
|
|
1222
|
-
|
|
1223
|
-
|
|
1241
|
+
noAppsMigration: "{{#bold}}No apps to migrate{{/bold}}"
|
|
1242
|
+
noAppsClone: "{{#bold}}No apps to clone{{/bold}}"
|
|
1243
|
+
noAppsMigrationMessage: "The selected developer account {{#bold}}{{ accountName }}{{/bold}} doesn't have any apps that can be migrated to the projects framework."
|
|
1244
|
+
noAppsCloneMessage: "The selected developer account {{#bold}}{{ accountName }}{{/bold}} doesn't have any apps that can be cloned to the projects framework."
|
|
1224
1245
|
errorFetchingApps: "There was an error fetching public apps."
|
|
1225
|
-
|
|
1226
|
-
|
|
1246
|
+
cannotBeMigrated: "Cannot be migrated"
|
|
1247
|
+
cloneAppLocationPrompt:
|
|
1248
|
+
enterLocation: "[--location] Where should the cloned project be created?"
|
|
1249
|
+
errors:
|
|
1250
|
+
locationRequired: "A project location is required"
|
|
1251
|
+
invalidLocation: "The selected destination already exists. Please provide a new path for this project."
|
|
1252
|
+
invalidCharacters: "The selected destination contains invalid characters. Please provide a new path and try again."
|
|
1227
1253
|
downloadProjectPrompt:
|
|
1228
1254
|
selectProject: "Select a project to download:"
|
|
1229
1255
|
errors:
|
|
@@ -1265,10 +1291,7 @@ en:
|
|
|
1265
1291
|
bugPrompt: "Create an issue on Github in your browser?"
|
|
1266
1292
|
generalPrompt: "Create an issue on Github in your browser?"
|
|
1267
1293
|
buildIdPrompt:
|
|
1268
|
-
enterBuildId: "[--
|
|
1269
|
-
errors:
|
|
1270
|
-
buildIdDoesNotExist: "Build {{ buildId }} does not exist for project {{ projectName }}."
|
|
1271
|
-
buildAlreadyDeployed: "Build {{ buildId }} is already deployed."
|
|
1294
|
+
enterBuildId: "[--buildId] Deploy which build?"
|
|
1272
1295
|
previewPrompt:
|
|
1273
1296
|
enterSrc: "[--src] Enter a local theme directory to preview."
|
|
1274
1297
|
enterDest: "[--dest] Enter the destination path for the src theme in HubSpot Design Tools."
|
|
@@ -1280,7 +1303,9 @@ en:
|
|
|
1280
1303
|
message: "You are about to remove any remote files in \"{{ filePath }}\" on HubSpot account {{ accountId }} that don't exist locally. Are you sure you want to do this?"
|
|
1281
1304
|
installPublicAppPrompt:
|
|
1282
1305
|
explanation: "Local development requires this app to be installed in the target test account"
|
|
1306
|
+
reinstallExplanation: "This app's required scopes have been updated since it was last installed on the target test account. To avoid issues with local development, we recommend reinstalling the app with the updated scopes."
|
|
1283
1307
|
prompt: "Open hubspot.com to install this app?"
|
|
1308
|
+
reinstallPrompt: "Open hubspot.com to reinstall this app?"
|
|
1284
1309
|
decline: "To continue local development of this app, install it in your target test account and re-run {{#bold}}`hs project dev`{{/bold}}"
|
|
1285
1310
|
activeInstallConfirmationPrompt:
|
|
1286
1311
|
message: "Proceed with local development of this {{#bold}}production{{/bold}} app?"
|
package/lib/LocalDevManager.js
CHANGED
|
@@ -298,16 +298,20 @@ class LocalDevManager {
|
|
|
298
298
|
|
|
299
299
|
async checkPublicAppInstallation() {
|
|
300
300
|
const {
|
|
301
|
-
isInstalledWithScopeGroups
|
|
301
|
+
isInstalledWithScopeGroups,
|
|
302
|
+
previouslyAuthorizedScopeGroups,
|
|
302
303
|
} = await this.getActiveAppInstallationData();
|
|
303
304
|
|
|
304
|
-
|
|
305
|
+
const isReinstall = previouslyAuthorizedScopeGroups.length > 0;
|
|
306
|
+
|
|
307
|
+
if (!isInstalledWithScopeGroups) {
|
|
305
308
|
await installPublicAppPrompt(
|
|
306
309
|
this.env,
|
|
307
310
|
this.targetAccountId,
|
|
308
311
|
this.activePublicAppData.clientId,
|
|
309
312
|
this.activeApp.config.auth.requiredScopes,
|
|
310
|
-
this.activeApp.config.auth.redirectUrls
|
|
313
|
+
this.activeApp.config.auth.redirectUrls,
|
|
314
|
+
isReinstall
|
|
311
315
|
);
|
|
312
316
|
}
|
|
313
317
|
}
|
package/lib/constants.js
CHANGED
|
@@ -66,8 +66,9 @@ const PROJECT_ERROR_TYPES = {
|
|
|
66
66
|
};
|
|
67
67
|
const PROJECT_TASK_TYPES = {
|
|
68
68
|
PRIVATE_APP: 'private app',
|
|
69
|
+
PUBLIC_APP: 'public app',
|
|
69
70
|
APP_FUNCTION: 'function',
|
|
70
|
-
CRM_CARD_V2: '
|
|
71
|
+
CRM_CARD_V2: 'card',
|
|
71
72
|
};
|
|
72
73
|
const PROJECT_COMPONENT_TYPES = {
|
|
73
74
|
PROJECTS: 'projects',
|
package/lib/polling.js
CHANGED
|
@@ -3,19 +3,24 @@ const { POLLING_DELAY, POLLING_STATUS } = require('./constants');
|
|
|
3
3
|
const poll = (callback, accountId, taskId) => {
|
|
4
4
|
return new Promise((resolve, reject) => {
|
|
5
5
|
const pollInterval = setInterval(async () => {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
try {
|
|
7
|
+
const pollResp = await callback(accountId, taskId);
|
|
8
|
+
const { status } = pollResp;
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
if (status === POLLING_STATUS.SUCCESS) {
|
|
11
|
+
clearInterval(pollInterval);
|
|
12
|
+
resolve(pollResp);
|
|
13
|
+
} else if (
|
|
14
|
+
status === POLLING_STATUS.ERROR ||
|
|
15
|
+
status === POLLING_STATUS.REVERTED ||
|
|
16
|
+
status === POLLING_STATUS.FAILURE
|
|
17
|
+
) {
|
|
18
|
+
clearInterval(pollInterval);
|
|
19
|
+
reject(pollResp);
|
|
20
|
+
}
|
|
21
|
+
} catch (error) {
|
|
10
22
|
clearInterval(pollInterval);
|
|
11
|
-
|
|
12
|
-
} else if (
|
|
13
|
-
status === POLLING_STATUS.ERROR ||
|
|
14
|
-
status === POLLING_STATUS.REVERTED ||
|
|
15
|
-
status === POLLING_STATUS.FAILURE
|
|
16
|
-
) {
|
|
17
|
-
clearInterval(pollInterval);
|
|
18
|
-
reject(pollResp);
|
|
23
|
+
reject(error);
|
|
19
24
|
}
|
|
20
25
|
}, POLLING_DELAY);
|
|
21
26
|
});
|
package/lib/projects.js
CHANGED
|
@@ -635,9 +635,9 @@ const makePollTaskStatusFunc = ({
|
|
|
635
635
|
|
|
636
636
|
const tasksById = initialTaskStatus[statusText.SUBTASK_KEY].reduce(
|
|
637
637
|
(acc, task) => {
|
|
638
|
-
const
|
|
639
|
-
if (
|
|
640
|
-
acc[
|
|
638
|
+
const { id, visible } = task;
|
|
639
|
+
if (visible) {
|
|
640
|
+
acc[id] = task;
|
|
641
641
|
}
|
|
642
642
|
return acc;
|
|
643
643
|
},
|
|
@@ -2,8 +2,12 @@ const { promptUser } = require('./promptUtils');
|
|
|
2
2
|
const { i18n } = require('../lang');
|
|
3
3
|
|
|
4
4
|
const i18nKey = 'lib.prompts.buildIdPrompt';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
const buildIdPrompt = (
|
|
6
|
+
latestBuildId,
|
|
7
|
+
deployedBuildId,
|
|
8
|
+
projectName,
|
|
9
|
+
validate
|
|
10
|
+
) => {
|
|
7
11
|
return promptUser({
|
|
8
12
|
name: 'buildId',
|
|
9
13
|
message: i18n(`${i18nKey}.enterBuildId`),
|
|
@@ -13,20 +17,7 @@ const buildIdPrompt = (latestBuildId, deployedBuildId, projectName) => {
|
|
|
13
17
|
}
|
|
14
18
|
return latestBuildId;
|
|
15
19
|
},
|
|
16
|
-
validate
|
|
17
|
-
if (Number(val) > latestBuildId) {
|
|
18
|
-
return i18n(`${i18nKey}.errors.buildIdDoesNotExist`, {
|
|
19
|
-
buildId: val,
|
|
20
|
-
projectName,
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
if (Number(val) === deployedBuildId) {
|
|
24
|
-
return i18n(`${i18nKey}.errors.buildAlreadyDeployed`, {
|
|
25
|
-
buildId: val,
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
return true;
|
|
29
|
-
},
|
|
20
|
+
validate,
|
|
30
21
|
});
|
|
31
22
|
};
|
|
32
23
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { promptUser } = require('./promptUtils');
|
|
4
|
+
const { i18n } = require('../lang');
|
|
5
|
+
const { getCwd, isValidPath } = require('@hubspot/local-dev-lib/path');
|
|
6
|
+
|
|
7
|
+
const i18nKey = 'lib.prompts.cloneAppLocationPrompt';
|
|
8
|
+
|
|
9
|
+
const cloneAppLocationPrompt = (promptOptions, appName) => {
|
|
10
|
+
return promptUser({
|
|
11
|
+
name: 'location',
|
|
12
|
+
message: i18n(`${i18nKey}.enterLocation`),
|
|
13
|
+
when: !promptOptions.location,
|
|
14
|
+
default: path.resolve(getCwd(), appName),
|
|
15
|
+
validate: input => {
|
|
16
|
+
if (!input) {
|
|
17
|
+
return i18n(`${i18nKey}.errors.locationRequired`);
|
|
18
|
+
}
|
|
19
|
+
if (fs.existsSync(input)) {
|
|
20
|
+
return i18n(`${i18nKey}.errors.invalidLocation`);
|
|
21
|
+
}
|
|
22
|
+
if (!isValidPath(input)) {
|
|
23
|
+
return i18n(`${i18nKey}.errors.invalidCharacters`);
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
module.exports = {
|
|
31
|
+
cloneAppLocationPrompt,
|
|
32
|
+
};
|