@hubspot/cli 5.2.1-beta.5 → 5.2.1-beta.7

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/bin/hs CHANGED
@@ -1,3 +1,3 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node --no-deprecation
2
2
 
3
3
  require('./cli');
@@ -9,12 +9,7 @@ const {
9
9
  } = require('../../lib/commonOpts');
10
10
  const { trackCommandUsage } = require('../../lib/usageTracking');
11
11
  const { loadAndValidateOptions } = require('../../lib/validation');
12
- const { getSandboxName } = require('../../lib/sandboxes');
13
- const {
14
- isSandbox,
15
- isDeveloperTestAccount,
16
- isAppDeveloperAccount,
17
- } = require('../../lib/accountTypes');
12
+ const { isSandbox, isDeveloperTestAccount } = require('../../lib/accountTypes');
18
13
 
19
14
  const { i18n } = require('../../lib/lang');
20
15
  const {
@@ -63,23 +58,17 @@ const getPortalData = mappedPortalData => {
63
58
  p => p.portalId === parseInt(key, 10)
64
59
  )[0];
65
60
  set.forEach(portal => {
66
- let name = portal.name;
61
+ let name = `${portal.name} [${
62
+ HUBSPOT_ACCOUNT_TYPE_STRINGS[portal.accountType]
63
+ }]`;
67
64
  if (isSandbox(portal)) {
68
- name = `${portal.name} ${getSandboxName(portal)}`;
69
65
  if (hasParentPortal && set.length > 1) {
70
66
  name = `↳ ${name}`;
71
67
  }
72
68
  } else if (isDeveloperTestAccount(portal)) {
73
- name = `${portal.name} [${
74
- HUBSPOT_ACCOUNT_TYPE_STRINGS[HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST]
75
- }]`;
76
69
  if (hasParentPortal && set.length > 1) {
77
70
  name = `↳ ${name}`;
78
71
  }
79
- } else if (isAppDeveloperAccount(portal)) {
80
- name = `${portal.name} [${
81
- HUBSPOT_ACCOUNT_TYPE_STRINGS[HUBSPOT_ACCOUNT_TYPES.APP_DEVELOPER]
82
- }]`;
83
72
  }
84
73
  portalData.push([name, portal.portalId, portal.authType]);
85
74
  });
@@ -10,7 +10,8 @@ const {
10
10
  logApiErrorInstance,
11
11
  ApiErrorContext,
12
12
  } = require('../../lib/errorHandlers/apiErrors');
13
- const { POLLING_DELAY } = require('../../lib/constants');
13
+
14
+ const { poll } = require('../../lib/polling');
14
15
  const { logger } = require('@hubspot/local-dev-lib/logger');
15
16
  const {
16
17
  buildPackage,
@@ -22,23 +23,6 @@ const { i18n } = require('../../lib/lang');
22
23
 
23
24
  const i18nKey = 'commands.functions.subcommands.deploy';
24
25
 
25
- const pollBuildStatus = (accountId, buildId) => {
26
- return new Promise((resolve, reject) => {
27
- const pollInterval = setInterval(async () => {
28
- const pollResp = await getBuildStatus(accountId, buildId);
29
- const { status } = pollResp;
30
-
31
- if (status === 'SUCCESS') {
32
- clearInterval(pollInterval);
33
- resolve(pollResp);
34
- } else if (status === 'ERROR') {
35
- clearInterval(pollInterval);
36
- reject(pollResp);
37
- }
38
- }, POLLING_DELAY);
39
- });
40
- };
41
-
42
26
  exports.command = 'deploy <path>';
43
27
  exports.describe = false;
44
28
 
@@ -78,7 +62,7 @@ exports.handler = async options => {
78
62
  })
79
63
  ).start();
80
64
  const buildId = await buildPackage(accountId, functionPath);
81
- const successResp = await pollBuildStatus(accountId, buildId);
65
+ const successResp = await poll(getBuildStatus, accountId, buildId);
82
66
  const buildTimeSeconds = (successResp.buildTime / 1000).toFixed(2);
83
67
  spinner.stop();
84
68
  await outputBuildLog(successResp.cdnUrl);
