@hubspot/cli 3.0.10-beta.6 → 3.0.10

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.
Files changed (90) hide show
  1. package/README.md +7 -1
  2. package/bin/cli.js +3 -4
  3. package/commands/accounts/list.js +18 -26
  4. package/commands/accounts/rename.js +13 -24
  5. package/commands/accounts.js +4 -1
  6. package/commands/auth.js +33 -16
  7. package/commands/config/set/allowUsageTracking.js +17 -33
  8. package/commands/config/set/defaultAccount.js +24 -34
  9. package/commands/config/set/defaultMode.js +25 -44
  10. package/commands/config/set/httpTimeout.js +10 -28
  11. package/commands/config/set.js +4 -1
  12. package/commands/config.js +4 -1
  13. package/commands/create/api-sample.js +20 -14
  14. package/commands/create/function.js +3 -1
  15. package/commands/create/index.js +0 -1
  16. package/commands/create/module.js +20 -7
  17. package/commands/create/template.js +22 -5
  18. package/commands/create/website-theme.js +12 -1
  19. package/commands/create.js +23 -8
  20. package/commands/customObject/create.js +22 -24
  21. package/commands/customObject/schema/create.js +30 -28
  22. package/commands/customObject/schema/delete.js +20 -20
  23. package/commands/customObject/schema/fetch-all.js +17 -24
  24. package/commands/customObject/schema/fetch.js +29 -24
  25. package/commands/customObject/schema/list.js +8 -17
  26. package/commands/customObject/schema/update.js +31 -29
  27. package/commands/customObject/schema.js +4 -1
  28. package/commands/customObject.js +10 -21
  29. package/commands/fetch.js +15 -30
  30. package/commands/filemanager/fetch.js +13 -25
  31. package/commands/filemanager/upload.js +47 -35
  32. package/commands/filemanager.js +4 -1
  33. package/commands/functions/deploy.js +34 -37
  34. package/commands/functions/list.js +9 -24
  35. package/commands/functions/server.js +13 -29
  36. package/commands/functions.js +4 -1
  37. package/commands/hubdb/clear.js +25 -21
  38. package/commands/hubdb/create.js +25 -22
  39. package/commands/hubdb/delete.js +19 -20
  40. package/commands/hubdb/fetch.js +15 -20
  41. package/commands/hubdb.js +4 -1
  42. package/commands/init.js +25 -13
  43. package/commands/lint.js +14 -23
  44. package/commands/list.js +19 -25
  45. package/commands/logs.js +23 -44
  46. package/commands/mv.js +21 -25
  47. package/commands/open.js +9 -7
  48. package/commands/project/create.js +26 -42
  49. package/commands/project/deploy.js +35 -36
  50. package/commands/project/listBuilds.js +160 -0
  51. package/commands/project/logs.js +87 -79
  52. package/commands/project/upload.js +74 -78
  53. package/commands/project/watch.js +103 -0
  54. package/commands/project.js +5 -6
  55. package/commands/remove.js +12 -20
  56. package/commands/sandbox/create.js +18 -13
  57. package/commands/secrets/addSecret.js +19 -22
  58. package/commands/secrets/deleteSecret.js +18 -21
  59. package/commands/secrets/listSecrets.js +10 -19
  60. package/commands/secrets/updateSecret.js +19 -22
  61. package/commands/secrets.js +4 -1
  62. package/commands/server.js +15 -6
  63. package/commands/{marketplaceValidate/validateTheme.js → theme/marketplace-validate.js} +26 -24
  64. package/commands/theme.js +5 -3
  65. package/commands/upload.js +66 -45
  66. package/commands/watch.js +33 -55
  67. package/lib/commonOpts.js +14 -11
  68. package/lib/enums/exitCodes.js +14 -0
  69. package/lib/links.js +0 -10
  70. package/lib/projects.js +121 -77
  71. package/lib/{createApiSamplePrompt.js → prompts/createApiSamplePrompt.js} +10 -11
  72. package/lib/{createFunctionPrompt.js → prompts/createFunctionPrompt.js} +27 -21
  73. package/lib/{createModulePrompt.js → prompts/createModulePrompt.js} +12 -9
  74. package/lib/prompts/createProjectPrompt.js +68 -0
  75. package/lib/{createTemplatePrompt.js → prompts/createTemplatePrompt.js} +7 -4
  76. package/lib/prompts/folderOverwritePrompt.js +17 -0
  77. package/lib/{prompts.js → prompts/personalAccessKeyPrompt.js} +25 -42
  78. package/lib/prompts/promptUtils.js +10 -0
  79. package/lib/prompts/sandboxesPrompt.js +24 -0
  80. package/lib/prompts/secretPrompt.js +25 -0
  81. package/lib/serverlessLogs.js +4 -3
  82. package/lib/ui.js +48 -0
  83. package/lib/validation.js +4 -3
  84. package/package.json +8 -7
  85. package/commands/app/deploy.js +0 -116
  86. package/commands/app.js +0 -14
  87. package/commands/create/project.js +0 -25
  88. package/lib/prompts/projects.js +0 -40
  89. package/lib/prompts/sandboxes.js +0 -22
  90. package/lib/secretPrompt.js +0 -22
