@hubspot/cli 3.0.12-beta.0 → 3.0.12-beta.1

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.
@@ -10,7 +10,7 @@ const fs = require('fs-extra');
10
10
  const ora = require('ora');
11
11
  const { fetchJsonFromRepository } = require('@hubspot/cli-lib/github');
12
12
  const { GITHUB_RELEASE_TYPES } = require('@hubspot/cli-lib/lib/constants');
13
- const { createProject } = require('@hubspot/cli-lib/projects');
13
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
14
14
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
15
15
 
16
16
  const i18nKey = 'cli.commands.create.subcommands.apiSample';
@@ -60,7 +60,7 @@ module.exports = {
60
60
  sampleLanguage,
61
61
  })
62
62
  );
63
- const created = await createProject(
63
+ const created = await cloneGitHubRepo(
64
64
  filePath,
65
65
  assetType,
66
66
  sampleType,
@@ -1,8 +1,8 @@
1
- const { createProject } = require('@hubspot/cli-lib/projects');
1
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
2
2
 
3
3
  module.exports = {
4
4
  hidden: true,
5
5
  dest: ({ name, assetType }) => name || assetType,
6
6
  execute: async ({ options, dest, assetType }) =>
7
- createProject(dest, assetType, 'crm-card-weather-app', '', options),
7
+ cloneGitHubRepo(dest, assetType, 'crm-card-weather-app', '', options),
8
8
  };
@@ -1,7 +1,7 @@
1
- const { createProject } = require('@hubspot/cli-lib/projects');
1
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
2
2
 
3
3
  module.exports = {
4
4
  dest: ({ name, assetType }) => name || assetType,
5
5
  execute: async ({ options, dest, assetType }) =>
6
- createProject(dest, assetType, 'cms-react-boilerplate', '', options),
6
+ cloneGitHubRepo(dest, assetType, 'cms-react-boilerplate', '', options),
7
7
  };
@@ -1,7 +1,7 @@
1
- const { createProject } = require('@hubspot/cli-lib/projects');
1
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
2
2
 
3
3
  module.exports = {
4
4
  dest: ({ name, assetType }) => name || assetType,
5
5
  execute: async ({ options, dest, assetType }) =>
6
- createProject(dest, assetType, 'cms-vue-boilerplate', '', options),
6
+ cloneGitHubRepo(dest, assetType, 'cms-vue-boilerplate', '', options),
7
7
  };
@@ -1,9 +1,9 @@
1
- const { createProject } = require('@hubspot/cli-lib/projects');
1
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
2
2
 
3
3
  module.exports = {
4
4
  dest: ({ name, assetType }) => name || assetType,
5
5
  execute: async ({ options, dest, assetType }) =>
6
- createProject(
6
+ cloneGitHubRepo(
7
7
  dest,
8
8
  assetType,
9
9
  'cms-webpack-serverless-boilerplate',
@@ -1,4 +1,4 @@
1
- const { createProject } = require('@hubspot/cli-lib/projects');
1
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
2
2
  const { GITHUB_RELEASE_TYPES } = require('@hubspot/cli-lib/lib/constants');
3
3
  const { getIsInProject } = require('../../lib/projects');
4
4
 
@@ -14,6 +14,6 @@ module.exports = {
14
14
  // releaseType has to be 'REPOSITORY' to download a specific branch
15
15
  options.releaseType = GITHUB_RELEASE_TYPES.REPOSITORY;
16
16
  }
17
- createProject(dest, assetType, 'cms-theme-boilerplate', 'src', options);
17
+ cloneGitHubRepo(dest, assetType, 'cms-theme-boilerplate', 'src', options);
18
18
  },
19
19
  };
@@ -11,7 +11,10 @@ const path = require('path');
11
11
  const {
12
12
  createProjectPrompt,
13
13
  } = require('../../lib/prompts/createProjectPrompt');
14
- const { createProjectConfig } = require('../../lib/projects');
14
+ const {
15
+ createProjectConfig,
16
+ showProjectWelcomeMessage,
17
+ } = require('../../lib/projects');
15
18
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
16
19
 
17
20
  const i18nKey = 'cli.commands.project.subcommands.create';
@@ -33,6 +36,8 @@ exports.handler = async options => {
33
36
  options.name || name,
34
37
  options.template || template
35
38
  );
39
+
40
+ showProjectWelcomeMessage();
36
41
  };
37
42
 
38
43
  exports.builder = yargs => {
@@ -0,0 +1,127 @@
1
+ const path = require('path');
2
+
3
+ const {
4
+ getAccountId,
5
+ addUseEnvironmentOptions,
6
+ } = require('../../lib/commonOpts');
7
+ const { trackCommandUsage } = require('../../lib/usageTracking');
8
+ const { getCwd } = require('@hubspot/cli-lib/path');
9
+ const {
10
+ logApiErrorInstance,
11
+ ApiErrorContext,
12
+ } = require('@hubspot/cli-lib/errorHandlers');
13
+ const { logger } = require('@hubspot/cli-lib/logger');
14
+ const { extractZipArchive } = require('@hubspot/cli-lib/archive');
15
+ const {
16
+ downloadProject,
17
+ fetchProjectBuilds,
18
+ } = require('@hubspot/cli-lib/api/dfs');
19
+ const {
20
+ createProjectConfig,
21
+ ensureProjectExists,
22
+ } = require('../../lib/projects');
23
+ const { loadAndValidateOptions } = require('../../lib/validation');
24
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
25
+
26
+ const i18nKey = 'cli.commands.project.subcommands.download';
27
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
28
+
29
+ exports.command = 'download <name> [dest]';
30
+ exports.describe = i18n(`${i18nKey}.describe`);
31
+
32
+ exports.handler = async options => {
33
+ await loadAndValidateOptions(options);
34
+
35
+ const { name: projectName, dest, buildNumber } = options;
36
+ const accountId = getAccountId(options);
37
+
38
+ trackCommandUsage('project-download', { projectName }, accountId);
39
+
40
+ await ensureProjectExists(accountId, projectName, { allowCreate: false });
41
+
42
+ const absoluteDestPath = dest ? path.resolve(getCwd(), dest) : getCwd();
43
+
44
+ const projectConfigCreated = await createProjectConfig(
45
+ absoluteDestPath,
46
+ projectName,
47
+ 'none'
48
+ );
49
+
50
+ if (!projectConfigCreated) {
51
+ logger.log(i18n(`${i18nKey}.logs.downloadCancelled`));
52
+ process.exit(EXIT_CODES.SUCCESS);
53
+ }
54
+
55
+ let success = false;
56
+ let buildNumberToDownload = buildNumber;
57
+
58
+ if (!buildNumberToDownload) {
59
+ let projectBuildsResult;
60
+
61
+ try {
62
+ projectBuildsResult = await fetchProjectBuilds(accountId, projectName);
63
+ } catch (e) {
64
+ logApiErrorInstance(e, new ApiErrorContext({ accountId }));
65
+ process.exit(EXIT_CODES.ERROR);
66
+ }
67
+
68
+ const { results: projectBuilds } = projectBuildsResult;
69
+
70
+ if (projectBuilds && projectBuilds.length) {
71
+ const latestBuild = projectBuilds[0];
72
+ buildNumberToDownload = latestBuild.buildId;
73
+ }
74
+ }
75
+
76
+ const zippedProject = await downloadProject(
77
+ accountId,
78
+ projectName,
79
+ buildNumberToDownload
80
+ );
81
+
82
+ success = await extractZipArchive(
83
+ zippedProject,
84
+ projectName,
85
+ path.resolve(absoluteDestPath, 'src'),
86
+ {
87
+ includesRootDir: false,
88
+ }
89
+ );
90
+
91
+ if (!success) {
92
+ logger.log(i18n(`${i18nKey}.errors.downloadFailed`));
93
+ process.exit(EXIT_CODES.ERROR);
94
+ }
95
+
96
+ logger.log(
97
+ i18n(`${i18nKey}.logs.downloadSucceeded`, {
98
+ buildId: buildNumberToDownload,
99
+ projectName,
100
+ })
101
+ );
102
+ process.exit(EXIT_CODES.SUCCESS);
103
+ };
104
+
105
+ exports.builder = yargs => {
106
+ addUseEnvironmentOptions(yargs, true);
107
+
108
+ yargs.positional('name', {
109
+ describe: i18n(`${i18nKey}.positionals.name.describe`),
110
+ type: 'string',
111
+ });
112
+ yargs.positional('dest', {
113
+ describe: i18n(`${i18nKey}.positionals.dest.describe`),
114
+ type: 'string',
115
+ });
116
+ yargs.option('buildNumber', {
117
+ describe: i18n(`${i18nKey}.options.buildNumber.describe`),
118
+ type: 'number',
119
+ });
120
+ yargs.example([
121
+ [
122
+ '$0 project download myProject myProjectFolder',
123
+ i18n(`${i18nKey}.examples.default`),
124
+ ],
125
+ ]);
126
+ return yargs;
127
+ };
@@ -37,7 +37,7 @@ exports.handler = async options => {
37
37
 
38
38
  validateProjectConfig(projectConfig, projectDir);
39
39
 
40
- await ensureProjectExists(accountId, projectConfig.name, forceCreate);
40
+ await ensureProjectExists(accountId, projectConfig.name, { forceCreate });
41
41
 
42
42
  const startPolling = async (tempFile, buildId) => {
43
43
  let exitCode = EXIT_CODES.SUCCESS;
@@ -5,6 +5,7 @@ const upload = require('./project/upload');
5
5
  const listBuilds = require('./project/listBuilds');
6
6
  const logs = require('./project/logs');
7
7
  const watch = require('./project/watch');
8
+ const download = require('./project/download');
8
9
 
9
10
  exports.command = 'project';
10
11
  exports.describe = false; //'Commands for working with projects';
@@ -20,6 +21,7 @@ exports.builder = yargs => {
20
21
  yargs.command(watch).demandCommand(0, '');
21
22
  yargs.command(listBuilds).demandCommand(0, '');
22
23
  yargs.command(logs).demandCommand(1, '');
24
+ yargs.command(download).demandCommand(0, '');
23
25
 
24
26
  return yargs;
25
27
  };
package/lib/projects.js CHANGED
@@ -7,15 +7,14 @@ const findup = require('findup-sync');
7
7
  const Spinnies = require('spinnies');
8
8
  const { logger } = require('@hubspot/cli-lib/logger');
9
9
  const { getEnv } = require('@hubspot/cli-lib/lib/config');
10
- const {
11
- createProject: createProjectTemplate,
12
- } = require('@hubspot/cli-lib/projects');
10
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
13
11
  const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
14
12
  const {
15
13
  ENVIRONMENTS,
16
14
  POLLING_DELAY,
17
15
  PROJECT_TEMPLATES,
18
- PROJECT_TEXT,
16
+ PROJECT_BUILD_TEXT,
17
+ PROJECT_DEPLOY_TEXT,
19
18
  PROJECT_CONFIG_FILE,
20
19
  } = require('@hubspot/cli-lib/lib/constants');
21
20
  const {
@@ -36,33 +35,6 @@ const { EXIT_CODES } = require('./enums/exitCodes');
36
35
  const { uiLine, uiAccountDescription } = require('../lib/ui');
37
36
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
38
37
 
39
- const PROJECT_STRINGS = {
40
- BUILD: {
41
- INITIALIZE: (name, numOfComponents) =>
42
- `Building ${chalk.bold(name)}\n\nFound ${numOfComponents} component${
43
- numOfComponents !== 1 ? 's' : ''
44
- } in this project ...\n`,
45
- SUCCESS: name => `Built ${chalk.bold(name)}`,
46
- FAIL: name => `Failed to build ${chalk.bold(name)}`,
47
- SUBTASK_FAIL: (taskId, name) =>
48
- `Build #${taskId} failed because there was a problem\nbuilding ${chalk.bold(
49
- name
50
- )}`,
51
- },
52
- DEPLOY: {
53
- INITIALIZE: (name, numOfComponents) =>
54
- `Deploying ${chalk.bold(name)}\n\nFound ${numOfComponents} component${
55
- numOfComponents !== 1 ? 's' : ''
56
- } in this project ...\n`,
57
- SUCCESS: name => `Deployed ${chalk.bold(name)}`,
58
- FAIL: name => `Failed to deploy ${chalk.bold(name)}`,
59
- SUBTASK_FAIL: (taskId, name) =>
60
- `Deploy for build #${taskId} failed because there was a\nproblem deploying ${chalk.bold(
61
- name
62
- )}`,
63
- },
64
- };
65
-
66
38
  const writeProjectConfig = (configPath, config) => {
67
39
  try {
68
40
  fs.ensureFileSync(configPath);
@@ -131,14 +103,16 @@ const createProjectConfig = async (projectPath, projectName, template) => {
131
103
  ]);
132
104
 
133
105
  if (!shouldContinue) {
134
- return;
106
+ return false;
135
107
  }
136
108
  }
137
109
 
138
110
  const projectConfigPath = path.join(projectPath, PROJECT_CONFIG_FILE);
139
111
 
140
112
  logger.log(
141
- `Creating project in ${projectPath ? projectPath : 'the current folder'}`
113
+ `Creating project config in ${
114
+ projectPath ? projectPath : 'the current folder'
115
+ }`
142
116
  );
143
117
 
144
118
  if (template === 'none') {
@@ -149,7 +123,7 @@ const createProjectConfig = async (projectPath, projectName, template) => {
149
123
  srcDir: 'src',
150
124
  });
151
125
  } else {
152
- await createProjectTemplate(
126
+ await cloneGitHubRepo(
153
127
  projectPath,
154
128
  'project',
155
129
  PROJECT_TEMPLATES.find(t => t.name === template).repo,
@@ -162,7 +136,7 @@ const createProjectConfig = async (projectPath, projectName, template) => {
162
136
  });
163
137
  }
164
138
 
165
- return projectConfig;
139
+ return true;
166
140
  };
167
141
 
168
142
  const validateProjectConfig = (projectConfig, projectDir) => {
@@ -188,14 +162,18 @@ const validateProjectConfig = (projectConfig, projectDir) => {
188
162
  }
189
163
  };
190
164
 
191
- const ensureProjectExists = async (accountId, projectName, forceCreate) => {
165
+ const ensureProjectExists = async (
166
+ accountId,
167
+ projectName,
168
+ { forceCreate, allowCreate } = { forceCreate: false, allowCreate: true }
169
+ ) => {
192
170
  try {
193
171
  await fetchProject(accountId, projectName);
194
172
  } catch (err) {
195
173
  if (err.statusCode === 404) {
196
174
  let shouldCreateProject = forceCreate;
197
175
 
198
- if (!shouldCreateProject) {
176
+ if (allowCreate && !shouldCreateProject) {
199
177
  const promptResult = await promptUser([
200
178
  {
201
179
  name: 'shouldCreateProject',
@@ -338,12 +316,12 @@ const handleProjectUpload = async (
338
316
  archive.finalize();
339
317
  };
340
318
 
341
- const showWelcomeMessage = () => {
319
+ const showProjectWelcomeMessage = () => {
342
320
  logger.log('');
343
321
  logger.log(chalk.bold('Welcome to HubSpot Developer Projects!'));
344
- logger.log(
345
- '\n-------------------------------------------------------------\n'
346
- );
322
+ logger.log('\n');
323
+ uiLine();
324
+ logger.log('\n');
347
325
  logger.log(chalk.bold("What's next?\n"));
348
326
  logger.log('🎨 Add components to your project with `hs create`.\n');
349
327
  logger.log(
@@ -355,35 +333,22 @@ const showWelcomeMessage = () => {
355
333
  logger.log(
356
334
  `🔗 Use \`hs project --help\` to learn more about available commands.\n`
357
335
  );
358
- logger.log('-------------------------------------------------------------');
336
+ uiLine();
359
337
  };
360
338
 
361
- const makeGetTaskStatus = taskType => {
362
- let statusFn, statusText, statusStrings;
363
- switch (taskType) {
364
- case 'build':
365
- statusFn = getBuildStatus;
366
- statusText = PROJECT_TEXT.BUILD;
367
- statusStrings = PROJECT_STRINGS.BUILD;
368
- break;
369
- case 'deploy':
370
- statusFn = getDeployStatus;
371
- statusText = PROJECT_TEXT.DEPLOY;
372
- statusStrings = PROJECT_STRINGS.DEPLOY;
373
- break;
374
- default:
375
- logger.error(`Cannot get status for task type ${taskType}`);
376
- }
339
+ const makePollTaskStatusFunc = ({ statusFn, statusText, statusStrings }) => {
340
+ const isTaskComplete = task => {
341
+ if (
342
+ !task[statusText.SUBTASK_KEY].length ||
343
+ task.status === statusText.STATES.FAILURE
344
+ ) {
345
+ return true;
346
+ } else if (task.status === statusText.STATES.SUCCESS) {
347
+ return task.isAutoDeployEnabled ? !!task.deployStatusTaskLocator : true;
348
+ }
349
+ };
377
350
 
378
351
  return async (accountId, taskName, taskId, buildId) => {
379
- const isTaskComplete = task => {
380
- if (task.status === statusText.STATES.FAILURE) {
381
- return true;
382
- } else if (task.status === statusText.STATES.SUCCESS) {
383
- return task.isAutoDeployEnabled ? !!task.deployStatusTaskLocator : true;
384
- }
385
- };
386
-
387
352
  const spinnies = new Spinnies({
388
353
  succeedColor: 'white',
389
354
  failColor: 'white',
@@ -402,9 +367,12 @@ const makeGetTaskStatus = taskType => {
402
367
  });
403
368
 
404
369
  for (let subTask of initialTaskStatus[statusText.SUBTASK_KEY]) {
405
- spinnies.add(subTask[statusText.SUBTASK_NAME_KEY], {
406
- text: `${chalk.bold(subTask[statusText.SUBTASK_NAME_KEY])} #${buildId ||
407
- taskId} ${statusText.STATUS_TEXT[statusText.STATES.ENQUEUED]}\n`,
370
+ const subTaskName = subTask[statusText.SUBTASK_NAME_KEY];
371
+
372
+ spinnies.add(subTaskName, {
373
+ text: `${chalk.bold(subTaskName)} #${buildId || taskId} ${
374
+ statusText.STATUS_TEXT[statusText.STATES.ENQUEUED]
375
+ }\n`,
408
376
  });
409
377
  }
410
378
 
@@ -418,29 +386,25 @@ const makeGetTaskStatus = taskType => {
418
386
 
419
387
  if (spinnies.hasActiveSpinners()) {
420
388
  subTaskStatus.forEach(subTask => {
421
- if (!spinnies.pick(subTask[statusText.SUBTASK_NAME_KEY])) {
389
+ const subTaskName = subTask[statusText.SUBTASK_NAME_KEY];
390
+
391
+ if (!spinnies.pick(subTaskName)) {
422
392
  return;
423
393
  }
424
394
 
425
- const updatedText = `${chalk.bold(
426
- subTask[statusText.SUBTASK_NAME_KEY]
427
- )} #${taskId} ${statusText.STATUS_TEXT[subTask.status]}\n`;
395
+ const updatedText = `${chalk.bold(subTaskName)} #${taskId} ${
396
+ statusText.STATUS_TEXT[subTask.status]
397
+ }\n`;
428
398
 
429
399
  switch (subTask.status) {
430
400
  case statusText.STATES.SUCCESS:
431
- spinnies.succeed(subTask[statusText.SUBTASK_NAME_KEY], {
432
- text: updatedText,
433
- });
401
+ spinnies.succeed(subTaskName, { text: updatedText });
434
402
  break;
435
403
  case statusText.STATES.FAILURE:
436
- spinnies.fail(subTask[statusText.SUBTASK_NAME_KEY], {
437
- text: updatedText,
438
- });
404
+ spinnies.fail(subTaskName, { text: updatedText });
439
405
  break;
440
406
  default:
441
- spinnies.update(subTask[statusText.SUBTASK_NAME_KEY], {
442
- text: updatedText,
443
- });
407
+ spinnies.update(subTaskName, { text: updatedText });
444
408
  break;
445
409
  }
446
410
  });
@@ -494,6 +458,40 @@ const makeGetTaskStatus = taskType => {
494
458
  };
495
459
  };
496
460
 
461
+ const pollBuildStatus = makePollTaskStatusFunc({
462
+ statusFn: getBuildStatus,
463
+ statusText: PROJECT_BUILD_TEXT,
464
+ statusStrings: {
465
+ INITIALIZE: (name, numOfComponents) =>
466
+ `Building ${chalk.bold(name)}\n\nFound ${numOfComponents} component${
467
+ numOfComponents !== 1 ? 's' : ''
468
+ } in this project ...\n`,
469
+ SUCCESS: name => `Built ${chalk.bold(name)}`,
470
+ FAIL: name => `Failed to build ${chalk.bold(name)}`,
471
+ SUBTASK_FAIL: (taskId, name) =>
472
+ `Build #${taskId} failed because there was a problem\nbuilding ${chalk.bold(
473
+ name
474
+ )}`,
475
+ },
476
+ });
477
+
478
+ const pollDeployStatus = makePollTaskStatusFunc({
479
+ statusFn: getDeployStatus,
480
+ statusText: PROJECT_DEPLOY_TEXT,
481
+ statusStrings: {
482
+ INITIALIZE: (name, numOfComponents) =>
483
+ `Deploying ${chalk.bold(name)}\n\nFound ${numOfComponents} component${
484
+ numOfComponents !== 1 ? 's' : ''
485
+ } in this project ...\n`,
486
+ SUCCESS: name => `Deployed ${chalk.bold(name)}`,
487
+ FAIL: name => `Failed to deploy ${chalk.bold(name)}`,
488
+ SUBTASK_FAIL: (taskId, name) =>
489
+ `Deploy for build #${taskId} failed because there was a\nproblem deploying ${chalk.bold(
490
+ name
491
+ )}`,
492
+ },
493
+ });
494
+
497
495
  module.exports = {
498
496
  writeProjectConfig,
499
497
  getProjectConfig,
@@ -501,9 +499,9 @@ module.exports = {
501
499
  handleProjectUpload,
502
500
  createProjectConfig,
503
501
  validateProjectConfig,
504
- showWelcomeMessage,
502
+ showProjectWelcomeMessage,
505
503
  getProjectDetailUrl,
506
- pollBuildStatus: makeGetTaskStatus('build'),
507
- pollDeployStatus: makeGetTaskStatus('deploy'),
504
+ pollBuildStatus,
505
+ pollDeployStatus,
508
506
  ensureProjectExists,
509
507
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "3.0.12-beta.0",
3
+ "version": "3.0.12-beta.1",
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.12-beta.0",
12
- "@hubspot/serverless-dev-runtime": "^3.0.12-beta.0",
11
+ "@hubspot/cli-lib": "^3.0.12-beta.1",
12
+ "@hubspot/serverless-dev-runtime": "^3.0.12-beta.1",
13
13
  "archiver": "^5.3.0",
14
14
  "chalk": "^4.1.2",
15
15
  "express": "^4.17.1",
@@ -39,5 +39,5 @@
39
39
  "publishConfig": {
40
40
  "access": "public"
41
41
  },
42
- "gitHead": "cb056da116cf2f64b7e2f7df69f44e5a19d03527"
42
+ "gitHead": "01b4b086b16f92ec7a0c5022ed0beb4de6a16ae7"
43
43
  }