@@ -0,0 +1,206 @@
1
+ const path = require('path');
2
+ const {
3
+ addAccountOptions,
4
+ addConfigOptions,
5
+ getAccountId,
6
+ addUseEnvironmentOptions,
7
+ } = require('../../lib/commonOpts');
8
+ const { trackCommandUsage } = require('../../lib/usageTracking');
9
+ const { loadAndValidateOptions } = require('../../lib/validation');
10
+ const {
11
+ createProjectPrompt,
12
+ } = require('../../lib/prompts/createProjectPrompt');
13
+ const { i18n } = require('../../lib/lang');
14
+ const {
15
+ fetchPublicAppOptions,
16
+ selectPublicAppPrompt,
17
+ } = require('../../lib/prompts/selectPublicAppPrompt');
18
+ const { poll } = require('../../lib/polling');
19
+ const {
20
+ uiBetaTag,
21
+ uiLine,
22
+ uiLink,
23
+ uiCommandReference,
24
+ uiAccountDescription,
25
+ } = require('../../lib/ui');
26
+ const SpinniesManager = require('../../lib/ui/SpinniesManager');
27
+ const {
28
+ logApiErrorInstance,
29
+ ApiErrorContext,
30
+ } = require('../../lib/errorHandlers/apiErrors');
31
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
32
+ const { promptUser } = require('../../lib/prompts/promptUtils');
33
+ const { isAppDeveloperAccount } = require('../../lib/accountTypes');
34
+ const { ensureProjectExists } = require('../../lib/projects');
35
+ const {
36
+ migrateApp,
37
+ checkMigrationStatus,
38
+ } = require('@hubspot/local-dev-lib/api/projects');
39
+ const { getCwd } = require('@hubspot/local-dev-lib/path');
40
+ const { logger } = require('@hubspot/local-dev-lib/logger');
41
+ const { getAccountConfig } = require('@hubspot/local-dev-lib/config');
42
+ const { downloadProject } = require('@hubspot/local-dev-lib/api/projects');
43
+ const { extractZipArchive } = require('@hubspot/local-dev-lib/archive');
44
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/local-dev-lib/urls');
45
+
46
+ const i18nKey = 'commands.project.subcommands.migrateApp';
47
+
48
+ exports.command = 'migrate-app';
49
+ exports.describe = null; // uiBetaTag(i18n(`${i18nKey}.describe`), false);
50
+
51
+ exports.handler = async options => {
52
+ await loadAndValidateOptions(options);
53
+
54
+ const accountId = getAccountId(options);
55
+ const accountConfig = getAccountConfig(accountId);
56
+ const accountName = uiAccountDescription(accountId);
57
+
58
+ trackCommandUsage('migrate-app', {}, accountId);
59
+
60
+ if (!isAppDeveloperAccount(accountConfig)) {
61
+ logger.error(
62
+ i18n(`${i18nKey}.errors.invalidAccountType`, {
63
+ accountName,
64
+ accountType: accountConfig.accountType,
65
+ useCommand: uiCommandReference('hs accounts use'),
66
+ authCommand: uiCommandReference('hs auth'),
67
+ })
68
+ );
69
+ process.exit(EXIT_CODES.ERROR);
70
+ }
71
+
72
+ const { appId } =
73
+ 'appId' in options
74
+ ? options
75
+ : await selectPublicAppPrompt({
76
+ accountId,
77
+ accountName,
78
+ options,
79
+ migrateApp: true,
80
+ });
81
+
82
+ const publicApps = await fetchPublicAppOptions(accountId, accountName);
83
+ if (!publicApps.find(a => a.id === appId)) {
84
+ logger.error(i18n(`${i18nKey}.errors.invalidAppId`, { appId }));
85
+ process.exit(EXIT_CODES.ERROR);
86
+ }
87
+
88
+ const { name, location } = await createProjectPrompt('', options, true);
89
+
90
+ const projectName = options.name || name;
91
+ const projectLocation = options.location || location;
92
+
93
+ const { projectExists } = await ensureProjectExists(accountId, projectName, {
94
+ allowCreate: false,
95
+ noLogs: true,
96
+ });
97
+
98
+ if (projectExists) {
99
+ logger.error(
100
+ i18n(`${i18nKey}.errors.projectAlreadyExists`, {
101
+ projectName,
102
+ })
103
+ );
104
+ process.exit(EXIT_CODES.ERROR);
105
+ }
106
+
107
+ logger.log('');
108
+ uiLine();
109
+ logger.log(uiBetaTag(i18n(`${i18nKey}.warning.title`), false));
110
+ logger.log(i18n(`${i18nKey}.warning.projectConversion`));
111
+ logger.log(i18n(`${i18nKey}.warning.appConfig`));
112
+ logger.log('');
113
+ logger.log(i18n(`${i18nKey}.warning.buildAndDeploy`));
114
+ logger.log('');
115
+ logger.log(i18n(`${i18nKey}.warning.existingApps`));
116
+ logger.log('');
117
+ logger.log(i18n(`${i18nKey}.warning.copyApp`));
118
+ uiLine();
119
+
120
+ const { shouldCreateApp } = await promptUser({
121
+ name: 'shouldCreateApp',
122
+ type: 'confirm',
123
+ message: i18n(`${i18nKey}.createAppPrompt`),
124
+ });
125
+
126
+ if (!shouldCreateApp) {
127
+ process.exit(EXIT_CODES.SUCCESS);
128
+ }
129
+
130
+ try {
131
+ SpinniesManager.init();
132
+
133
+ SpinniesManager.add('migrateApp', {
134
+ text: i18n(`${i18nKey}.migrationStatus.inProgress`),
135
+ });
136
+
137
+ const migrateResponse = await migrateApp(accountId, appId, projectName);
138
+ const { id } = migrateResponse;
139
+ const pollResponse = await poll(checkMigrationStatus, accountId, id);
140
+ const { status, project } = pollResponse;
141
+ if (status === 'SUCCESS') {
142
+ const absoluteDestPath = path.resolve(getCwd(), projectLocation);
143
+ const { env } = getAccountConfig(accountId);
144
+ const baseUrl = getHubSpotWebsiteOrigin(env);
145
+
146
+ const zippedProject = await downloadProject(accountId, projectName, 1);
147
+
148
+ await extractZipArchive(
149
+ zippedProject,
150
+ projectName,
151
+ path.resolve(absoluteDestPath),
152
+ { includesRootDir: true, hideLogs: true }
153
+ );
154
+
155
+ SpinniesManager.succeed('migrateApp', {
156
+ text: i18n(`${i18nKey}.migrationStatus.done`),
157
+ succeedColor: 'white',
158
+ });
159
+ logger.log('');
160
+ uiLine();
161
+ logger.success(i18n(`${i18nKey}.migrationStatus.success`));
162
+ logger.log('');
163
+ logger.log(
164
+ uiLink(
165
+ i18n(`${i18nKey}.projectDetailsLink`),
166
+ `${baseUrl}/developer-projects/${accountId}/project/${project.name}`
167
+ )
168
+ );
169
+ process.exit(EXIT_CODES.SUCCESS);
170
+ }
171
+ } catch (e) {
172
+ SpinniesManager.fail('migrateApp', {
173
+ text: i18n(`${i18nKey}.migrationStatus.failure`),
174
+ failColor: 'white',
175
+ });
176
+ logApiErrorInstance(e.error, new ApiErrorContext({ accountId }));
177
+ process.exit(EXIT_CODES.ERROR);
178
+ }
179
+ };
180
+
181
+ exports.builder = yargs => {
182
+ yargs.options({
183
+ name: {
184
+ describe: i18n(`${i18nKey}.options.name.describe`),
185
+ type: 'string',
186
+ },
187
+ location: {
188
+ describe: i18n(`${i18nKey}.options.location.describe`),
189
+ type: 'string',
190
+ },
191
+ appId: {
192
+ describe: i18n(`${i18nKey}.options.appId.describe`),
193
+ type: 'number',
194
+ },
195
+ });
196
+
197
+ yargs.example([
198
+ ['$0 project migrate-app', i18n(`${i18nKey}.examples.default`)],
199
+ ]);
200
+
201
+ addConfigOptions(yargs, true);
202
+ addAccountOptions(yargs, true);
203
+ addUseEnvironmentOptions(yargs, true);
204
+
205
+ return yargs;
206
+ };
@@ -11,6 +11,7 @@ const download = require('./project/download');
11
11
  const open = require('./project/open');
