@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.
@@ -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 { getProjectConfig, pollDeployStatus } = require('../../lib/projects');
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 [--project] [--buildId]';
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
- if (!buildIdOption) {
63
- const { latestBuild, deployedBuildId } = await fetchProject(
64
- accountId,
65
- projectName
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 (!latestBuild || !latestBuild.buildId) {
68
- logger.error(i18n(`${i18nKey}.errors.noBuilds`));
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.statusCode === 404) {
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
- if (e.statusCode === 400) {
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.statusCode === 404) {
127
+ if (e.response && e.response.status === 404) {
128
128
  logger.error(`Project ${projectConfig.name} not found. `);
129
129
  } else {
130
130
  logApiErrorInstance(
@@ -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.statusCode === 404) {
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.ERROR);
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
- const publicApps = await fetchPublicAppOptions(accountId, accountName);
84
- const selectedApp = publicApps.find(a => a.id === appId);
85
- const appName = selectedApp ? selectedApp.name : 'app';
86
- if (!selectedApp) {
87
- logger.error(i18n(`${i18nKey}.errors.invalidAppId`, { appId }));
88
- process.exit(EXIT_CODES.ERROR);
89
- }
90
- if (selectedApp.listingInfo) {
91
- logger.error(i18n(`${i18nKey}.errors.invalidMarketplaceApp`, { appId }));
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
- const { name, location } = await createProjectPrompt('', options, true);
98
+ let projectName;
99
+ let projectLocation;
100
+ try {
101
+ const { name, location } = await createProjectPrompt('', options, true);
96
102
 
97
- const projectName = options.name || name;
98
- const projectLocation = options.location || location;
103
+ projectName = options.name || name;
104
+ projectLocation = options.location || location;
99
105
 
100
- const { projectExists } = await ensureProjectExists(accountId, projectName, {
101
- allowCreate: false,
102
- noLogs: true,
103
- });
104
-
105
- if (projectExists) {
106
- logger.error(
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
- if (error.errors) {
193
- error.errors.forEach(logApiErrorInstance);
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
  }
@@ -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 { uiFeatureHighlight, uiAccountDescription } = require('../../lib/ui');
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 = i18n(`${i18nKey}.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);
@@ -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);
@@ -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
- invalidAppId: "[--appId] Could not find appId {{ appId }}. Please choose another public app."
531
- invalidMarketplaceApp: "[--appId]: Could not migrate appId {{ appId }}. Marketplace apps cannot be migrated at this time. Please choose another public app."
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
- noApps: "{{#bold}}No apps to migrate{{/bold}}"
1223
- noAppsMessage: "The selected developer account {{#bold}}{{ accountName }}{{/bold}} doesn't have any apps that can be migrated to the projects framework."
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
- invalidAppId: "[--appId] Could not find appId {{ appId }}. Please choose another public app."
1226
- marketplaceApp: "Marketplace app"
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: "[--build] Deploy which build?"
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?"
@@ -298,16 +298,20 @@ class LocalDevManager {
298
298
 
299
299
  async checkPublicAppInstallation() {
300
300
  const {
301
- isInstalledWithScopeGroups: isInstalled,
301
+ isInstalledWithScopeGroups,
302
+ previouslyAuthorizedScopeGroups,
302
303
  } = await this.getActiveAppInstallationData();
303
304
 
304
- if (!isInstalled) {
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: 'crm card',
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
- const pollResp = await callback(accountId, taskId);
7
- const { status } = pollResp;
6
+ try {
7
+ const pollResp = await callback(accountId, taskId);
8
+ const { status } = pollResp;
8
9
 
9
- if (status === POLLING_STATUS.SUCCESS) {
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
- resolve(pollResp);
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 type = task[statusText.TYPE_KEY];
639
- if (type !== 'APP_ID' && type !== 'SERVERLESS_PKG') {
640
- acc[task.id] = task;
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
- const buildIdPrompt = (latestBuildId, deployedBuildId, projectName) => {
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: val => {
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
+ };