@@ -1,48 +1,51 @@
1
1
  const Spinnies = require('spinnies');
2
+ const { getCwd } = require('@hubspot/cli-lib/path');
2
3
  const {
3
4
  addAccountOptions,
4
5
  addConfigOptions,
5
- setLogLevel,
6
6
  getAccountId,
7
7
  addUseEnvironmentOptions,
8
8
  } = require('../../lib/commonOpts');
9
9
  const { trackCommandUsage } = require('../../lib/usageTracking');
10
- const { logDebugInfo } = require('../../lib/debugInfo');
11
- const {
12
- loadConfig,
13
- validateConfig,
14
- checkAndWarnGitInclusion,
15
- } = require('@hubspot/cli-lib');
16
10
  const { logger } = require('@hubspot/cli-lib/logger');
17
11
  const { outputLogs } = require('@hubspot/cli-lib/lib/logs');
18
12
  const {
19
13
  getProjectAppFunctionLogs,
20
14
  getLatestProjectAppFunctionLog,
21
15
  } = require('@hubspot/cli-lib/api/functions');
22
- const { validateAccount } = require('../../lib/validation');
16
+ const {
17
+ getFunctionLogs,
18
+ getLatestFunctionLog,
19
+ } = require('@hubspot/cli-lib/api/results');
20
+ const { getProjectConfig } = require('../../lib/projects');
21
+ const { loadAndValidateOptions } = require('../../lib/validation');
23
22
  const { tailLogs } = require('../../lib/serverlessLogs');
23
+ const { uiAccountDescription } = require('../../lib/ui');
24
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
24
25
 
25
- const loadAndValidateOptions = async options => {
26
- setLogLevel(options);
27
- logDebugInfo(options);
28
- const { config: configPath } = options;
29
- loadConfig(configPath, options);
30
- checkAndWarnGitInclusion();
31
-
32
- if (!(validateConfig() && (await validateAccount(options)))) {
33
- process.exit(1);
34
- }
35
- };
26
+ const i18nKey = 'cli.commands.project.subcommands.logs';
27
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
36
28
 
37
29
  const handleLogsError = (e, accountId, projectName, appPath, functionName) => {
38
30
  if (e.statusCode === 404) {
39
31
  logger.error(
40
- `No logs were found for the function name '${functionName}' in the app path '${appPath}' within the project '${projectName}' in account ${accountId}.`
32
+ appPath
33
+ ? i18n(`${i18nKey}.errors.noAppFunctionLogs`, {
34
+ accountId,
35
+ appPath,
36
+ functionName,
37
+ projectName,
38
+ })
39
+ : i18n(`${i18nKey}.errors.noEndpointLogs`, {
40
+ accountId,
41
+ functionName,
42
+ projectName,
43
+ })
41
44
  );
42
45
  }
43
46
  };