12
12
  const dev = require('./project/dev');
13
13
  const add = require('./project/add');
14
+ const migrateApp = require('./project/migrateApp');
14
15
 
15
16
  const i18nKey = 'commands.project';
16
17
 
@@ -32,6 +33,7 @@ exports.builder = yargs => {
32
33
  yargs.command(listBuilds).demandCommand(0, '');
33
34
  yargs.command(download).demandCommand(0, '');
34
35
  yargs.command(open).demandCommand(0, '');
36
+ yargs.command(migrateApp).demandCommand(0, '');
35
37
 
36
38
  return yargs;
37
39
  };
@@ -33,7 +33,8 @@ const {
33
33
  COMPONENT_TYPES,
34
34
  } = require('../../lib/projectStructure');
35
35
 
36
- const i18nKey = 'commands.preview';
36
+ const i18nKey = 'commands.theme.subcommands.preview';
37
+
37
38
  exports.command = 'preview [--src] [--dest]';
38
39
  exports.describe = i18n(`${i18nKey}.describe`);
39
40
 
package/commands/watch.js CHANGED
@@ -18,8 +18,13 @@ const { validateMode, loadAndValidateOptions } = require('../lib/validation');
18
18
  const { trackCommandUsage } = require('../lib/usageTracking');
19
19
  const { i18n } = require('../lib/lang');
20
20
  const { getUploadableFileList } = require('../lib/upload');
21
-
21
+ const { logErrorInstance } = require('../lib/errorHandlers/standardErrors');
22
+ const {
23
+ logApiUploadErrorInstance,
24
+ ApiErrorContext,
25
+ } = require('../lib/errorHandlers/apiErrors');
22
26
  const i18nKey = 'commands.watch';
27
+
23
28
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
24
29
 
25
30
  exports.command = 'watch [--src] [--dest]';
@@ -87,14 +92,55 @@ exports.handler = async options => {
87
92
  }
