@hubspot/cli 4.1.8-beta.0 → 4.1.8-beta.2

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/README.md CHANGED
@@ -4,7 +4,7 @@ Provides an `hs` command for interacting with HubSpot. [Learn more about buildin
4
4
 
5
5
  ## Getting started
6
6
 
7
- For more information on using these tools, see [Local Development Tooling: Getting Started](https://designers.hubspot.com/tutorials/getting-started-with-local-development)
7
+ For more information on using these tools, see [Local Development Tooling: Getting Started](https://developers.hubspot.com/docs/cms/guides/getting-started-with-local-development)
8
8
 
9
9
  ### Installation
10
10
 
@@ -33,7 +33,7 @@ and copying the output to either your `.bashrc` or `.zshrc`, and then sourcing t
33
33
 
34
34
  ## Commands
35
35
 
36
- A full breakdown of the commands can be found on the [local development tools reference page](https://designers.hubspot.com/docs/developer-reference/local-development-cli).
36
+ A full breakdown of the commands can be found on the [local development tools reference page](https://developers.hubspot.com/docs/cms/developer-reference/local-development-cli).
37
37
 
38
38
  **Note:** When `@hubspot/cli` is installed local to a project, the commands need to be prefixed with either `yarn` if using `yarn` or `npx` if using `npm`.
39
39
 
@@ -12,7 +12,7 @@ const {
12
12
  } = require('../../lib/commonOpts');
13
13
  const { trackCommandUsage } = require('../../lib/usageTracking');
14
14
  const { loadAndValidateOptions } = require('../../lib/validation');
15
- const { getSandboxType } = require('../../lib/sandboxes');
15
+ const { getSandboxTypeAsString } = require('../../lib/sandboxes');
16
16
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
17
17
 
18
18
  const i18nKey = 'cli.commands.accounts.subcommands.list';
@@ -58,7 +58,7 @@ const getPortalData = mappedPortalData => {
58
58
  portalData.push([portal.name, portal.portalId, portal.authType]);
59
59
  } else {
60
60
  portalData.push([
61
- `↳ ${portal.name} [${getSandboxType(
61
+ `↳ ${portal.name} [${getSandboxTypeAsString(
62
62
  portal.sandboxAccountType
63
63
  )} sandbox]`,
64
64
  portal.portalId,
@@ -39,7 +39,7 @@ module.exports = {
39
39
  const downloadSpinner = ora(i18n(`${i18nKey}.loading.apiSamples`));
40
40
  downloadSpinner.start();
41
41
  const samplesConfig = await fetchJsonFromRepository(
42
- 'sample-apps-list',
42
+ 'HubSpot/sample-apps-list',
43
43
  'main/samples.json'
44
44
  );
45
45
  downloadSpinner.stop();
@@ -0,0 +1,56 @@
1
+ const { logger } = require('@hubspot/cli-lib/logger');
2
+ const { getAccountId } = require('@hubspot/cli-lib/lib/config');
3
+ const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
4
+
5
+ const { trackCommandUsage } = require('../../lib/usageTracking');
6
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
7
+ const { projectAddPrompt } = require('../../lib/prompts/projectAddPrompt');
8
+ const { createProjectComponent } = require('../../lib/projects');
9
+ const { loadAndValidateOptions } = require('../../lib/validation');
10
+
11
+ const i18nKey = 'cli.commands.project.subcommands.add';
12
+
13
+ exports.command = 'add';
14
+ exports.describe = null; //i18n(`${i18nKey}.describe`);
15
+
16
+ exports.handler = async options => {
17
+ await loadAndValidateOptions(options);
18
+
19
+ const accountId = getAccountId(options);
20
+
21
+ logger.log('');
22
+ logger.log(i18n(`${i18nKey}.creatingComponent.message`));
23
+ logger.log('');
24
+ const { type, name } = await projectAddPrompt(options);
25
+
26
+ trackCommandUsage('project-add', null, accountId);
27
+
28
+ try {
29
+ await createProjectComponent(options.type || type, options.name || name);
30
+ logger.log('');
31
+ logger.log(
32
+ i18n(`${i18nKey}.success.message`, {
33
+ componentName: options.name || name,
34
+ })
35
+ );
36
+ } catch (error) {
37
+ logErrorInstance(error);
38
+ }
39
+ };
40
+
41
+ exports.builder = yargs => {
42
+ yargs.options({
43
+ type: {
44
+ describe: i18n(`${i18nKey}.options.type.describe`),
45
+ type: 'string',
46
+ },
47
+ name: {
48
+ describe: i18n(`${i18nKey}.options.name.describe`),
49
+ type: 'string',
50
+ },
51
+ });
52
+
53
+ yargs.example([['$0 project add', i18n(`${i18nKey}.examples.default`)]]);
54
+
55
+ return yargs;
56
+ };
@@ -14,7 +14,6 @@ const {
14
14
  } = require('../../lib/prompts/createProjectPrompt');
15
15
  const { createProjectConfig } = require('../../lib/projects');
16
16
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
17
- const { PROJECT_TEMPLATES } = require('@hubspot/cli-lib/lib/constants');
18
17
  const { uiFeatureHighlight } = require('../../lib/ui');
19
18
  const { logger } = require('@hubspot/cli-lib/logger');
20
19
 
@@ -35,7 +34,8 @@ exports.handler = async options => {
35
34
  await createProjectConfig(
36
35
  path.resolve(getCwd(), options.location || location),
37
36
  options.name || name,
38
- options.template || template
37
+ options.template || template,
38
+ options.repoPath
39
39
  );
40
40
 
41
41
  logger.log('');
@@ -61,7 +61,10 @@ exports.builder = yargs => {
61
61
  template: {
62
62
  describe: i18n(`${i18nKey}.options.template.describe`),
63
63
  type: 'string',
64
- choices: PROJECT_TEMPLATES.map(template => template.name),
64
+ },
65
+ repoPath: {
66
+ describe: i18n(`${i18nKey}.options.repoPath.describe`),
67
+ type: 'string',
65
68
  },
66
69
  });
67
70
 
@@ -14,6 +14,7 @@ const { deployProject, fetchProject } = require('@hubspot/cli-lib/api/dfs');
14
14
  const { loadAndValidateOptions } = require('../../lib/validation');
15
15
  const { getProjectConfig, pollDeployStatus } = require('../../lib/projects');
16
16
  const { projectNamePrompt } = require('../../lib/prompts/projectNamePrompt');
17
+ const { buildIdPrompt } = require('../../lib/prompts/buildIdPrompt');
17
18
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
18
19
  const { getAccountConfig } = require('@hubspot/cli-lib');
19
20
 
@@ -28,46 +29,59 @@ exports.handler = async options => {
28
29
 
29
30
  const accountId = getAccountId(options);
30
31
  const accountConfig = getAccountConfig(accountId);
31
- const { project, buildId } = options;
32
+ const { project: projectOption, buildId: buildIdOption } = options;
32
33
  const sandboxType = accountConfig && accountConfig.sandboxAccountType;
33
34
 
34
35
  trackCommandUsage('project-deploy', { type: sandboxType }, accountId);
35
36
 
36
37
  const { projectConfig } = await getProjectConfig();
37
38
 
38
- let projectName = project;
39
+ let projectName = projectOption;
39
40
 
40
- if (!projectName && projectConfig) {
41
+ if (!projectOption && projectConfig) {
41
42
  projectName = projectConfig.name;
42
43
  }
43
44
 
44
- const namePrompt = await projectNamePrompt(accountId, {
45
+ const namePromptResponse = await projectNamePrompt(accountId, {
45
46
  project: projectName,
46
47
  });
47
48
 
48
- if (!projectName && namePrompt.projectName) {
49
- projectName = namePrompt.projectName;
49
+ if (!projectName && namePromptResponse.projectName) {
50
+ projectName = namePromptResponse.projectName;
50
51
  }
51
52
 
52
- let exitCode = EXIT_CODES.SUCCESS;
53
+ let buildIdToDeploy = buildIdOption;
53
54
 
54
- const getBuildId = async () => {
55
- const { latestBuild } = await fetchProject(accountId, projectName);
56
- if (latestBuild && latestBuild.buildId) {
57
- return latestBuild.buildId;
55
+ if (!buildIdOption) {
56
+ const { latestBuild, deployedBuildId } = await fetchProject(
57
+ accountId,
58
+ projectName
59
+ );
60
+ if (!latestBuild || !latestBuild.buildId) {
61
+ logger.error(i18n(`${i18nKey}.errors.noBuilds`));
62
+ process.exit(EXIT_CODES.ERROR);
58
63
  }
64
+ const buildIdPromptResponse = await buildIdPrompt(
65
+ latestBuild.buildId,
66
+ deployedBuildId,
67
+ projectName
68
+ );
69
+
70
+ buildIdToDeploy = buildIdPromptResponse.buildId;
71
+ }
72
+
73
+ if (!buildIdToDeploy) {
59
74
  logger.error(i18n(`${i18nKey}.errors.noBuildId`));
60
- exitCode = EXIT_CODES.ERROR;
61
- return;
62
- };
75
+ process.exit(EXIT_CODES.ERROR);
76
+ }
63
77
 
64
- try {
65
- const deployedBuildId = buildId || (await getBuildId());
78
+ let exitCode = EXIT_CODES.SUCCESS;
66
79
 
80
+ try {
67
81
  const deployResp = await deployProject(
68
82
  accountId,
69
83
  projectName,
70
- deployedBuildId
84
+ buildIdToDeploy
71
85
  );
72
86
 
73
87
  if (deployResp.error) {
@@ -84,7 +98,7 @@ exports.handler = async options => {
84
98
  accountId,
85
99
  projectName,
86
100
  deployResp.id,
87
- deployedBuildId
101
+ buildIdToDeploy
88
102
  );
89
103
  } catch (e) {
90
104
  if (e.statusCode === 400) {
@@ -0,0 +1,278 @@
1
+ const {
2
+ addAccountOptions,
3
+ addConfigOptions,
4
+ getAccountId,
5
+ addUseEnvironmentOptions,
6
+ addTestingOptions,
7
+ } = require('../../lib/commonOpts');
8
+ const { trackCommandUsage } = require('../../lib/usageTracking');
9
+ const { loadAndValidateOptions } = require('../../lib/validation');
10
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
11
+ const { logger } = require('@hubspot/cli-lib/logger');
12
+ const { getConfigAccounts } = require('@hubspot/cli-lib/lib/config');
13
+ const { createProject, fetchProject } = require('@hubspot/cli-lib/api/dfs');
14
+ const { handleExit } = require('@hubspot/cli-lib/lib/process');
15
+ const {
16
+ getProjectConfig,
17
+ ensureProjectExists,
18
+ handleProjectUpload,
19
+ pollProjectBuildAndDeploy,
20
+ } = require('../../lib/projects');
21
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
22
+ const { uiAccountDescription, uiLine } = require('../../lib/ui');
23
+ const { confirmPrompt } = require('../../lib/prompts/promptUtils');
24
+ const {
25
+ selectTargetAccountPrompt,
26
+ } = require('../../lib/prompts/projectDevTargetAccountPrompt');
27
+ const SpinniesManager = require('../../lib/SpinniesManager');
28
+ const {
29
+ LocalDevManager,
30
+ UPLOAD_PERMISSIONS,
31
+ } = require('../../lib/LocalDevManager');
32
+ const { getAccountConfig, getEnv } = require('@hubspot/cli-lib');
33
+ const { sandboxNamePrompt } = require('../../lib/prompts/sandboxesPrompt');
34
+ const {
35
+ validateSandboxUsageLimits,
36
+ DEVELOPER_SANDBOX,
37
+ getAvailableSyncTypes,
38
+ } = require('../../lib/sandboxes');
39
+ const { getValidEnv } = require('@hubspot/cli-lib/lib/environment');
40
+ const { logErrorInstance } = require('@hubspot/cli-lib/errorHandlers');
41
+ const { buildSandbox } = require('../../lib/sandbox-create');
42
+ const { syncSandbox } = require('../../lib/sandbox-sync');
43
+
44
+ const i18nKey = 'cli.commands.project.subcommands.dev';
45
+
46
+ exports.command = 'dev [--account] [--mockServers]';
47
+ exports.describe = null; //i18n(`${i18nKey}.describe`);
48
+
49
+ exports.handler = async options => {
50
+ await loadAndValidateOptions(options);
51
+ const accountId = getAccountId(options);
52
+ const accountConfig = getAccountConfig(accountId);
53
+ const env = getValidEnv(getEnv(accountId));
54
+
55
+ trackCommandUsage('project-dev', null, accountId);
56
+
57
+ const { projectConfig, projectDir } = await getProjectConfig();
58
+
59
+ logger.log(i18n(`${i18nKey}.logs.introHeader`));
60
+ uiLine();
61
+ logger.log(i18n(`${i18nKey}.logs.introBody1`));
62
+ logger.log();
63
+ logger.log(i18n(`${i18nKey}.logs.introBody2`));
64
+ uiLine();
65
+ logger.log();
66
+
67
+ if (!projectConfig) {
68
+ logger.error(i18n(`${i18nKey}.errors.noProjectConfig`));
69
+ process.exit(EXIT_CODES.ERROR);
70
+ }
71
+
72
+ const accounts = getConfigAccounts();
73
+ let targetAccountId = options.accountId;
74
+ let createNewSandbox = false;
75
+ let chooseNonSandbox = false;
76
+
77
+ if (!targetAccountId) {
78
+ const {
79
+ targetAccountId: promptTargetAccountId,
80
+ chooseNonSandbox: promptChooseNonSandbox,
81
+ createNewSandbox: promptCreateNewSandbox,
82
+ } = await selectTargetAccountPrompt(accounts, accountConfig, false);
83
+
84
+ targetAccountId = promptTargetAccountId;
85
+ chooseNonSandbox = promptChooseNonSandbox;
86
+ createNewSandbox = promptCreateNewSandbox;
87
+ }
88
+
89
+ logger.log();
90
+
91
+ // Show a warning if the user chooses a non-sandbox account (false)
92
+ let shouldTargetNonSandboxAccount;
93
+ if (chooseNonSandbox) {
94
+ uiLine();
95
+ logger.warn(i18n(`${i18nKey}.logs.prodAccountWarning`));
96
+ uiLine();
97
+ logger.log();
98
+
99
+ shouldTargetNonSandboxAccount = await confirmPrompt(
100
+ i18n(`${i18nKey}.prompt.targetNonSandbox`)
101
+ );
102
+
103
+ if (shouldTargetNonSandboxAccount) {
104
+ const {
105
+ targetAccountId: promptNonSandboxTargetAccountId,
106
+ } = await selectTargetAccountPrompt(accounts, accountConfig, true);
107
+
108
+ targetAccountId = promptNonSandboxTargetAccountId;
109
+ logger.log();
110
+ } else {
111
+ process.exit(EXIT_CODES.SUCCESS);
112
+ }
113
+ } else if (createNewSandbox) {
114
+ try {
115
+ await validateSandboxUsageLimits(accountConfig, DEVELOPER_SANDBOX, env);
116
+ } catch (err) {
117
+ logErrorInstance(err);
118
+ process.exit(EXIT_CODES.ERROR);
119
+ }
120
+ try {
121
+ const { name } = await sandboxNamePrompt(DEVELOPER_SANDBOX);
122
+ const { result } = await buildSandbox({
123
+ name,
124
+ type: DEVELOPER_SANDBOX,
125
+ accountConfig,
126
+ env,
127
+ });
128
+
129
+ targetAccountId = result.sandbox.sandboxHubId;
130
+
131
+ const sandboxAccountConfig = getAccountConfig(
132
+ result.sandbox.sandboxHubId
133
+ );
134
+ const syncTasks = await getAvailableSyncTypes(
135
+ accountConfig,
136
+ sandboxAccountConfig
137
+ );
138
+ await syncSandbox({
139
+ accountConfig: sandboxAccountConfig,
140
+ parentAccountConfig: accountConfig,
141
+ env,
142
+ syncTasks,
143
+ allowEarlyTermination: false, // Don't let user terminate early in this flow
144
+ skipPolling: true, // Skip polling, sync will run and complete in the background
145
+ });
146
+ } catch (err) {
147
+ logErrorInstance(err);
148
+ process.exit(EXIT_CODES.ERROR);
149
+ }
150
+ }
151
+
152
+ const projectExists = await ensureProjectExists(
153
+ targetAccountId,
154
+ projectConfig.name,
155
+ {
156
+ allowCreate: false,
157
+ noLogs: true,
158
+ withPolling: true,
159
+ }
160
+ );
161
+
162
+ const isNonSandboxAccount = shouldTargetNonSandboxAccount;
163
+
164
+ let uploadPermission = isNonSandboxAccount
165
+ ? UPLOAD_PERMISSIONS.manual
166
+ : UPLOAD_PERMISSIONS.always;
167
+
168
+ if (projectExists) {
169
+ const { sourceIntegration } = await fetchProject(
170
+ targetAccountId,
171
+ projectConfig.name
172
+ );
173
+ if (sourceIntegration) {
174
+ uploadPermission = UPLOAD_PERMISSIONS.never;
175
+ }
176
+ }
177
+
178
+ const spinnies = SpinniesManager.init();
179
+
180
+ if (!projectExists) {
181
+ uiLine();
182
+ logger.warn(
183
+ i18n(`${i18nKey}.logs.projectMustExistExplanation`, {
184
+ accountIdentifier: uiAccountDescription(targetAccountId),
185
+ projectName: projectConfig.name,
186
+ })
187
+ );
188
+ uiLine();
189
+
190
+ const shouldCreateProject = await confirmPrompt(
191
+ i18n(`${i18nKey}.prompt.createProject`, {
192
+ accountIdentifier: uiAccountDescription(targetAccountId),
193
+ projectName: projectConfig.name,
194
+ })
195
+ );
196
+
197
+ if (shouldCreateProject) {
198
+ try {
199
+ spinnies.add('createProject', {
200
+ text: i18n(`${i18nKey}.status.creatingProject`, {
201
+ accountIdentifier: uiAccountDescription(targetAccountId),
202
+ projectName: projectConfig.name,
203
+ }),
204
+ });
205
+ await createProject(targetAccountId, projectConfig.name);
206
+ spinnies.succeed('createProject', {
207
+ text: i18n(`${i18nKey}.status.createdProject`, {
208
+ accountIdentifier: uiAccountDescription(targetAccountId),
209
+ projectName: projectConfig.name,
210
+ }),
211
+ });
212
+ } catch (err) {
213
+ logger.log(i18n(`${i18nKey}.logs.failedToCreateProject`));
214
+ process.exit(EXIT_CODES.ERROR);
215
+ }
216
+ } else {
217
+ // We cannot continue if the project does not exist in the target account
218
+ logger.log(i18n(`${i18nKey}.logs.choseNotToCreateProject`));
219
+ process.exit(EXIT_CODES.SUCCESS);
220
+ }
221
+ }
222
+
223
+ spinnies.add('devModeSetup', {
224
+ text: i18n(`${i18nKey}.status.startupMessage`, {
225
+ projectName: projectConfig.name,
226
+ }),
227
+ isParent: true,
228
+ });
229
+
230
+ let result;
231
+ if (uploadPermission === UPLOAD_PERMISSIONS.always) {
232
+ result = await handleProjectUpload(
233
+ targetAccountId,
234
+ projectConfig,
235
+ projectDir,
236
+ (...args) => pollProjectBuildAndDeploy(...args, true),
237
+ i18n(`${i18nKey}.logs.initialUploadMessage`)
238
+ );
239
+ }
240
+
241
+ if (result && !result.succeeded) {
242
+ spinnies.fail('devModeSetup', {
243
+ text: i18n(`${i18nKey}.status.startupFailed`),
244
+ });
245
+ process.exit(EXIT_CODES.ERROR);
246
+ } else {
247
+ spinnies.remove('devModeSetup');
248
+ }
249
+
250
+ const LocalDev = new LocalDevManager({
251
+ debug: options.debug,
252
+ mockServers: options.mockServers,
253
+ projectConfig,
254
+ projectDir,
255
+ targetAccountId,
256
+ uploadPermission,
257
+ });
258
+
259
+ await LocalDev.start();
260
+
261
+ handleExit(LocalDev.stop);
262
+ };
263
+
264
+ exports.builder = yargs => {
265
+ addConfigOptions(yargs, true);
266
+ addAccountOptions(yargs, true);
267
+ addUseEnvironmentOptions(yargs, true);
268
+ addTestingOptions(yargs, true);
269
+
270
+ yargs.option('mockServers', {
271
+ describe: 'mock servers',
272
+ type: 'boolean',
273
+ default: false,
274
+ });
275
+ yargs.example([['$0 project dev', i18n(`${i18nKey}.examples.default`)]]);
276
+
277
+ return yargs;
278
+ };
@@ -22,18 +22,24 @@ const {
22
22
  ensureProjectExists,
23
23
  } = require('../../lib/projects');
24
24
  const { loadAndValidateOptions } = require('../../lib/validation');
25
+ const {
26
+ downloadProjectPrompt,
27
+ } = require('../../lib/prompts/downloadProjectPrompt');
25
28
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
26
29
 
27
30
  const i18nKey = 'cli.commands.project.subcommands.download';
28
31
  const { EXIT_CODES } = require('../../lib/enums/exitCodes');
29
32
 
30
- exports.command = 'download <name> [dest]';
33
+ exports.command = 'download [--project]';
31
34
  exports.describe = i18n(`${i18nKey}.describe`);
32
35
 
33
36
  exports.handler = async options => {
34
37
  await loadAndValidateOptions(options);
35
38
 
36
- const { name: projectName, dest, buildNumber } = options;
39
+ const { project, dest, buildNumber } = options;
40
+ let { project: promptedProjectName } = await downloadProjectPrompt(options);
41
+ let projectName = promptedProjectName || project;
42
+
37
43
  const accountId = getAccountId(options);
38
44
 
39
45
  trackCommandUsage('project-download', null, accountId);
@@ -50,7 +56,8 @@ exports.handler = async options => {
50
56
  accountId: chalk.bold(accountId),
51
57
  })
52
58
  );
53
- process.exit(EXIT_CODES.ERROR);
59
+ let { name: promptedProjectName } = await downloadProjectPrompt(options);
60
+ let projectName = promptedProjectName || project;
54
61
  }
55
62
 
56
63
  const absoluteDestPath = dest ? path.resolve(getCwd(), dest) : getCwd();
@@ -58,7 +65,7 @@ exports.handler = async options => {
58
65
  const projectConfigCreated = await createProjectConfig(
59
66
  absoluteDestPath,
60
67
  projectName,
61
- 'no-template'
68
+ { name: 'no-template' }
62
69
  );
63
70
 
64
71
  if (!projectConfigCreated) {
@@ -119,23 +126,27 @@ exports.handler = async options => {
119
126
  exports.builder = yargs => {
120
127
  addUseEnvironmentOptions(yargs, true);
121
128
 
122
- yargs.positional('name', {
123
- describe: i18n(`${i18nKey}.positionals.name.describe`),
124
- type: 'string',
125
- });
126
- yargs.positional('dest', {
127
- describe: i18n(`${i18nKey}.positionals.dest.describe`),
128
- type: 'string',
129
- });
130
- yargs.option('buildNumber', {
131
- describe: i18n(`${i18nKey}.options.buildNumber.describe`),
132
- type: 'number',
129
+ yargs.options({
130
+ project: {
131
+ describe: i18n(`${i18nKey}.options.project.describe`),
132
+ type: 'string',
133
+ },
134
+ dest: {
135
+ describe: i18n(`${i18nKey}.options.dest.describe`),
136
+ type: 'string',
137
+ },
138
+ buildNumber: {
139
+ describe: i18n(`${i18nKey}.options.buildNumber.describe`),
140
+ type: 'number',
141
+ },
133
142
  });
143
+
134
144
  yargs.example([
135
145
  [
136
- '$0 project download myProject myProjectFolder',
146
+ '$0 project download --project=myProject --dest=myProjectFolder',
137
147
  i18n(`${i18nKey}.examples.default`),
138
148
  ],
139
149
  ]);
150
+
140
151
  return yargs;
141
152
  };