44
47
 
45
- const appFunctionLog = async (accountId, options) => {
48
+ const functionLog = async (accountId, options) => {
46
49
  const {
47
50
  latest,
48
51
  follow,
@@ -54,28 +57,43 @@ const appFunctionLog = async (accountId, options) => {
54
57
 
55
58
  let logsResp;
56
59
 
60
+ const tailCall = async after => {
61
+ try {
62
+ return appPath
63
+ ? getProjectAppFunctionLogs(
64
+ accountId,
65
+ functionName,
66
+ projectName,
67
+ appPath,
68
+ {
69
+ after,
70
+ }
71
+ )
72
+ : getFunctionLogs(accountId, functionName, { after });
73
+ } catch (e) {
74
+ handleLogsError(e, accountId, projectName, appPath, functionName);
75
+ }
76
+ };
77
+ const fetchLatest = async () => {
78
+ return appPath
79
+ ? getLatestProjectAppFunctionLog(
80
+ accountId,
81
+ functionName,
82
+ projectName,
83
+ appPath
84
+ )
85
+ : getLatestFunctionLog(accountId, functionName);
86
+ };
87
+
57
88
  if (follow) {
58
89
  const spinnies = new Spinnies();
59
90
 
60
91
  spinnies.add('tailLogs', {
61
- text: `Waiting for log entries for '${functionName}' on account '${accountId}'.\n`,
92
+ text: i18n(`${i18nKey}.loading`, {
93
+ functionName,
94
+ accountId: uiAccountDescription(accountId),
95
+ }),
62
96
  });
63
- const tailCall = after =>
64
- getProjectAppFunctionLogs(accountId, functionName, projectName, appPath, {
65
- after,
66
- });
67
- const fetchLatest = () => {
68
- try {
69
- return getLatestProjectAppFunctionLog(
70
- accountId,
71
- functionName,
72
- projectName,
73
- appPath
74
- );
75
- } catch (e) {
76
- handleLogsError(e, accountId, projectName, appPath, functionName);
77
- }
78
- };
79
97
 
80
98
  await tailLogs({
81
99
  accountId,
@@ -86,24 +104,13 @@ const appFunctionLog = async (accountId, options) => {
86
104
  });
87
105
  } else if (latest) {
88
106
  try {
89
- logsResp = await getLatestProjectAppFunctionLog(
90
- accountId,
91
- functionName,
92
- projectName,
93
- appPath
94
- );
107
+ logsResp = await fetchLatest();
95
108
  } catch (e) {
96
109
  handleLogsError(e, accountId, projectName, appPath, functionName);
97
110
  }
98
111
  } else {
99
112
  try {
100
- logsResp = await getProjectAppFunctionLogs(
101
- accountId,
102
- functionName,
103
- projectName,
104
- appPath,
105
- {}
106
- );
113
+ logsResp = await tailCall();
107
114
  } catch (e) {
108
115
  handleLogsError(e, accountId, projectName, appPath, functionName);
109
116
  }
@@ -114,68 +121,69 @@ const appFunctionLog = async (accountId, options) => {
114
121
  }
115
122
  };
116
123
 
117
- exports.command = 'logs [functionName]';
118
- exports.describe = 'get logs for a function within a project';
124
+ exports.command = 'logs';
125
+ exports.describe = i18n(`${i18nKey}.describe`);
119
126
 
120
127
  exports.handler = async options => {
121
- loadAndValidateOptions(options);
128
+ await loadAndValidateOptions(options);
122
129
 
123
- const { latest, functionName, projectName, appPath } = options;
130
+ const { latest, functionName } = options;
131
+ let projectName = options.projectName;
124
132
 
125
133
  if (!functionName) {
126
- logger.error('You must pass a function name to retrieve logs for.');
127
- process.exit(0);
134
+ logger.error(i18n(`${i18nKey}.errors.functionNameRequired`));
135
+ process.exit(EXIT_CODES.ERROR);
128
136
  } else if (!projectName) {
129
- logger.error(
130
- 'You must specify a project name using the --projectName argument.'
131
- );
132
- process.exit(0);
133
- } else if (!appPath) {
134
- logger.error('You must specify an app path using the --appPath argument.');
135
- process.exit(0);
137
+ const { projectConfig } = await getProjectConfig(getCwd());
138
+ if (projectConfig && projectConfig.name) {
139
+ projectName = projectConfig.name;
140
+ } else {
141
+ logger.error(i18n(`${i18nKey}.errors.projectNameRequired`));
142
+ process.exit(EXIT_CODES.ERROR);
143
+ }
136
144
  }
137
145
 
138
146
  const accountId = getAccountId(options);
139
147
 
140
148
  trackCommandUsage('project-logs', { latest }, accountId);
141
149
 
142
- appFunctionLog(accountId, options);
150
+ functionLog(accountId, { ...options, projectName });
143
151
  };
144
152
 
145
153
  exports.builder = yargs => {
146
- yargs.positional('functionName', {
147
- describe: 'Serverless function name',
148
- type: 'string',
149
- });
150
154
  yargs
151
155
  .options({
156
+ functionName: {
157
+ alias: 'function',
158
+ describe: i18n(`${i18nKey}.positionals.functionName.describe`),
159
+ type: 'string',
160
+ demandOption: true,
161
+ },
152
162
  appPath: {
153
- describe: 'path to the app',
163
+ describe: i18n(`${i18nKey}.options.appPath.describe`),
154
164
  type: 'string',
155
- hidden: true,
156
165
  },
157
166
  projectName: {
158
- describe: 'name of the project',
167
+ describe: i18n(`${i18nKey}.options.projectName.describe`),
159
168
  type: 'string',
160
- hidden: true,
161
169
  },
162
170
  latest: {
163
171
  alias: 'l',
164
- describe: 'retrieve most recent log only',
172
+ describe: i18n(`${i18nKey}.options.latest.describe`),
165
173
  type: 'boolean',
166
174
  },
167
175
  compact: {
168
- describe: 'output compact logs',
176
+ describe: i18n(`${i18nKey}.options.compact.describe`),
169
177
  type: 'boolean',
170
178
  },
171
179
  follow: {
172
180
  alias: ['t', 'tail', 'f'],
173
- describe: 'follow logs',
181
+ describe: i18n(`${i18nKey}.options.follow.describe`),
174
182
  type: 'boolean',
175
183
  },
176
184
  limit: {
177
185
  alias: ['limit', 'n', 'max-count'],
178
- describe: 'limit the number of logs to output',
186
+ describe: i18n(`${i18nKey}.options.limit.describe`),
179
187
  type: 'number',
180
188
  },
181
189
  })
@@ -183,8 +191,8 @@ exports.builder = yargs => {
183
191
 
184
192
  yargs.example([
185
193
  [
186
- '$0 project logs my-function --appName="app" --projectName="my-project"',
187
- 'Get 5 most recent logs for function named "my-function" within the app named "app" within the project named "my-project"',
194
+ '$0 project logs --function=my-function --appPath="app" --projectName="my-project"',
195
+ i18n(`${i18nKey}.examples.default`),
188
196
  ],
189
197
  ]);
190
198
 
@@ -7,26 +7,19 @@ const Spinnies = require('spinnies');
7
7
  const {
8
8
  addAccountOptions,
9
9
  addConfigOptions,
10
- setLogLevel,
11
10
  getAccountId,
12
11
  addUseEnvironmentOptions,
13
12
  } = require('../../lib/commonOpts');
14
13
  const { trackCommandUsage } = require('../../lib/usageTracking');
15
- const { logDebugInfo } = require('../../lib/debugInfo');
16
- const {
17
- loadConfig,
18
- validateConfig,
19
- checkAndWarnGitInclusion,
20
- } = require('@hubspot/cli-lib');
21
14
  const {
22
15
  logApiErrorInstance,
23
16
  ApiErrorContext,
24
17
  } = require('@hubspot/cli-lib/errorHandlers');
18
+ const { uiLine, uiAccountDescription } = require('../../lib/ui');
25
19
  const { logger } = require('@hubspot/cli-lib/logger');
26
20
  const { uploadProject } = require('@hubspot/cli-lib/api/dfs');
27
21
  const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');
28
- const { getCwd } = require('@hubspot/cli-lib/path');
29
- const { validateAccount } = require('../../lib/validation');
22
+ const { loadAndValidateOptions } = require('../../lib/validation');
30
23
  const {
31
24
  getProjectConfig,
32
25
  validateProjectConfig,
@@ -34,31 +27,25 @@ const {
34
27
  ensureProjectExists,
35
28
  pollDeployStatus,
36
29
  } = require('../../lib/projects');
30
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
37
31
 
38
- const loadAndValidateOptions = async options => {
39
- setLogLevel(options);
40
- logDebugInfo(options);
41
- const { config: configPath } = options;
42
- loadConfig(configPath, options);
43
- checkAndWarnGitInclusion();
44
-
45
- if (!(validateConfig() && (await validateAccount(options)))) {
46
- process.exit(1);
47
- }
48
- };
32
+ const i18nKey = 'cli.commands.project.subcommands.upload';
33
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
49
34
 
50
35
  exports.command = 'upload [path]';
51
- exports.describe = false;
36
+ exports.describe = i18n(`${i18nKey}.describe`);
52
37
 
53
38
  const uploadProjectFiles = async (accountId, projectName, filePath) => {
54
39
  const spinnies = new Spinnies({
55
40
  succeedColor: 'white',
56
41
  });
42
+ const accountIdentifier = uiAccountDescription(accountId);
57
43
 
58
44
  spinnies.add('upload', {
59
- text: `Uploading ${chalk.bold(projectName)} project files to ${chalk.bold(
60
- accountId
61
- )}`,
45
+ text: i18n(`${i18nKey}.loading.upload.add`, {
46
+ accountIdentifier,
47
+ projectName,
48
+ }),
62
49
  });
63
50
 
64
51
  let buildId;
@@ -69,19 +56,24 @@ const uploadProjectFiles = async (accountId, projectName, filePath) => {
69
56
  buildId = upload.buildId;
70
57
 
71
58
  spinnies.succeed('upload', {
72
- text: `Uploaded ${chalk.bold(projectName)} project files to ${chalk.bold(
73
- accountId
74
- )}`,
59
+ text: i18n(`${i18nKey}.loading.upload.succeed`, {
60
+ accountIdentifier,
61
+ projectName,
62
+ }),
75
63
  });
76
64
 
77
65
  logger.debug(
78
- `Project "${projectName}" uploaded and build #${buildId} created`
66
+ i18n(`${i18nKey}.debug.buildCreated`, {
67
+ buildId,
68
+ projectName,
69
+ })
79
70
  );
80
71
  } catch (err) {
81
72
  spinnies.fail('upload', {
82
- text: `Failed to upload ${chalk.bold(
83
- projectName
84
- )} project files to ${chalk.bold(accountId)}`,
73
+ text: i18n(`${i18nKey}.loading.upload.fail`, {
74
+ accountIdentifier,
75
+ projectName,
76
+ }),
85
77
  });
86
78
 
87
79
  logApiErrorInstance(
@@ -91,24 +83,21 @@ const uploadProjectFiles = async (accountId, projectName, filePath) => {
91
83
  projectName,
92
84
  })
93
85
  );
94
- process.exit(1);
86
+ process.exit(EXIT_CODES.ERROR);
95
87
  }
96
88
 
97
89
  return { buildId };
98
90
  };
99
91
 
100
92
  exports.handler = async options => {
101
- loadAndValidateOptions(options);
93
+ await loadAndValidateOptions(options);
102
94
 
103
95
  const { forceCreate, path: projectPath } = options;
104
96
  const accountId = getAccountId(options);
105
97
 
106
98
  trackCommandUsage('project-upload', { projectPath }, accountId);
107
99
 
108
- const projectDir = projectPath
109
- ? path.resolve(getCwd(), projectPath)
110
- : getCwd();
111
- const projectConfig = await getProjectConfig(projectDir);
100
+ const { projectConfig, projectDir } = await getProjectConfig(projectPath);
112
101
 
113
102
  validateProjectConfig(projectConfig, projectDir);
114
103
 
@@ -116,13 +105,22 @@ exports.handler = async options => {
116
105
 
117
106
  const tempFile = tmp.fileSync({ postfix: '.zip' });
118
107
 
119
- logger.debug(`Compressing build files to '${tempFile.name}'`);
108
+ logger.debug(
109
+ i18n(`${i18nKey}.debug.compressing`, {
110
+ path: tempFile.name,
111
+ })
112
+ );
120
113
 
121
114
  const output = fs.createWriteStream(tempFile.name);
122
115
  const archive = archiver('zip');
123
116
 
124
117
  output.on('close', async function() {
125
- logger.debug(`Project files compressed: ${archive.pointer()} bytes`);
118
+ let exitCode = EXIT_CODES.SUCCESS;
119
+ logger.debug(
120
+ i18n(`${i18nKey}.debug.compressed`, {
121
+ byteCount: archive.pointer(),
122
+ })
123
+ );
126
124
 
127
125
  const { buildId } = await uploadProjectFiles(
128
126
  accountId,
@@ -134,61 +132,57 @@ exports.handler = async options => {
134
132
  isAutoDeployEnabled,
135
133
  deployStatusTaskLocator,
136
134
  status,
137
- subbuildStatuses,
138
135
  } = await pollBuildStatus(accountId, projectConfig.name, buildId);
139
136
 
140
137
  if (status === 'FAILURE') {
141
- const failedSubbuilds = subbuildStatuses.filter(
142
- subbuild => subbuild.status === 'FAILURE'
143
- );
144
-
145
- logger.log('-'.repeat(50));
146
- logger.log(
147
- `Build #${buildId} failed because there was a problem\nbuilding ${
148
- failedSubbuilds.length === 1
149
- ? failedSubbuilds[0].buildName
150
- : failedSubbuilds.length + ' components'
151
- }\n`
152
- );
153
- logger.log('See below for a summary of errors.');
154
- logger.log('-'.repeat(50));
155
-
156
- failedSubbuilds.forEach(subbuild => {
157
- logger.log(
158
- `\n--- ${subbuild.buildName} failed to build with the following error ---`
159
- );
160
- logger.error(subbuild.errorMessage);
161
- });
162
-
138
+ exitCode = EXIT_CODES.ERROR;
163
139
  return;
164
- }
165
-
166
- if (isAutoDeployEnabled && deployStatusTaskLocator) {
140
+ } else if (isAutoDeployEnabled && deployStatusTaskLocator) {
167
141
  logger.log(
168
- `Build #${buildId} succeeded. ${chalk.bold(
169
- 'Automatically deploying'
170
- )} to ${accountId}`
142
+ i18n(`${i18nKey}.logs.buildSucceededAutomaticallyDeploying`, {
143
+ accountIdentifier: uiAccountDescription(accountId),
144
+ buildId,
145
+ })
171
146
  );
172
- await pollDeployStatus(
147
+ const { status } = await pollDeployStatus(
173
148
  accountId,
174
149
  projectConfig.name,
175
150
  deployStatusTaskLocator.id,
176
151
  buildId
177
152
  );
153
+ if (status === 'FAILURE') {
154
+ exitCode = EXIT_CODES.ERROR;
155
+ }
178
156
  } else {
179
- logger.log('-'.repeat(50));
180
- logger.log(chalk.bold(`Build #${buildId} succeeded\n`));
181
- logger.log('🚀 Ready to take your project live?');
182
- logger.log(`Run \`${chalk.hex('f5c26b')('hs project deploy')}\``);
183
- logger.log('-'.repeat(50));
157
+ uiLine();
158
+ logger.log(
159
+ chalk.bold(
160
+ i18n(`${i18nKey}.logs.buildSucceeded`, {
161
+ buildId,
162
+ })
163
+ )
164
+ );
165
+ logger.log(i18n(`${i18nKey}.logs.readyToGoLive`));
166
+ logger.log(
167
+ i18n(`${i18nKey}.logs.runCommand`, {
168
+ command: chalk.hex('f5c26b')('hs project deploy'),
169
+ })
170
+ );
171
+ uiLine();
184
172
  }
185
173
 
186
174
  try {
187
175
  tempFile.removeCallback();
188
- logger.debug(`Cleaned up temporary file ${tempFile.name}`);
176
+ logger.debug(
177
+ i18n(`${i18nKey}.debug.cleanedUpTempFile`, {
178
+ path: tempFile.name,
179
+ })
180
+ );
189
181
  } catch (e) {
190
182
  logger.error(e);
191
183
  }
184
+
185
+ process.exit(exitCode);
192
186
  });
193
187
 
194
188
  archive.on('error', function(err) {
@@ -208,17 +202,19 @@ exports.handler = async options => {
208
202
 
209
203
  exports.builder = yargs => {
210
204
  yargs.positional('path', {
211
- describe: 'Path to a project folder',
205
+ describe: i18n(`${i18nKey}.positionals.path.describe`),
212
206
  type: 'string',
213
207
  });
214
208
 
215
209
  yargs.option('forceCreate', {
216
- describe: 'Automatically create project if it does not exist',
210
+ describe: i18n(`${i18nKey}.options.forceCreate.describe`),
217
211
  type: 'boolean',
218
212
  default: false,
219
213
  });
220
214
 
221
- yargs.example([['$0 project upload myProjectFolder', 'Upload a project']]);
215
+ yargs.example([
216
+ ['$0 project upload myProjectFolder', i18n(`${i18nKey}.examples.default`)],
217
+ ]);
222
218
 
223
219
  addConfigOptions(yargs, true);
224
220
  addAccountOptions(yargs, true);
@@ -0,0 +1,103 @@
1
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
2
+ const { createWatcher } = require('@hubspot/cli-lib/projectsWatch');
3
+ const { cancelStagedBuild } = require('@hubspot/cli-lib/api/dfs');
4
+ const {
5
+ logApiErrorInstance,
6
+ ApiErrorContext,
7
+ } = require('@hubspot/cli-lib/errorHandlers');
8
+ const { logger } = require('@hubspot/cli-lib/logger');
9
+ const {
10
+ addAccountOptions,
11
+ addConfigOptions,
12
+ getAccountId,
13
+ addUseEnvironmentOptions,
14
+ } = require('../../lib/commonOpts');
15
+ const { trackCommandUsage } = require('../../lib/usageTracking');
16
+ const {
17
+ getProjectConfig,
18
+ validateProjectConfig,
19
+ pollBuildStatus,
20
+ pollDeployStatus,
21
+ } = require('../../lib/projects');
22
+ const { loadAndValidateOptions } = require('../../lib/validation');
23
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
24
+
25
+ const i18nKey = 'cli.commands.project.subcommands.watch';
26
+
27
+ exports.command = 'watch [path]';
28
+ exports.describe = i18n(`${i18nKey}.describe`);
29
+
30
+ const handleBuildStatus = async (accountId, projectName, buildId) => {
31
+ const {
32
+ isAutoDeployEnabled,
33
+ deployStatusTaskLocator,
34
+ } = await pollBuildStatus(accountId, projectName, buildId);
35
+
36
+ if (isAutoDeployEnabled && deployStatusTaskLocator) {
37
+ await pollDeployStatus(
38
+ accountId,
39
+ projectName,
40
+ deployStatusTaskLocator.id,
41
+ buildId
42
+ );
43
+ }
44
+ };
45
+
46
+ const handleSigInt = (accountId, projectName, currentBuildId) => {
47
+ process.removeAllListeners('SIGINT');
48
+ process.on('SIGINT', async () => {
49
+ if (currentBuildId) {
50
+ try {
51
+ await cancelStagedBuild(accountId, projectName);
52
+ logger.debug(i18n(`${i18nKey}.debug.buildCancelled`));
53
+ process.exit(EXIT_CODES.SUCCESS);
54
+ } catch (err) {
55
+ logApiErrorInstance(
56
+ err,
57
+ new ApiErrorContext({ accountId, projectName: projectName })
58
+ );
59
+ process.exit(EXIT_CODES.ERROR);
60
+ }
61
+ } else {
62
+ process.exit(EXIT_CODES.SUCCESS);
63
+ }
64
+ });
65
+ };
66
+
67
+ exports.handler = async options => {
68
+ await loadAndValidateOptions(options);
69
+
70
+ const { path: projectPath } = options;
71
+ const accountId = getAccountId(options);
72
+
73
+ trackCommandUsage('project-watch', { projectPath }, accountId);
74
+
75
+ const { projectConfig, projectDir } = await getProjectConfig(projectPath);
76
+
77
+ validateProjectConfig(projectConfig, projectDir);
78
+
79
+ await createWatcher(
80
+ accountId,
81
+ projectConfig,
82
+ projectDir,
83
+ handleBuildStatus,
84
+ handleSigInt
85
+ );
86
+ };
87
+
88
+ exports.builder = yargs => {
89
+ yargs.positional('path', {
90
+ describe: i18n(`${i18nKey}.describe`),
91
+ type: 'string',
92
+ });
93
+
94
+ yargs.example([
95
+ ['$0 project watch myProjectFolder', i18n(`${i18nKey}.examples.default`)],
96
+ ]);
97
+
98
+ addConfigOptions(yargs, true);
99
+ addAccountOptions(yargs, true);
100
+ addUseEnvironmentOptions(yargs, true);
101
+
102
+ return yargs;
103
+ };
@@ -1,18 +1,15 @@
1
- const {
2
- addConfigOptions,
3
- addAccountOptions,
4
- addOverwriteOptions,
5
- } = require('../lib/commonOpts');
1
+ const { addConfigOptions, addAccountOptions } = require('../lib/commonOpts');
6
2
  const deploy = require('./project/deploy');
7
3
  const create = require('./project/create');
8
4
  const upload = require('./project/upload');
5
+ const listBuilds = require('./project/listBuilds');
9
6
  const logs = require('./project/logs');
7
+ const watch = require('./project/watch');
10
8
 
11
9
  exports.command = 'project';
12
10
  exports.describe = false; //'Commands for working with projects';
13
11
 
14
12
  exports.builder = yargs => {
15
- addOverwriteOptions(yargs, true);
16
13
  addConfigOptions(yargs, true);
17
14
  addAccountOptions(yargs, true);
18
15
 
@@ -20,6 +17,8 @@ exports.builder = yargs => {
20
17
  yargs.command(deploy).demandCommand(1, '');
21
18
  yargs.command(create).demandCommand(0, '');
22
19
  yargs.command(upload).demandCommand(0, '');
20
+ yargs.command(watch).demandCommand(0, '');
21
+ yargs.command(listBuilds).demandCommand(0, '');
23
22
  yargs.command(logs).demandCommand(1, '');
24
23
 
25
24
  return yargs;