88
93
 
89
94
  trackCommandUsage('watch', { mode }, accountId);
90
- watch(accountId, absoluteSrcPath, dest, {
91
- mode,
92
- remove,
93
- disableInitial: initialUpload ? false : true,
94
- notify,
95
- commandOptions: options,
96
- filePaths: filesToUpload,
97
- });
95
+
96
+ const postInitialUploadCallback = null;
97
+ const onUploadFolderError = error => {
98
+ logger.error(
99
+ i18n(`${i18nKey}.errors.folderFailed`, {
100
+ src,
101
+ dest,
102
+ accountId,
103
+ })
104
+ );
105
+ logErrorInstance(error, {
106
+ accountId,
107
+ });
108
+ };
109
+ const onQueueAddError = null;
110
+ const onUploadFileError = (file, dest, accountId) => error => {
111
+ logger.error(
112
+ i18n(`${i18nKey}.errors.fileFailed`, {
113
+ file,
114
+ dest,
115
+ accountId,
116
+ })
117
+ );
118
+ logApiUploadErrorInstance(
119
+ error,
120
+ new ApiErrorContext({
121
+ accountId,
122
+ request: dest,
123
+ payload: file,
124
+ })
125
+ );
126
+ };
127
+ watch(
128
+ accountId,
129
+ absoluteSrcPath,
130
+ dest,
131
+ {
132
+ mode,
133
+ remove,
134
+ disableInitial: initialUpload ? false : true,
135
+ notify,
136
+ commandOptions: options,
137
+ filePaths: filesToUpload,
138
+ },
139
+ postInitialUploadCallback,
140
+ onUploadFolderError,
141
+ onQueueAddError,
142
+ onUploadFileError
143
+ );
98
144
  };
99
145
 
