@hubspot/cli 3.0.13-beta.3 → 4.0.1-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js CHANGED
@@ -58,24 +58,55 @@ const getTerminalWidth = () => {
58
58
  return width;
59
59
  };
60
60
 
61
- const argv = yargs
62
- .usage('Tools for working with HubSpot')
63
- .middleware([setLogLevel])
64
- .exitProcess(false)
65
- .fail((msg, err, yargs) => {
66
- if (msg) {
67
- logger.error(msg);
68
- } else if (err) {
69
- logErrorInstance(err);
70
- }
61
+ const handleFailure = (msg, err, yargs) => {
62
+ if (msg) {
63
+ logger.error(msg);
64
+ } else if (err) {
65
+ logErrorInstance(err);
66
+ }
71
67
 
72
- if (msg === null) {
73
- yargs.showHelp();
74
- process.exit(EXIT_CODES.SUCCESS);
75
- } else {
68
+ if (msg === null) {
69
+ yargs.showHelp();
70
+ process.exit(EXIT_CODES.SUCCESS);
71
+ } else {
72
+ process.exit(EXIT_CODES.ERROR);
73
+ }
74
+ };
75
+
76
+ const performChecks = argv => {
77
+ // "hs config set default-account" has moved to "hs accounts use"
78
+ if (
79
+ argv._[0] === 'config' &&
80
+ argv._[1] === 'set' &&
81
+ argv._[2] === 'default-account'
82
+ ) {
83
+ logger.error(i18n(`${i18nKey}.setDefaultAccountMoved`));
84
+ process.exit(EXIT_CODES.ERROR);
85
+ }
86
+
87
+ // Require "project" command when running upload/watch inside of a project
88
+ if (argv._.length === 1 && ['upload', 'watch'].includes(argv._[0])) {
89
+ if (getIsInProject(argv.src)) {
90
+ logger.error(
91
+ i18n(`${i18nKey}.srcIsProject`, {
92
+ src: argv.src || './',
93
+ command: argv._.join(' '),
94
+ })
95
+ );
76
96
  process.exit(EXIT_CODES.ERROR);
97
+ } else {
98
+ return true;
77
99
  }
78
- })
100
+ } else {
101
+ return true;
102
+ }
103
+ };
104
+
105
+ const argv = yargs
106
+ .usage('The command line interface to interact with HubSpot.')
107
+ .middleware([setLogLevel])
108
+ .exitProcess(false)
109
+ .fail(handleFailure)
79
110
  .option('debug', {
80
111
  alias: 'd',
81
112
  default: false,
@@ -94,23 +125,7 @@ const argv = yargs
94
125
  hidden: true,
95
126
  type: 'boolean',
96
127
  })
97
- .check(argv => {
98
- if (argv._.length === 1 && ['upload', 'watch'].includes(argv._[0])) {
99
- if (getIsInProject(argv.src)) {
100
- logger.error(
101
- i18n(`${i18nKey}.srcIsProject`, {
102
- src: argv.src,
103
- command: argv._.join(' '),
104
- })
105
- );
106
- process.exit(EXIT_CODES.ERROR);
107
- } else {
108
- return true;
109
- }
110
- } else {
111
- return true;
112
- }
113
- })
128
+ .check(performChecks)
114
129
  .command(authCommand)
115
130
  .command(initCommand)
116
131
  .command(logsCommand)
@@ -33,7 +33,7 @@ const selectAccountFromConfig = async config => {
33
33
  return selectedDefault;
34
34
  };
35
35
 
36
- exports.command = 'use [account]';
36
+ exports.command = 'use [--account]';
37
37
  exports.describe = i18n(`${i18nKey}.describe`);
38
38
 
39
39
  exports.handler = async options => {
@@ -68,14 +68,17 @@ exports.handler = async options => {
68
68
  };
69
69
 
70
70
  exports.builder = yargs => {
71
- yargs.positional('account', {
72
- describe: i18n(`${i18nKey}.positionals.account.describe`),
71
+ yargs.option('account', {
72
+ describe: i18n(`${i18nKey}.options.account.describe`),
73
73
  type: 'string',
74
74
  });
75
75
  yargs.example([
76
76
  ['$0 accounts use', i18n(`${i18nKey}.examples.default`)],
77
- ['$0 accounts use MyAccount', i18n(`${i18nKey}.examples.nameBased`)],
78
- ['$0 accounts use 1234567', i18n(`${i18nKey}.examples.idBased`)],
77
+ [
78
+ '$0 accounts use --account=MyAccount',
79
+ i18n(`${i18nKey}.examples.nameBased`),
80
+ ],
81
+ ['$0 accounts use --account=1234567', i18n(`${i18nKey}.examples.idBased`)],
79
82
  ]);
80
83
 
81
84
  return yargs;
package/commands/auth.js CHANGED
@@ -13,8 +13,8 @@ const {
13
13
  } = require('@hubspot/cli-lib/personalAccessKey');
14
14
  const {
15
15
  updateAccountConfig,
16
- accountNameExistsInConfig,
17
16
  writeConfig,
17
+ getConfig,
18
18
  getConfigPath,
19
19
  } = require('@hubspot/cli-lib/lib/config');
20
20
  const { commaSeparatedValues } = require('@hubspot/cli-lib/lib/text');
@@ -23,8 +23,13 @@ const {
23
23
  personalAccessKeyPrompt,
24
24
  OAUTH_FLOW,
25
25
  API_KEY_FLOW,
26
- ACCOUNT_NAME,
27
26
  } = require('../lib/prompts/personalAccessKeyPrompt');
27
+ const {
28
+ enterAccountNamePrompt,
29
+ } = require('../lib/prompts/enterAccountNamePrompt');
30
+ const {
31
+ setAsDefaultAccountPrompt,
32
+ } = require('../lib/prompts/setAsDefaultAccountPrompt');
28
33
  const {
29
34
  addConfigOptions,
30
35
  setLogLevel,
@@ -34,6 +39,7 @@ const { logDebugInfo } = require('../lib/debugInfo');
34
39
  const { trackCommandUsage } = require('../lib/usageTracking');
35
40
  const { authenticateWithOauth } = require('../lib/oauth');
36
41
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
42
+ const { uiFeatureHighlight } = require('../lib/ui');
37
43
 
38
44
  const i18nKey = 'cli.commands.auth';
39
45
 
@@ -45,34 +51,13 @@ const SUPPORTED_AUTHENTICATION_PROTOCOLS_TEXT = commaSeparatedValues(
45
51
  ALLOWED_AUTH_METHODS
46
52
  );
47
53
 
48
- const promptForAccountNameIfNotSet = async updatedConfig => {
49
- if (!updatedConfig.name) {
50
- let promptAnswer;
51
- let validName = null;
52
- while (!validName) {
53
- promptAnswer = await promptUser([ACCOUNT_NAME]);
54
-
55
- if (!accountNameExistsInConfig(promptAnswer.name)) {
56
- validName = promptAnswer.name;
57
- } else {
58
- logger.log(
59
- i18n(`${i18nKey}.errors.accountNameExists`, {
60
- name: promptAnswer.name,
61
- })
62
- );
63
- }
64
- }
65
- return validName;
66
- }
67
- };
68
-
69
- exports.command = 'auth [type]';
54
+ exports.command = 'auth [type] [--account]';
70
55
  exports.describe = i18n(`${i18nKey}.describe`, {
71
56
  supportedProtocols: SUPPORTED_AUTHENTICATION_PROTOCOLS_TEXT,
72
57
  });
73
58
 
74
59
  exports.handler = async options => {
75
- const { type, config: configPath, qa } = options;
60
+ const { type, config: configPath, qa, account } = options;
76
61
  const authType =
77
62
  (type && type.toLowerCase()) || PERSONAL_ACCESS_KEY_AUTH_METHOD.value;
78
63
  setLogLevel(options);
@@ -92,11 +77,18 @@ exports.handler = async options => {
92
77
  let configData;
93
78
  let updatedConfig;
94
79
  let validName;
80
+ let successAuthMethod;
81
+
95
82
  switch (authType) {
96
83
  case API_KEY_AUTH_METHOD.value:
97
84
  configData = await promptUser(API_KEY_FLOW);
98
85
  updatedConfig = await updateAccountConfig(configData);
99
- validName = await promptForAccountNameIfNotSet(updatedConfig);
86
+ validName = updatedConfig.name;
87
+
88
+ if (!validName) {
89
+ const { name: namePrompt } = await enterAccountNamePrompt();
90
+ validName = namePrompt;
91
+ }
100
92
 
101
93
  updateAccountConfig({
102
94
  ...updatedConfig,
@@ -105,13 +97,7 @@ exports.handler = async options => {
105
97
  });
106
98
  writeConfig();
107
99
 
108
- logger.success(
109
- i18n(`${i18nKey}.success.configFileUpdated`, {
110
- configFilename: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
111
- authMethod: API_KEY_AUTH_METHOD.name,
112
- })
113
- );
114
-
100
+ successAuthMethod = API_KEY_AUTH_METHOD.name;
115
101
  break;
116
102
  case OAUTH_AUTH_METHOD.value:
117
103
  configData = await promptUser(OAUTH_FLOW);
@@ -119,16 +105,22 @@ exports.handler = async options => {
119
105
  ...configData,
120
106
  env,
121
107
  });
108
+ successAuthMethod = OAUTH_AUTH_METHOD.name;
122
109
  break;
123
110
  case PERSONAL_ACCESS_KEY_AUTH_METHOD.value:
124
- configData = await personalAccessKeyPrompt({ env });
111
+ configData = await personalAccessKeyPrompt({ env, account });
125
112
  updatedConfig = await updateConfigWithPersonalAccessKey(configData);
126
113
 
127
114
  if (!updatedConfig) {
128
- process.exit(EXIT_CODES.SUCCESS);
115
+ break;
129
116
  }
130
117
 
131
- validName = await promptForAccountNameIfNotSet(updatedConfig);
118
+ validName = updatedConfig.name;
119
+
120
+ if (!validName) {
121
+ const { name: namePrompt } = await enterAccountNamePrompt();
122
+ validName = namePrompt;
123
+ }
132
124
 
133
125
  updateAccountConfig({
134
126
  ...updatedConfig,
@@ -138,12 +130,7 @@ exports.handler = async options => {
138
130
  });
139
131
  writeConfig();
140
132
 
141
- logger.success(
142
- i18n(`${i18nKey}.success.configFileUpdated`, {
143
- configFilename: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
144
- authMethod: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
145
- })
146
- );
133
+ successAuthMethod = PERSONAL_ACCESS_KEY_AUTH_METHOD.name;
147
134
  break;
148
135
  default:
149
136
  logger.error(
@@ -154,6 +141,43 @@ exports.handler = async options => {
154
141
  );
155
142
  break;
156
143
  }
144
+
145
+ if (!successAuthMethod) {
146
+ process.exit(EXIT_CODES.ERROR);
147
+ }
148
+
149
+ const accountName = updatedConfig.name || validName;
150
+
151
+ const setAsDefault = await setAsDefaultAccountPrompt(accountName);
152
+
153
+ logger.log('');
154
+ if (setAsDefault) {
155
+ logger.success(
156
+ i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.setAsDefaultAccount`, {
157
+ accountName,
158
+ })
159
+ );
160
+ } else {
161
+ const config = getConfig();
162
+ logger.info(
163
+ i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.keepingCurrentDefault`, {
164
+ accountName: config.defaultPortal,
165
+ })
166
+ );
167
+ }
168
+ logger.success(
169
+ i18n(`${i18nKey}.success.configFileUpdated`, {
170
+ configFilename: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
171
+ authType: successAuthMethod,
172
+ accountName,
173
+ })
174
+ );
175
+ uiFeatureHighlight([
176
+ 'accountsUseCommand',
177
+ 'accountOption',
178
+ 'accountsListCommand',
179
+ ]);
180
+
157
181
  process.exit(EXIT_CODES.SUCCESS);
158
182
  };
159
183
 
@@ -172,6 +196,13 @@ exports.builder = yargs => {
172
196
  }),
173
197
  });
174
198
 
199
+ yargs.options({
200
+ account: {
201
+ describe: i18n(`${i18nKey}.options.account.describe`),
202
+ type: 'string',
203
+ },
204
+ });
205
+
175
206
  addConfigOptions(yargs, true);
176
207
  addTestingOptions(yargs, true);
177
208
 
@@ -33,7 +33,7 @@ const selectOptions = async () => {
33
33
  };
34
34
 
35
35
  const handleConfigUpdate = async (accountId, options) => {
36
- const { defaultMode, httpTimeout, allowUsageTracking } = options;
36
+ const { allowUsageTracking, defaultMode, httpTimeout } = options;
37
37
 
38
38
  if (typeof defaultMode !== 'undefined') {
39
39
  await setDefaultMode({ defaultMode, accountId });
@@ -89,5 +89,8 @@ exports.builder = yargs => {
89
89
 
90
90
  yargs.example([['$0 config set', i18n(`${i18nKey}.examples.default`)]]);
91
91
 
92
+ //TODO remove this when "hs accounts use" is fully rolled out
93
+ yargs.strict(false);
94
+
92
95
  return yargs;
93
96
  };
package/commands/init.js CHANGED
@@ -30,12 +30,15 @@ const { promptUser } = require('../lib/prompts/promptUtils');
30
30
  const {
31
31
  OAUTH_FLOW,
32
32
  API_KEY_FLOW,
33
- ACCOUNT_NAME,
34
33
  personalAccessKeyPrompt,
35
34
  } = require('../lib/prompts/personalAccessKeyPrompt');
35
+ const {
36
+ enterAccountNamePrompt,
37
+ } = require('../lib/prompts/enterAccountNamePrompt');
36
38
  const { logDebugInfo } = require('../lib/debugInfo');
37
39
  const { authenticateWithOauth } = require('../lib/oauth');
38
40
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
41
+ const { uiFeatureHighlight } = require('../lib/ui');
39
42
 
40
43
  const i18nKey = 'cli.commands.init';
41
44
 
@@ -45,9 +48,9 @@ const TRACKING_STATUS = {
45
48
  COMPLETE: 'complete',
46
49
  };
47
50
 
48
- const personalAccessKeyConfigCreationFlow = async env => {
49
- const configData = await personalAccessKeyPrompt({ env });
50
- const { name } = await promptUser([ACCOUNT_NAME]);
51
+ const personalAccessKeyConfigCreationFlow = async (env, account) => {
52
+ const configData = await personalAccessKeyPrompt({ env, account });
53
+ const { name } = await enterAccountNamePrompt();
51
54
  const accountConfig = {
52
55
  ...configData,
53
56
  name,
@@ -85,13 +88,23 @@ const CONFIG_CREATION_FLOWS = {
85
88
  [API_KEY_AUTH_METHOD.value]: apiKeyConfigCreationFlow,
86
89
  };
87
90
 
88
- exports.command = 'init';
91
+ const AUTH_TYPE_NAMES = {
92
+ [PERSONAL_ACCESS_KEY_AUTH_METHOD.value]: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
93
+ [OAUTH_AUTH_METHOD.value]: OAUTH_AUTH_METHOD.name,
94
+ [API_KEY_AUTH_METHOD.value]: API_KEY_AUTH_METHOD.name,
95
+ };
96
+
97
+ exports.command = 'init [--account]';
89
98
  exports.describe = i18n(`${i18nKey}.describe`, {
90
99
  configName: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
91
100
  });
92
101
 
93
102
  exports.handler = async options => {
94
- const { auth: authType = PERSONAL_ACCESS_KEY_AUTH_METHOD.value, c } = options;
103
+ const {
104
+ auth: authType = PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
105
+ c,
106
+ account: optionalAccount,
107
+ } = options;
95
108
  const configPath = (c && path.join(getCwd(), c)) || getConfigPath();
96
109
  setLogLevel(options);
97
110
  logDebugInfo(options);
@@ -106,7 +119,7 @@ exports.handler = async options => {
106
119
  configPath,
107
120
  })
108
121
  );
109
- logger.info(i18n(`${i18nKey}.info.updateConfig`));
122
+ logger.info(i18n(`${i18nKey}.logs.updateConfig`));
110
123
  process.exit(EXIT_CODES.ERROR);
111
124
  }
112
125
 
@@ -115,16 +128,25 @@ exports.handler = async options => {
115
128
  handleExit(deleteEmptyConfigFile);
116
129
 
117
130
  try {
118
- const { accountId, name } = await CONFIG_CREATION_FLOWS[authType](env);
131
+ const { accountId, name } = await CONFIG_CREATION_FLOWS[authType](
132
+ env,
133
+ optionalAccount
134
+ );
119
135
  const configPath = getConfigPath();
120
136
 
137
+ logger.log('');
121
138
  logger.success(
122
139
  i18n(`${i18nKey}.success.configFileCreated`, {
123
140
  configPath,
124
- authType,
141
+ })
142
+ );
143
+ logger.success(
144
+ i18n(`${i18nKey}.success.configFileUpdated`, {
145
+ authType: AUTH_TYPE_NAMES[authType],
125
146
  account: name || accountId,
126
147
  })
127
148
  );
149
+ uiFeatureHighlight(['helpCommand', 'authCommand', 'accountsListCommand']);
128
150
 
129
151
  trackAuthAction('init', authType, TRACKING_STATUS.COMPLETE, accountId);
130
152
  process.exit(EXIT_CODES.SUCCESS);
@@ -135,18 +157,24 @@ exports.handler = async options => {
135
157
  };
136
158
 
137
159
  exports.builder = yargs => {
138
- yargs.option('auth', {
139
- describe: i18n(`${i18nKey}.options.auth.describe`),
140
- type: 'string',
141
- choices: [
142
- `${PERSONAL_ACCESS_KEY_AUTH_METHOD.value}`,
143
- `${OAUTH_AUTH_METHOD.value}`,
144
- `${API_KEY_AUTH_METHOD.value}`,
145
- ],
146
- default: PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
147
- defaultDescription: i18n(`${i18nKey}.options.auth.defaultDescription`, {
148
- defaultType: PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
149
- }),
160
+ yargs.options({
161
+ auth: {
162
+ describe: i18n(`${i18nKey}.options.auth.describe`),
163
+ type: 'string',
164
+ choices: [
165
+ `${PERSONAL_ACCESS_KEY_AUTH_METHOD.value}`,
166
+ `${OAUTH_AUTH_METHOD.value}`,
167
+ `${API_KEY_AUTH_METHOD.value}`,
168
+ ],
169
+ default: PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
170
+ defaultDescription: i18n(`${i18nKey}.options.auth.defaultDescription`, {
171
+ defaultType: PERSONAL_ACCESS_KEY_AUTH_METHOD.value,
172
+ }),
173
+ },
174
+ account: {
175
+ describe: i18n(`${i18nKey}.options.account.describe`),
176
+ type: 'string',
177
+ },
150
178
  });
151
179
 
152
180
  addConfigOptions(yargs, true);
@@ -8,14 +8,14 @@ const { trackCommandUsage } = require('../../lib/usageTracking');
8
8
  const { loadAndValidateOptions } = require('../../lib/validation');
9
9
  const { getCwd } = require('@hubspot/cli-lib/path');
10
10
  const path = require('path');
11
+ const chalk = require('chalk');
11
12
  const {
12
13
  createProjectPrompt,
13
14
  } = require('../../lib/prompts/createProjectPrompt');
14
- const {
15
- createProjectConfig,
16
- showProjectWelcomeMessage,
17
- } = require('../../lib/projects');
15
+ const { createProjectConfig } = require('../../lib/projects');
18
16
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
17
+ const { uiFeatureHighlight } = require('../../lib/ui');
18
+ const { logger } = require('@hubspot/cli-lib/logger');
19
19
 
20
20
  const i18nKey = 'cli.commands.project.subcommands.create';
21
21
 
@@ -37,7 +37,14 @@ exports.handler = async options => {
37
37
  options.template || template
38
38
  );
39
39
 
40
- showProjectWelcomeMessage();
40
+ logger.log('');
41
+ logger.log(chalk.bold(i18n(`${i18nKey}.logs.welcomeMessage`)));
42
+ uiFeatureHighlight([
43
+ 'createCommand',
44
+ 'projectUploadCommand',
45
+ 'projectDeployCommand',
46
+ 'projectHelpCommand',
47
+ ]);
41
48
  };
42
49
 
43
50
  exports.builder = yargs => {
@@ -12,41 +12,44 @@ const {
12
12
  const { logger } = require('@hubspot/cli-lib/logger');
13
13
  const { deployProject, fetchProject } = require('@hubspot/cli-lib/api/dfs');
14
14
  const { loadAndValidateOptions } = require('../../lib/validation');
15
- const {
16
- getProjectConfig,
17
- pollDeployStatus,
18
- validateProjectConfig,
19
- } = require('../../lib/projects');
15
+ const { getProjectConfig, pollDeployStatus } = require('../../lib/projects');
16
+ const { projectNamePrompt } = require('../../lib/prompts/projectNamePrompt');
20
17
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
21
18
 
22
19
  const i18nKey = 'cli.commands.project.subcommands.deploy';
23
20
  const { EXIT_CODES } = require('../../lib/enums/exitCodes');
24
21
 
25
- exports.command = 'deploy [path]';
22
+ exports.command = 'deploy [--project] [--buildId]';
26
23
  exports.describe = i18n(`${i18nKey}.describe`);
27
24
 
28
25
  exports.handler = async options => {
29
26
  await loadAndValidateOptions(options);
30
27
 
31
- const { path: projectPath, buildId } = options;
32
28
  const accountId = getAccountId(options);
29
+ const { project, buildId } = options;
33
30
 
34
- trackCommandUsage('project-deploy', { projectPath }, accountId);
31
+ trackCommandUsage('project-deploy', { project }, accountId);
35
32
 
36
- const { projectConfig, projectDir } = await getProjectConfig(projectPath);
33
+ const { projectConfig } = await getProjectConfig();
37
34
 
38
- validateProjectConfig(projectConfig, projectDir);
35
+ let projectName = project;
39
36
 
40
- logger.debug(
41
- i18n(`${i18nKey}.debug.deploying`, {
42
- path: projectPath,
43
- })
44
- );
37
+ if (!projectName && projectConfig) {
38
+ projectName = projectConfig.name;
39
+ }
40
+
41
+ const namePrompt = await projectNamePrompt(accountId, {
42
+ project: projectName,
43
+ });
44
+
45
+ if (!projectName && namePrompt.projectName) {
46
+ projectName = namePrompt.projectName;
47
+ }
45
48
 
46
49
  let exitCode = EXIT_CODES.SUCCESS;
47
50
 
48
51
  const getBuildId = async () => {
49
- const { latestBuild } = await fetchProject(accountId, projectConfig.name);
52
+ const { latestBuild } = await fetchProject(accountId, projectName);
50
53
  if (latestBuild && latestBuild.buildId) {
51
54
  return latestBuild.buildId;
52
55
  }
@@ -60,7 +63,7 @@ exports.handler = async options => {
60
63
 
61
64
  const deployResp = await deployProject(
62
65
  accountId,
63
- projectConfig.name,
66
+ projectName,
64
67
  deployedBuildId
65
68
  );
66
69
 
@@ -76,7 +79,7 @@ exports.handler = async options => {
76
79
 
77
80
  await pollDeployStatus(
78
81
  accountId,
79
- projectConfig.name,
82
+ projectName,
80
83
  deployResp.id,
81
84
  deployedBuildId
82
85
  );
@@ -84,7 +87,7 @@ exports.handler = async options => {
84
87
  if (e.statusCode === 400) {
85
88
  logger.error(e.error.message);
86
89
  } else {
87
- logApiErrorInstance(e, new ApiErrorContext({ accountId, projectPath }));
90
+ logApiErrorInstance(e, new ApiErrorContext({ accountId, projectName }));
88
91
  }
89
92
  exitCode = 1;
90
93
  }
@@ -92,20 +95,23 @@ exports.handler = async options => {
92
95
  };
93
96
 
94
97
  exports.builder = yargs => {
95
- yargs.positional('path', {
96
- describe: i18n(`${i18nKey}.positionals.path.describe`),
97
- type: 'string',
98
- });
99
-
100
98
  yargs.options({
99
+ project: {
100
+ describe: i18n(`${i18nKey}.options.project.describe`),
101
+ type: 'string',
102
+ },
101
103
  buildId: {
102
104
  describe: i18n(`${i18nKey}.options.buildId.describe`),
103
105
  type: 'number',
104
106
  },
105
107
  });
106
108
 
109
+ yargs.example([['$0 project deploy', i18n(`${i18nKey}.examples.default`)]]);
107
110
  yargs.example([
108
- ['$0 project deploy myProjectFolder', i18n(`${i18nKey}.examples.default`)],
111
+ [
112
+ '$0 project deploy --project="my-project" --buildId=5',
113
+ i18n(`${i18nKey}.examples.withOptions`),
114
+ ],
109
115
  ]);
110
116
 
111
117
  addConfigOptions(yargs, true);
@@ -6,6 +6,7 @@ const {
6
6
  addUseEnvironmentOptions,
7
7
  addTestingOptions,
8
8
  } = require('../../lib/commonOpts');
9
+ const { trackCommandUsage } = require('../../lib/usageTracking');
9
10
  const { loadAndValidateOptions } = require('../../lib/validation');
10
11
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
11
12
  const { logger } = require('@hubspot/cli-lib/logger');
@@ -27,6 +28,9 @@ exports.handler = async options => {
27
28
 
28
29
  const accountId = getAccountId(options);
29
30
  const { project } = options;
31
+
32
+ trackCommandUsage('project-open', { project }, accountId);
33
+
30
34
  const { projectConfig } = await getProjectConfig();
31
35
 
32
36
  let projectName = project;
@@ -42,7 +46,7 @@ exports.handler = async options => {
42
46
  } else if (!projectName && projectConfig) {
43
47
  projectName = projectConfig.name;
44
48
  } else if (!projectName && !projectConfig) {
45
- const namePrompt = await projectNamePrompt(accountId, projectConfig);
49
+ const namePrompt = await projectNamePrompt(accountId);
46
50
 
47
51
  if (!namePrompt.projectName) {
48
52
  process.exit(EXIT_CODES.ERROR);
@@ -3,6 +3,7 @@ const {
3
3
  addConfigOptions,
4
4
  getAccountId,
5
5
  addUseEnvironmentOptions,
6
+ addTestingOptions,
6
7
  } = require('../../lib/commonOpts');
7
8
  const { trackCommandUsage } = require('../../lib/usageTracking');
8
9
  const { logger } = require('@hubspot/cli-lib/logger');
@@ -11,9 +12,97 @@ const { createSandbox } = require('@hubspot/cli-lib/sandboxes');
11
12
  const { loadAndValidateOptions } = require('../../lib/validation');
12
13
  const { createSandboxPrompt } = require('../../lib/prompts/sandboxesPrompt');
13
14
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
15
+ const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
16
+ const {
17
+ ENVIRONMENTS,
18
+ PERSONAL_ACCESS_KEY_AUTH_METHOD,
19
+ DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
20
+ } = require('@hubspot/cli-lib/lib/constants');
21
+ const {
22
+ personalAccessKeyPrompt,
23
+ } = require('../../lib/prompts/personalAccessKeyPrompt');
24
+ const {
25
+ updateConfigWithPersonalAccessKey,
26
+ } = require('@hubspot/cli-lib/personalAccessKey');
27
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
28
+ const {
29
+ getConfig,
30
+ writeConfig,
31
+ updateAccountConfig,
32
+ getAccountConfig,
33
+ } = require('@hubspot/cli-lib');
34
+ const {
35
+ enterAccountNamePrompt,
36
+ } = require('../../lib/prompts/enterAccountNamePrompt');
37
+ const {
38
+ setAsDefaultAccountPrompt,
39
+ } = require('../../lib/prompts/setAsDefaultAccountPrompt');
40
+ const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
41
+ const {
42
+ isMissingScopeError,
43
+ } = require('@hubspot/cli-lib/errorHandlers/apiErrors');
44
+ const { uiFeatureHighlight } = require('../../lib/ui');
14
45
 
15
46
  const i18nKey = 'cli.commands.sandbox.subcommands.create';
16
47
 
48
+ const personalAccessKeyFlow = async (env, account, name) => {
49
+ const configData = await personalAccessKeyPrompt({ env, account });
50
+ const updatedConfig = await updateConfigWithPersonalAccessKey(configData);
51
+
52
+ if (!updatedConfig) {
53
+ process.exit(EXIT_CODES.SUCCESS);
54
+ }
55
+
56
+ let validName = updatedConfig.name;
57
+
58
+ if (!updatedConfig.name) {
59
+ const nameForConfig = name
60
+ .toLowerCase()
61
+ .split(' ')
62
+ .join('-');
63
+ const { name: promptName } = await enterAccountNamePrompt(nameForConfig);
64
+ validName = promptName;
65
+ }
66
+
67
+ updateAccountConfig({
68
+ ...updatedConfig,
69
+ environment: updatedConfig.env,
70
+ tokenInfo: updatedConfig.auth.tokenInfo,
71
+ name: validName,
72
+ });
73
+ writeConfig();
74
+
75
+ const setAsDefault = await setAsDefaultAccountPrompt(validName);
76
+
77
+ logger.log('');
78
+ if (setAsDefault) {
79
+ logger.success(
80
+ i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.setAsDefaultAccount`, {
81
+ accountName: validName,
82
+ })
83
+ );
84
+ } else {
85
+ const config = getConfig();
86
+ logger.info(
87
+ i18n(`cli.lib.prompts.setAsDefaultAccountPrompt.keepingCurrentDefault`, {
88
+ accountName: config.defaultPortal,
89
+ })
90
+ );
91
+ }
92
+ logger.success(
93
+ i18n(`${i18nKey}.success.configFileUpdated`, {
94
+ configFilename: DEFAULT_HUBSPOT_CONFIG_YAML_FILE_NAME,
95
+ authMethod: PERSONAL_ACCESS_KEY_AUTH_METHOD.name,
96
+ account: validName,
97
+ })
98
+ );
99
+ uiFeatureHighlight([
100
+ 'accountsUseCommand',
101
+ 'accountOption',
102
+ 'accountsListCommand',
103
+ ]);
104
+ };
105
+
17
106
  exports.command = 'create [name]';
18
107
  exports.describe = i18n(`${i18nKey}.describe`);
19
108
 
@@ -22,6 +111,8 @@ exports.handler = async options => {
22
111
 
23
112
  const { name } = options;
24
113
  const accountId = getAccountId(options);
114
+ const accountConfig = getAccountConfig(accountId);
115
+ const env = options.qa ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD;
25
116
  let namePrompt;
26
117
 
27
118
  trackCommandUsage('sandbox-create', {}, accountId);
@@ -37,18 +128,44 @@ exports.handler = async options => {
37
128
  name: sandboxName,
38
129
  })
39
130
  );
40
-
41
- return createSandbox(accountId, sandboxName).then(
42
- ({ name, sandboxHubId }) => {
43
- logger.success(
44
- i18n(`${i18nKey}.success.create`, {
45
- name,
46
- sandboxHubId,
131
+ let result;
132
+ try {
133
+ result = await createSandbox(accountId, sandboxName).then(
134
+ ({ name, sandboxHubId }) => {
135
+ logger.log('');
136
+ logger.success(
137
+ i18n(`${i18nKey}.success.create`, {
138
+ name,
139
+ sandboxHubId,
140
+ })
141
+ );
142
+ return { name, sandboxHubId };
143
+ }
144
+ );
145
+ } catch (err) {
146
+ if (isMissingScopeError(err)) {
147
+ logger.error(
148
+ i18n(`${i18nKey}.failure.scopes.message`, {
149
+ accountName: accountConfig.name || accountId,
150
+ })
151
+ );
152
+ const websiteOrigin = getHubSpotWebsiteOrigin(env);
153
+ const url = `${websiteOrigin}/personal-access-key/${accountId}`;
154
+ logger.info(
155
+ i18n(`${i18nKey}.failure.scopes.instructions`, {
156
+ accountName: accountConfig.name || accountId,
157
+ url,
47
158
  })
48
159
  );
49
- logger.info(i18n(`${i18nKey}.info.auth`));
50
160
  }
51
- );
161
+ process.exit(EXIT_CODES.ERROR);
162
+ }
163
+ try {
164
+ await personalAccessKeyFlow(env, result.sandboxHubId, result.name);
165
+ process.exit(EXIT_CODES.SUCCESS);
166
+ } catch (err) {
167
+ logErrorInstance(err);
168
+ }
52
169
  };
53
170
 
54
171
  exports.builder = yargs => {
@@ -64,6 +181,7 @@ exports.builder = yargs => {
64
181
  addConfigOptions(yargs, true);
65
182
  addAccountOptions(yargs, true);
66
183
  addUseEnvironmentOptions(yargs, true);
184
+ addTestingOptions(yargs, true);
67
185
 
68
186
  return yargs;
69
187
  };
@@ -26,6 +26,7 @@ const {
26
26
  getAccountId,
27
27
  getMode,
28
28
  } = require('../lib/commonOpts');
29
+ const { uploadPrompt } = require('../lib/prompts/uploadPrompt');
29
30
  const { validateMode, loadAndValidateOptions } = require('../lib/validation');
30
31
  const { trackCommandUsage } = require('../lib/usageTracking');
31
32
  const { getThemePreviewUrl } = require('@hubspot/cli-lib/lib/files');
@@ -34,7 +35,7 @@ const { i18n } = require('@hubspot/cli-lib/lib/lang');
34
35
  const i18nKey = 'cli.commands.upload';
35
36
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
36
37
 
37
- exports.command = 'upload <src> <dest>';
38
+ exports.command = 'upload [--src] [--dest]';
38
39
  exports.describe = i18n(`${i18nKey}.describe`);
39
40
 
40
41
  const logThemePreview = (filePath, accountId) => {
@@ -50,8 +51,6 @@ const logThemePreview = (filePath, accountId) => {
50
51
  };
51
52
 
52
53
  exports.handler = async options => {
53
- const { src, dest } = options;
54
-
55
54
  await loadAndValidateOptions(options);
56
55
 
57
56
  if (!validateMode(options)) {
@@ -60,6 +59,12 @@ exports.handler = async options => {
60
59
 
61
60
  const accountId = getAccountId(options);
62
61
  const mode = getMode(options);
62
+
63
+ const uploadPromptAnswers = await uploadPrompt(options);
64
+
65
+ const src = options.src || uploadPromptAnswers.src;
66
+ const dest = options.dest || uploadPromptAnswers.dest;
67
+
63
68
  const absoluteSrcPath = path.resolve(getCwd(), src);
64
69
  let stats;
65
70
  try {
package/commands/watch.js CHANGED
@@ -13,6 +13,7 @@ const {
13
13
  getAccountId,
14
14
  getMode,
15
15
  } = require('../lib/commonOpts');
16
+ const { uploadPrompt } = require('../lib/prompts/uploadPrompt');
16
17
  const { validateMode, loadAndValidateOptions } = require('../lib/validation');
17
18
  const { trackCommandUsage } = require('../lib/usageTracking');
18
19
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
@@ -20,11 +21,11 @@ const { i18n } = require('@hubspot/cli-lib/lib/lang');
20
21
  const i18nKey = 'cli.commands.watch';
21
22
  const { EXIT_CODES } = require('../lib/enums/exitCodes');
22
23
 
23
- exports.command = 'watch <src> <dest>';
24
+ exports.command = 'watch [--src] [--dest]';
24
25
  exports.describe = i18n(`${i18nKey}.describe`);
25
26
 
26
27
  exports.handler = async options => {
27
- const { src, dest, remove, initialUpload, disableInitial, notify } = options;
28
+ const { remove, initialUpload, disableInitial, notify } = options;
28
29
 
29
30
  await loadAndValidateOptions(options);
30
31
 
@@ -35,6 +36,11 @@ exports.handler = async options => {
35
36
  const accountId = getAccountId(options);
36
37
  const mode = getMode(options);
37
38
 
39
+ const uploadPromptAnswers = await uploadPrompt(options);
40
+
41
+ const src = options.src || uploadPromptAnswers.src;
42
+ const dest = options.dest || uploadPromptAnswers.dest;
43
+
38
44
  const absoluteSrcPath = path.resolve(getCwd(), src);
39
45
  try {
40
46
  const stats = fs.statSync(absoluteSrcPath);
package/lib/projects.js CHANGED
@@ -165,7 +165,7 @@ const validateProjectConfig = (projectConfig, projectDir) => {
165
165
  const ensureProjectExists = async (
166
166
  accountId,
167
167
  projectName,
168
- { forceCreate = false, allowCreate = true } = {}
168
+ { forceCreate = false, allowCreate = true, noLogs = false } = {}
169
169
  ) => {
170
170
  try {
171
171
  const project = await fetchProject(accountId, projectName);
@@ -194,11 +194,13 @@ const ensureProjectExists = async (
194
194
  return logApiErrorInstance(err, new ApiErrorContext({ accountId }));
195
195
  }
196
196
  } else {
197
- logger.log(
198
- `Your project ${chalk.bold(
199
- projectName
200
- )} could not be found in ${chalk.bold(accountId)}.`
201
- );
197
+ if (!noLogs) {
198
+ logger.log(
199
+ `Your project ${chalk.bold(
200
+ projectName
201
+ )} could not be found in ${chalk.bold(accountId)}.`
202
+ );
203
+ }
202
204
  return false;
203
205
  }
204
206
  }
@@ -211,7 +213,7 @@ const getProjectDetailUrl = (projectName, accountId) => {
211
213
  if (!projectName) return;
212
214
 
213
215
  const baseUrl = getHubSpotWebsiteOrigin(
214
- getEnv() === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
216
+ getEnv(accountId) === 'qa' ? ENVIRONMENTS.QA : ENVIRONMENTS.PROD
215
217
  );
216
218
 
217
219
  return `${baseUrl}/developer-projects/${accountId}/project/${projectName}`;
@@ -324,26 +326,6 @@ const handleProjectUpload = async (
324
326
  archive.finalize();
325
327
  };
326
328
 
327
- const showProjectWelcomeMessage = () => {
328
- logger.log('');
329
- logger.log(chalk.bold('Welcome to HubSpot Developer Projects!'));
330
- logger.log('\n');
331
- uiLine();
332
- logger.log('\n');
333
- logger.log(chalk.bold("What's next?\n"));
334
- logger.log('🎨 Add components to your project with `hs create`.\n');
335
- logger.log(
336
- `🏗 Run \`hs project upload\` to upload your files to HubSpot and trigger builds.\n`
337
- );
338
- logger.log(
339
- `🚀 Ready to take your project live? Run \`hs project deploy\`.\n`
340
- );
341
- logger.log(
342
- `🔗 Use \`hs project --help\` to learn more about available commands.\n`
343
- );
344
- uiLine();
345
- };
346
-
347
329
  const makePollTaskStatusFunc = ({
348
330
  statusFn,
349
331
  statusText,
@@ -519,7 +501,6 @@ module.exports = {
519
501
  handleProjectUpload,
520
502
  createProjectConfig,
521
503
  validateProjectConfig,
522
- showProjectWelcomeMessage,
523
504
  getProjectDetailUrl,
524
505
  getProjectBuildDetailUrl,
525
506
  pollBuildStatus,
@@ -0,0 +1,33 @@
1
+ const { accountNameExistsInConfig } = require('@hubspot/cli-lib/lib/config');
2
+ const { STRING_WITH_NO_SPACES_REGEX } = require('../regex');
3
+ const { promptUser } = require('./promptUtils');
4
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
5
+
6
+ const i18nKey = 'cli.lib.prompts.enterAccountNamePrompt';
7
+
8
+ const accountNamePrompt = defaultName => ({
9
+ name: 'name',
10
+ message: i18n(`${i18nKey}.enterAccountName`),
11
+ default: defaultName,
12
+ validate(val) {
13
+ if (typeof val !== 'string') {
14
+ return i18n(`${i18nKey}.errors.invalidName`);
15
+ } else if (!val.length) {
16
+ return i18n(`${i18nKey}.errors.nameRequired`);
17
+ } else if (!STRING_WITH_NO_SPACES_REGEX.test(val)) {
18
+ return i18n(`${i18nKey}.errors.spacesInName`);
19
+ }
20
+ return accountNameExistsInConfig(val)
21
+ ? i18n(`${i18nKey}.errors.accountNameExists`, { name: val })
22
+ : true;
23
+ },
24
+ });
25
+
26
+ const enterAccountNamePrompt = defaultName => {
27
+ return promptUser(accountNamePrompt(defaultName));
28
+ };
29
+
30
+ module.exports = {
31
+ accountNamePrompt,
32
+ enterAccountNamePrompt,
33
+ };
@@ -3,11 +3,15 @@ const {
3
3
  OAUTH_SCOPES,
4
4
  DEFAULT_OAUTH_SCOPES,
5
5
  } = require('@hubspot/cli-lib/lib/constants');
6
+ const { deleteEmptyConfigFile } = require('@hubspot/cli-lib/lib/config');
6
7
  const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
7
8
  const { logger } = require('@hubspot/cli-lib/logger');
8
- const { API_KEY_REGEX, STRING_WITH_NO_SPACES_REGEX } = require('../regex');
9
+ const { API_KEY_REGEX } = require('../regex');
9
10
  const { promptUser } = require('./promptUtils');
11
+ const { accountNamePrompt } = require('./enterAccountNamePrompt');
10
12
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
13
+ const { uiInfoSection } = require('../ui');
14
+ const { EXIT_CODES } = require('../enums/exitCodes');
11
15
 
12
16
  const i18nKey = 'cli.lib.prompts.personalAccessKeyPrompt';
13
17
 
@@ -15,12 +19,25 @@ const i18nKey = 'cli.lib.prompts.personalAccessKeyPrompt';
15
19
  * Displays notification to user that we are about to open the browser,
16
20
  * then opens their browser to the personal-access-key shortlink
17
21
  */
18
- const personalAccessKeyPrompt = async ({ env } = {}) => {
22
+ const personalAccessKeyPrompt = async ({ env, account } = {}) => {
19
23
  const websiteOrigin = getHubSpotWebsiteOrigin(env);
20
- const url = `${websiteOrigin}/l/personal-access-key`;
24
+ let url = `${websiteOrigin}/l/personal-access-key`;
21
25
  if (process.env.BROWSER !== 'none') {
22
- await promptUser([PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP]);
23
- open(url, { url: true });
26
+ uiInfoSection(i18n(`${i18nKey}.personalAccessKeySetupTitle`), () => {
27
+ logger.log(i18n(`${i18nKey}.personalAccessKeyBrowserOpenPrep`));
28
+ });
29
+ if (account) {
30
+ url = `${websiteOrigin}/personal-access-key/${account}`;
31
+ }
32
+ const { personalAcessKeyBrowserOpenPrep: shouldOpen } = await promptUser([
33
+ PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP,
34
+ ]);
35
+ if (shouldOpen) {
36
+ open(url, { url: true });
37
+ } else {
38
+ deleteEmptyConfigFile();
39
+ process.exit(EXIT_CODES.SUCCESS);
40
+ }
24
41
  }
25
42
 
26
43
  logger.log(i18n(`${i18nKey}.logs.openingWebBrowser`, { url }));
@@ -72,21 +89,6 @@ const CLIENT_SECRET = {
72
89
  },
73
90
  };
74
91
 
75
- const ACCOUNT_NAME = {
76
- name: 'name',
77
- message: i18n(`${i18nKey}.enterAccountName`),
78
- validate(val) {
79
- if (typeof val !== 'string') {
80
- return i18n(`${i18nKey}.errors.invalidName`);
81
- } else if (!val.length) {
82
- return i18n(`${i18nKey}.errors.nameRequired`);
83
- } else if (!STRING_WITH_NO_SPACES_REGEX.test(val)) {
84
- return i18n(`${i18nKey}.errors.spacesInName`);
85
- }
86
- return true;
87
- },
88
- };
89
-
90
92
  const ACCOUNT_API_KEY = {
91
93
  name: 'apiKey',
92
94
  message: i18n(`${i18nKey}.enterApiKey`),
@@ -100,12 +102,21 @@ const ACCOUNT_API_KEY = {
100
102
 
101
103
  const PERSONAL_ACCESS_KEY_BROWSER_OPEN_PREP = {
102
104
  name: 'personalAcessKeyBrowserOpenPrep',
103
- message: i18n(`${i18nKey}.personalAccessKeyBrowserOpenPrep`),
105
+ type: 'confirm',
106
+ message: i18n(`${i18nKey}.personalAccessKeyBrowserOpenPrompt`),
104
107
  };
105
108
 
106
109
  const PERSONAL_ACCESS_KEY = {
107
110
  name: 'personalAccessKey',
108
111
  message: i18n(`${i18nKey}.enterPersonalAccessKey`),
112
+ transformer: val => {
113
+ if (!val) return val;
114
+ let res = '';
115
+ for (let i = 0; i < val.length; i++) {
116
+ res += '*';
117
+ }
118
+ return res;
119
+ },
109
120
  validate(val) {
110
121
  if (!val || typeof val !== 'string') {
111
122
  return i18n(`${i18nKey}.errors.invalidPersonalAccessKey`);
@@ -124,8 +135,14 @@ const SCOPES = {
124
135
  choices: OAUTH_SCOPES,
125
136
  };
126
137
 
127
- const OAUTH_FLOW = [ACCOUNT_NAME, ACCOUNT_ID, CLIENT_ID, CLIENT_SECRET, SCOPES];
128
- const API_KEY_FLOW = [ACCOUNT_NAME, ACCOUNT_ID, ACCOUNT_API_KEY];
138
+ const OAUTH_FLOW = [
139
+ accountNamePrompt(),
140
+ ACCOUNT_ID,
141
+ CLIENT_ID,
142
+ CLIENT_SECRET,
143
+ SCOPES,
144
+ ];
145
+ const API_KEY_FLOW = [accountNamePrompt(), ACCOUNT_ID, ACCOUNT_API_KEY];
129
146
 
130
147
  module.exports = {
131
148
  personalAccessKeyPrompt,
@@ -133,10 +150,8 @@ module.exports = {
133
150
  CLIENT_SECRET,
134
151
  ACCOUNT_API_KEY,
135
152
  ACCOUNT_ID,
136
- ACCOUNT_NAME,
137
153
  SCOPES,
138
154
  PERSONAL_ACCESS_KEY,
139
-
140
155
  // Flows
141
156
  API_KEY_FLOW,
142
157
  OAUTH_FLOW,
@@ -4,20 +4,24 @@ const { ensureProjectExists } = require('../projects');
4
4
 
5
5
  const i18nKey = 'cli.lib.prompts.projectNamePrompt';
6
6
 
7
- const projectNamePrompt = accountId => {
7
+ const projectNamePrompt = (accountId, options = {}) => {
8
8
  return promptUser({
9
9
  name: 'projectName',
10
10
  message: i18n(`${i18nKey}.enterName`),
11
+ when: !options.project,
11
12
  validate: async val => {
12
- if (typeof val !== 'string') {
13
+ if (typeof val !== 'string' || !val) {
13
14
  return i18n(`${i18nKey}.errors.invalidName`);
14
15
  }
15
16
  const projectExists = await ensureProjectExists(accountId, val, {
16
17
  allowCreate: false,
18
+ noLogs: true,
17
19
  });
18
-
19
20
  if (!projectExists) {
20
- return false;
21
+ return i18n(`${i18nKey}.errors.projectDoesNotExist`, {
22
+ projectName: val,
23
+ accountId,
24
+ });
21
25
  }
22
26
  return true;
23
27
  },
@@ -0,0 +1,30 @@
1
+ const {
2
+ getConfig,
3
+ updateDefaultAccount,
4
+ } = require('@hubspot/cli-lib/lib/config');
5
+ const { promptUser } = require('./promptUtils');
6
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
7
+
8
+ const i18nKey = 'cli.lib.prompts.setAsDefaultAccountPrompt';
9
+
10
+ const setAsDefaultAccountPrompt = async accountName => {
11
+ const config = getConfig();
12
+
13
+ const { setAsDefault } = await promptUser([
14
+ {
15
+ name: 'setAsDefault',
16
+ type: 'confirm',
17
+ when: config.defaultPortal !== accountName,
18
+ message: i18n(`${i18nKey}.setAsDefaultAccountMessage`),
19
+ },
20
+ ]);
21
+
22
+ if (setAsDefault) {
23
+ updateDefaultAccount(accountName);
24
+ }
25
+ return setAsDefault;
26
+ };
27
+
28
+ module.exports = {
29
+ setAsDefaultAccountPrompt,
30
+ };
@@ -0,0 +1,39 @@
1
+ const path = require('path');
2
+ const { getCwd } = require('@hubspot/cli-lib/path');
3
+ const { promptUser } = require('./promptUtils');
4
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
5
+
6
+ const i18nKey = 'cli.lib.prompts.uploadPrompt';
7
+
8
+ const uploadPrompt = (promptOptions = {}) => {
9
+ return promptUser([
10
+ {
11
+ name: 'src',
12
+ message: i18n(`${i18nKey}.enterSrc`),
13
+ when: !promptOptions.src,
14
+ default: '.',
15
+ validate: input => {
16
+ if (!input) {
17
+ return i18n(`${i18nKey}.errors.srcRequired`);
18
+ }
19
+ return true;
20
+ },
21
+ },
22
+ {
23
+ name: 'dest',
24
+ message: i18n(`${i18nKey}.enterDest`),
25
+ when: !promptOptions.dest,
26
+ default: path.basename(getCwd()),
27
+ validate: input => {
28
+ if (!input) {
29
+ return i18n(`${i18nKey}.errors.destRequired`);
30
+ }
31
+ return true;
32
+ },
33
+ },
34
+ ]);
35
+ };
36
+
37
+ module.exports = {
38
+ uploadPrompt,
39
+ };
package/lib/ui.js CHANGED
@@ -2,6 +2,7 @@ const chalk = require('chalk');
2
2
  const supportsHyperlinks = require('../lib/supportHyperlinks');
3
3
  const supportsColor = require('../lib/supportsColor');
4
4
  const { getAccountConfig } = require('@hubspot/cli-lib/lib/config');
5
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
5
6
  const { logger } = require('@hubspot/cli-lib/logger');
6
7
 
7
8
  /**
@@ -65,8 +66,36 @@ const uiAccountDescription = accountId => {
65
66
  );
66
67
  };
67
68
 
69
+ const uiInfoSection = (title, logContent) => {
70
+ uiLine();
71
+ logger.log(chalk.bold(title));
72
+ logger.log('');
73
+ logContent();
74
+ logger.log('');
75
+ uiLine();
76
+ };
77
+
78
+ const uiFeatureHighlight = (commands, title) => {
79
+ const i18nKey = 'cli.lib.ui.featureHighlight';
80
+
81
+ uiInfoSection(title ? title : i18n(`${i18nKey}.defaultTitle`), () => {
82
+ commands.forEach((c, i) => {
83
+ const commandKey = `${i18nKey}.commandKeys.${c}`;
84
+ const message = i18n(`${commandKey}.message`, {
85
+ command: chalk.bold(i18n(`${commandKey}.command`)),
86
+ });
87
+ if (i !== 0) {
88
+ logger.log('');
89
+ }
90
+ logger.log(message);
91
+ });
92
+ });
93
+ };
94
+
68
95
  module.exports = {
96
+ uiAccountDescription,
97
+ uiFeatureHighlight,
98
+ uiInfoSection,
69
99
  uiLine,
70
100
  uiLink,
71
- uiAccountDescription,
72
101
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "3.0.13-beta.3",
3
+ "version": "4.0.1-beta.0",
4
4
  "description": "CLI for working with HubSpot",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,8 +8,8 @@
8
8
  "url": "https://github.com/HubSpot/hubspot-cms-tools"
9
9
  },
10
10
  "dependencies": {
11
- "@hubspot/cli-lib": "3.0.13-beta.3",
12
- "@hubspot/serverless-dev-runtime": "3.0.13-beta.3",
11
+ "@hubspot/cli-lib": "4.0.1-beta.0",
12
+ "@hubspot/serverless-dev-runtime": "4.0.1-beta.0",
13
13
  "archiver": "^5.3.0",
14
14
  "chalk": "^4.1.2",
15
15
  "express": "^4.17.1",
@@ -37,5 +37,5 @@
37
37
  "publishConfig": {
38
38
  "access": "public"
39
39
  },
40
- "gitHead": "34e55c31dee127f375faaddbbe0f71940c01c860"
40
+ "gitHead": "fb23906d7f8694f9de4bb43043127a74f5b63a29"
41
41
  }