100
146
  exports.builder = yargs => {
package/lang/en.lyaml CHANGED
@@ -496,6 +496,37 @@ en:
496
496
  describe: "The starting template"
497
497
  templateSource:
498
498
  describe: "Path to custom GitHub repository from which to create project template"
499
+ migrateApp:
500
+ describe: "Migrate a public app to the projects framework"
501
+ examples:
502
+ default: "Migrate a public app to the projects framework"
503
+ options:
504
+ appId:
505
+ describe: "The ID for the public app being migrated to the projects framework"
506
+ location:
507
+ describe: "Directory where project should be created"
508
+ name:
509
+ describe: "Project name (cannot be changed)"
510
+ migrationStatus:
511
+ inProgress: "Converting app configuration to {{#bold}}public-app.json{{/bold}} component definition ..."
512
+ success: "{{#bold}}Your app was converted and build #1 is deployed{{/bold}}"
513
+ done: "Converting app configuration to public-app.json component definition ... DONE"
514
+ failure: "Converting app configuration to public-app.json component definition ... FAILED"
515
+ warning:
516
+ title: "{{#bold}}Migrate an app to the projects framework?{{/bold}}"
517
+ projectConversion: "{{#bold}}The selected app will be permanently converted to a project component.{{/bold}}"
518
+ appConfig: "All supported app configuration will be moved to the {{#bold}}public-app.json{{/bold}} component definition file. Future updates to those features must be made through the project build and deploy pipeline, not the developer account UI."
519
+ buildAndDeploy: "This will create a new project with a single app component and immediately build and deploy it to your developer account (build #1)."
520
+ existingApps: "{{#bold}}This will not affect existing app users or installs.{{/bold}}"
521
+ copyApp: "We strongly recommend making a copy of your app to test this process in a development app before replacing production."
522
+ createAppPrompt: "Proceed with migrating this app to a project component?"
523
+ projectDetailsLink: "View project details in your developer account"
524
+ errors:
525
+ invalidAccountType: "Public apps must be migrated within an app developer account. Your current account {{#bold}}{{ accountName }}{{/bold}} is a {{ accountType }}.
526
+ \n- Run {{ useCommand }} to switch your default account to an app developer account.
527
+ \n- Run {{ authCommand }} to connect an app developer account to the HubSpot CLI.\n"
528
+ projectAlreadyExists: "A project with name {{ projectName }} already exists. Please choose another name."
529
+ invalidAppId: "[--appId] Could not find appId {{ appId }}. Please choose another public app."
499
530
  add:
500
531
  describe: "Create a new component within a project"
501
532
  options:
@@ -795,6 +826,28 @@ en:
795
826
  positionals:
796
827
  src:
797
828
  describe: "Path to the theme within the Design Manager."
829
+ preview:
830
+ describe: "Upload and watch a theme directory on your computer for changes and start a local development server to preview theme changes on a site"
831
+ errors:
832
+ invalidPath: "The path \"{{ path }}\" is not a path to a directory"
833
+ noThemeComponents: "Your project has no theme components available to preview."
834
+ options:
835
+ src:
836
+ describe: "Path to the local directory your theme is in, relative to your current working directory"
837
+ dest:
838
+ describe: "Path in HubSpot Design Tools. Can be a net new path. If you wish to preview a site page using your theme changes it must match the path of the theme used by the site."
839
+ notify:
840
+ describe: "Log to specified file when a watch task is triggered and after workers have gone idle. Ex. --notify path/to/file"
841
+ noSsl:
842
+ describe: "Disable HTTPS"
843
+ port:
844
+ describe: "The port on which to start the local server"
845
+ initialUploadProgressBar:
846
+ start: "Starting..."
847
+ uploading: "Uploading..."
848
+ finish: "Complete!"
849
+ logs:
850
+ processExited: "Stopping dev server..."
798
851
  module:
799
852
  describe: "Commands for working with modules, including marketplace validation with the marketplace-validate subcommand."
800
853
  subcommands:
@@ -852,6 +905,8 @@ en:
852
905
  watch:
853
906
  describe: "Watch a directory on your computer for changes and upload the changed files to the HubSpot CMS."
854
907
  errors:
908
+ folderFailed: "Initial uploading of folder \"{{ src }}\" to \"{{ dest }}\" in account {{ accountId }} had failures"
909
+ fileFailed: "Upload of file \"{{ file }}\" to \"{{ dest }}\" in account {{ accountId }} failed"
855
910
  destinationRequired: "A destination directory needs to be passed"
856
911
  invalidPath: "The \"{{ path }}\" is not a path to a directory"
857
912
  options:
@@ -878,28 +933,6 @@ en:
878
933
  disableInitial: "Passing the \"--disable-initial\" option is no longer necessary. Running \"hs watch\" no longer uploads the watched directory by default."
879
934
  initialUpload: "To upload the directory run \"hs upload\" beforehand or add the \"--initial-upload\" option when running \"hs watch\"."
880
935
  notUploaded: "The \"hs watch\" command no longer uploads the watched directory when started. The directory \"{{ path }}\" was not uploaded."
881
- preview:
882
- describe: "Upload and watch a theme directory on your computer for changes and start a local development server to preview theme changes on a site"
883
- errors:
884
- invalidPath: "The path \"{{ path }}\" is not a path to a directory"
885
- noThemeComponents: "Your project has no theme components available to preview."
886
- options:
887
- src:
888
- describe: "Path to the local directory your theme is in, relative to your current working directory"
889
- dest:
890
- describe: "Path in HubSpot Design Tools. Can be a net new path. If you wish to preview a site page using your theme changes it must match the path of the theme used by the site."
891
- notify:
892
- describe: "Log to specified file when a watch task is triggered and after workers have gone idle. Ex. --notify path/to/file"
893
- noSsl:
894
- describe: "Disable HTTPS"
895
- port:
896
- describe: "The port on which to start the local server"
897
- initialUploadProgressBar:
898
- start: "Starting..."
899
- uploading: "Uploading..."
900
- finish: "Complete!"
901
- logs:
902
- processExited: "Stopping dev server..."
903
936
  convertFields:
904
937
  describe: "Converts a specific JavaScript fields file of a module or theme to JSON"
905
938
  positionals:
@@ -962,7 +995,7 @@ en:
962
995
  createNewProjectForLocalDev:
963
996
  projectMustExistExplanation: "The project {{ projectName }} does not exist in the target account {{ accountIdentifier}}. This command requires the project to exist in the target account."
964
997
  publicAppProjectMustExistExplanation: "The project {{ projectName }} does not exist in {{ accountIdentifier}}, the app developer account associated with your target account. This command requires the project to exist in this app developer account."
965
- createProject: "Create new project {{ projectName}} in {{#bold}}[{{ accountIdentifier }}]{{/bold}}?"
998
+ createProject: "Create new project {{ projectName}} in {{ accountIdentifier }}?"
966
999
  choseNotToCreateProject: "Exiting because this command requires the project to exist in the target account."
967
1000
  creatingProject: "Creating project {{ projectName }} in {{ accountIdentifier }}"
968
1001
  createdProject: "Created project {{ projectName }} in {{ accountIdentifier }}"
@@ -1171,6 +1204,14 @@ en:
1171
1204
  invalidTemplate: "[--template] Could not find template {{ template }}. Please choose an available template."
1172
1205
  noProjectsInConfig: "Please ensure that there is a config.json file that contains a \"projects\" field."
1173
1206
  missingPropertiesInConfig: "Please ensure that each of the projects in your config.json file contain the following properties: [\"name\", \"label\", \"path\", \"insertPath\"]."
1207
+ selectPublicAppPrompt:
1208
+ selectAppIdMigrate: "[--appId] Choose an app under {{ accountName }} to migrate:"
1209
+ selectAppIdClone: "[--appId] Choose an app under {{ accountName }} to clone:"
1210
+ errors:
1211
+ noApps: "{{#bold}}No apps to migrate{{/bold}}"
1212
+ noAppsMessage: "The selected developer account {{#bold}}{{ accountName }}{{/bold}} doesn't have any apps that can be migrated to the projects framework."
1213
+ errorFetchingApps: "There was an error fetching public apps."
1214
+ invalidAppId: "[--appId] Could not find appId {{ appId }}. Please choose another public app."
1174
1215
  developerTestAccountPrompt:
1175
1216
  name:
1176
1217
  message: "Name your developer test account"
package/lib/constants.js CHANGED
@@ -22,6 +22,12 @@ const CONFIG_FLAGS = {
22
22
 
23
23
  const POLLING_DELAY = 2000;
24
24
 
25
+ const POLLING_STATUS = {
26
+ SUCCESS: 'SUCCESS',
27
+ ERROR: 'ERROR',
28
+ FAILURE: 'FAILURE',
29
+ };
30
+
25
31
  const PROJECT_CONFIG_FILE = 'hsproject.json';
26
32
  const PROJECT_BUILD_STATES = {
27
33
  BUILDING: 'BUILDING',
@@ -54,6 +60,8 @@ const PROJECT_ERROR_TYPES = {
54
60
  PROJECT_LOCKED: 'BuildPipelineErrorType.PROJECT_LOCKED',
55
61
  MISSING_PROJECT_PROVISION: 'BuildPipelineErrorType.MISSING_PROJECT_PROVISION',
56
62
  BUILD_NOT_IN_PROGRESS: 'BuildPipelineErrorType.BUILD_NOT_IN_PROGRESS',
63
+ SUBBUILD_FAILED: 'BuildPipelineErrorType.DEPENDENT_SUBBUILD_FAILED',
64
+ SUBDEPLOY_FAILED: 'DeployPipelineErrorType.DEPENDENT_SUBDEPLOY_FAILED',
57
65
  };
58
66
  const PROJECT_TASK_TYPES = {
59
67
  PRIVATE_APP: 'private app',
@@ -82,6 +90,7 @@ module.exports = {
82
90
  MARKETPLACE_FOLDER,
83
91
  CONFIG_FLAGS,
84
92
  POLLING_DELAY,
93
+ POLLING_STATUS,
85
94
  PROJECT_CONFIG_FILE,
86
95
  PROJECT_BUILD_TEXT,
87
96
  PROJECT_DEPLOY_TEXT,
package/lib/localDev.js CHANGED
@@ -23,7 +23,6 @@ const {
23
23
  const { confirmPrompt } = require('./prompts/promptUtils');
24
24
  const {
25
25
  validateSandboxUsageLimits,
26
- getSandboxTypeAsString,
27
26
  getAvailableSyncTypes,
28
27
  } = require('./sandboxes');
29
28
  const { buildSandbox } = require('./sandboxCreate');
@@ -70,9 +69,7 @@ const confirmDefaultAccountIsTarget = async accountConfig => {
70
69
  logger.log();
71
70
  const useDefaultAccount = await confirmDefaultAccountPrompt(
72
71
  accountConfig.name,
73
- isDeveloperTestAccount(accountConfig)
74
- ? HUBSPOT_ACCOUNT_TYPE_STRINGS[HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST]
75
- : `${getSandboxTypeAsString(accountConfig.accountType)} sandbox`
72
+ HUBSPOT_ACCOUNT_TYPE_STRINGS[accountConfig.accountType]
76
73
  );
77
74
 
78
75
  if (!useDefaultAccount) {
package/lib/polling.js ADDED
@@ -0,0 +1,25 @@
1
+ const { POLLING_DELAY, POLLING_STATUS } = require('./constants');
2
+
3
+ const poll = (callback, accountId, taskId) => {
4
+ return new Promise((resolve, reject) => {
5
+ const pollInterval = setInterval(async () => {
6
+ const pollResp = await callback(accountId, taskId);
7
+ const { status } = pollResp;
8
+
9
+ if (status === POLLING_STATUS.SUCCESS) {
10
+ clearInterval(pollInterval);
11
+ resolve(pollResp);
12
+ } else if (
13
+ status === POLLING_STATUS.ERROR ||
14
+ status === POLLING_STATUS.FAILURE
15
+ ) {
16
+ clearInterval(pollInterval);
17
+ reject(pollResp);
18
+ }
19
+ }, POLLING_DELAY);
20
+ });
21
+ };
22
+
23
+ module.exports = {
24
+ poll,
25
+ };
package/lib/projects.js CHANGED
@@ -17,6 +17,7 @@ const {
17
17
  PROJECT_DEPLOY_TEXT,
18
18
  PROJECT_CONFIG_FILE,
19
19
  PROJECT_TASK_TYPES,
20
+ PROJECT_ERROR_TYPES,
20
21
  } = require('./constants');
21
22
  const {
22
23
  createProject,
@@ -772,7 +773,9 @@ const makePollTaskStatusFunc = ({
772
773
  const displayErrors = failedSubtasks.filter(
773
774
  subtask =>
774
775
  subtask.standardError.subCategory !==
775
- 'BuildPipelineErrorType.DEPENDENT_SUBBUILD_FAILED'
776
+ PROJECT_ERROR_TYPES.SUBBUILD_FAILED &&
777
+ subtask.standardError.subCategory !==
778
+ PROJECT_ERROR_TYPES.SUBDEPLOY_FAILED
776
779
  );
777
780
 
778
781
  displayErrors.forEach(subTask => {
@@ -48,11 +48,18 @@ const createTemplateOptions = async (templateSource, githubRef) => {
48
48
  return config[PROJECT_COMPONENT_TYPES.PROJECTS];
49
49
  };
50
50
 
51
- const createProjectPrompt = async (githubRef, promptOptions = {}) => {
52
- const projectTemplates = await createTemplateOptions(
53
- promptOptions.templateSource,
54
- githubRef
55
- );
51
+ const createProjectPrompt = async (
52
+ githubRef,
53
+ promptOptions = {},
54
+ skipTemplatePrompt = false
55
+ ) => {
56
+ let projectTemplates = [];
57
+ if (!skipTemplatePrompt) {
58
+ projectTemplates = await createTemplateOptions(
59
+ promptOptions.templateSource,
60
+ githubRef
61
+ );
62
+ }
56
63
 
57
64
  return promptUser([
58
65
  {
@@ -91,8 +98,9 @@ const createProjectPrompt = async (githubRef, promptOptions = {}) => {
91
98
  : i18n(`${i18nKey}.selectTemplate`);
92
99
  },
93
100
  when:
94
- !promptOptions.template ||
95
- !projectTemplates.find(t => t.name === promptOptions.template),
101
+ !skipTemplatePrompt &&
102
+ (!promptOptions.template ||
103
+ !projectTemplates.find(t => t.name === promptOptions.template)),
96
104
  type: 'list',
97
105
  choices: projectTemplates.map(template => {
98
106
  return {
@@ -33,8 +33,8 @@ const installPublicAppPrompt = async (
33
33
  const url =
34
34
  `${websiteOrigin}/oauth/${targetAccountId}/authorize` +
35
35
  `?client_id=${encodeURIComponent(clientId)}` +
36
- `&scope=${encodeURIComponent(scopes)}` +
37
- `&redirect_uri=${encodeURIComponent(redirectUrls)}`;
36
+ `&scope=${encodeURIComponent(scopes.join(' '))}` +
37
+ `&redirect_uri=${encodeURIComponent(redirectUrls[0])}`;
38
38
 
39
39
  open(url);
40
40
  };
@@ -0,0 +1,74 @@
1
+ const { promptUser } = require('./promptUtils');
2
+ const { i18n } = require('../lang');
3
+ const { uiLine } = require('../ui');
4
+ const { logApiErrorInstance } = require('../errorHandlers/apiErrors');
5
+ const { logger } = require('@hubspot/local-dev-lib/logger');
6
+ const {
7
+ fetchPublicAppsForPortal,
8
+ } = require('@hubspot/local-dev-lib/api/appsDev');
9
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
10
+
11
+ const i18nKey = 'lib.prompts.selectPublicAppPrompt';
12
+
13
+ const fetchPublicAppOptions = async (accountId, accountName) => {
14
+ try {
15
+ const publicApps = await fetchPublicAppsForPortal(accountId);
16
+ const filteredPublicApps = publicApps.filter(
17
+ app => !app.projectId && !app.sourceId
18
+ );
19
+
20
+ if (!filteredPublicApps.length) {
21
+ uiLine();
22
+ logger.error(i18n(`${i18nKey}.errors.noApps`));
23
+ logger.log(i18n(`${i18nKey}.errors.noAppsMessage`, { accountName }));
24
+ uiLine();
25
+ process.exit(EXIT_CODES.SUCCESS);
26
+ }
27
+ return filteredPublicApps;
28
+ } catch (error) {
29
+ logApiErrorInstance(error, { accountId });
30
+ logger.error(i18n(`${i18nKey}.errors.errorFetchingApps`));
31
+ process.exit(EXIT_CODES.ERROR);
32
+ }
33
+ };
34
+
35
+ const selectPublicAppPrompt = async ({
36
+ accountId,
37
+ accountName,
38
+ promptOptions = {},
39
+ migrateApp = false,
40
+ }) => {
41
+ const publicApps = await fetchPublicAppOptions(accountId, accountName);
42
+ const translationKey = migrateApp ? 'selectAppIdMigrate' : 'selectAppIdClone';
43
+
44
+ return promptUser([
45
+ {
46
+ name: 'appId',
47
+ message: () => {
48
+ return promptOptions.appId &&
49
+ !publicApps.find(a => a.id === promptOptions.appId)
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),
60
+ type: 'list',
61
+ choices: publicApps.map(app => {
62
+ return {
63
+ name: `${app.name} (${app.id})`,
64
+ value: app.id,
65
+ };
66
+ }),
67
+ },
68
+ ]);
69
+ };
70
+
71
+ module.exports = {
72
+ fetchPublicAppOptions,
73
+ selectPublicAppPrompt,
74
+ };
package/lib/sandboxes.js CHANGED
@@ -39,9 +39,6 @@ const getSandboxTypeAsString = accountType => {
39
39
  return 'standard';
40
40
  };
41
41
 
42
- const getSandboxName = config =>
43
- `[${getSandboxTypeAsString(config.accountType)} sandbox] `;
44
-
45
42
  function getHasSandboxesByType(parentAccountConfig, type) {
46
43
  const config = getConfig();
47
44
  const parentPortalId = getAccountId(parentAccountConfig.portalId);
@@ -342,7 +339,6 @@ module.exports = {
342
339
  sandboxTypeMap,
343
340
  sandboxApiTypeMap,
344
341
  syncTypes,
345
- getSandboxName,
346
342
  getSandboxTypeAsString,
347
343
  getHasSandboxesByType,
348
344
  getSandboxLimit,
package/lib/ui/index.js CHANGED
@@ -3,17 +3,10 @@ const { getAccountConfig } = require('@hubspot/local-dev-lib/config');
3
3
  const { logger } = require('@hubspot/local-dev-lib/logger');
4
4
  const supportsHyperlinks = require('./supportHyperlinks');
5
5
  const supportsColor = require('./supportsColor');
6
- const {
7
- isSandbox,
8
- isDeveloperTestAccount,
9
- isAppDeveloperAccount,
10
- } = require('../accountTypes');
11
- const { getSandboxName } = require('../sandboxes');
12
6
  const { i18n } = require('../lang');
13
7
 
14
8
  const {
15
9
  HUBSPOT_ACCOUNT_TYPE_STRINGS,
16
- HUBSPOT_ACCOUNT_TYPES,
17
10
  } = require('@hubspot/local-dev-lib/constants/config');
18
11
 
19
12
  const UI_COLORS = {
@@ -84,19 +77,9 @@ const uiLink = (linkText, url) => {
84
77
  */
85
78
  const uiAccountDescription = (accountId, bold = true) => {
86
79
  const account = getAccountConfig(accountId);
87
- let accountTypeString = '';
88
- if (isSandbox(account)) {
89
- accountTypeString = getSandboxName(account);
90
- } else if (isDeveloperTestAccount(account)) {
91
- accountTypeString = `[${
92
- HUBSPOT_ACCOUNT_TYPE_STRINGS[HUBSPOT_ACCOUNT_TYPES.DEVELOPER_TEST]
93
- }] `;
94
- } else if (isAppDeveloperAccount(account)) {
95
- accountTypeString = `[${
96
- HUBSPOT_ACCOUNT_TYPE_STRINGS[HUBSPOT_ACCOUNT_TYPES.APP_DEVELOPER]
97
- }] `;
98
- }
99
- const message = `${account.name} ${accountTypeString}(${account.portalId})`;
80
+ const message = `${account.name} [${
81
+ HUBSPOT_ACCOUNT_TYPE_STRINGS[account.accountType]
82
+ }] (${account.portalId})`;
100
83
  return bold ? chalk.bold(message) : message;
101
84
  };
102
85
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "5.2.1-beta.5",
3
+ "version": "5.2.1-beta.7",
4
4
  "description": "CLI for working with HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,10 +8,10 @@
8
8
  "url": "https://github.com/HubSpot/hubspot-cms-tools"
9
9
  },
10
10
  "dependencies": {
11
- "@hubspot/local-dev-lib": "1.1.0",
12
- "@hubspot/serverless-dev-runtime": "5.2.1-beta.4",
13
- "@hubspot/theme-preview-dev-server": "0.0.6",
14
- "@hubspot/ui-extensions-dev-server": "0.8.17",
11
+ "@hubspot/local-dev-lib": "1.4.0",
12
+ "@hubspot/serverless-dev-runtime": "5.2.1-beta.7",
13
+ "@hubspot/theme-preview-dev-server": "0.0.7",
14
+ "@hubspot/ui-extensions-dev-server": "0.8.20",
15
15
  "archiver": "^5.3.0",
16
16
  "chalk": "^4.1.2",
17
17
  "chokidar": "^3.0.1",
@@ -45,5 +45,5 @@
45
45
  "publishConfig": {
46
46
  "access": "public"
47
47
  },
48
- "gitHead": "21195f0f24e2e148f37c0320f0b31c2ebad817ff"
48
+ "gitHead": "8a823fcec0f1538d0af77c30a177e1d33b474d50"
49
49
  }