@hubspot/cli 3.0.11-beta.1 → 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.
package/bin/hubspot ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+
3
+ require('./cli');
@@ -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
+ };
@@ -1,9 +1,4 @@
1
1
  const chalk = require('chalk');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const archiver = require('archiver');
5
- const tmp = require('tmp');
6
- const Spinnies = require('spinnies');
7
2
  const {
8
3
  addAccountOptions,
9
4
  addConfigOptions,
@@ -11,84 +6,25 @@ const {
11
6
  addUseEnvironmentOptions,
12
7
  } = require('../../lib/commonOpts');
13
8
  const { trackCommandUsage } = require('../../lib/usageTracking');
14
- const {
15
- logApiErrorInstance,
16
- ApiErrorContext,
17
- } = require('@hubspot/cli-lib/errorHandlers');
18
9
  const { uiLine, uiAccountDescription } = require('../../lib/ui');
19
10
  const { logger } = require('@hubspot/cli-lib/logger');
20
- const { uploadProject } = require('@hubspot/cli-lib/api/dfs');
21
- const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');
22
11
  const { loadAndValidateOptions } = require('../../lib/validation');
23
12
  const {
13
+ ensureProjectExists,
24
14
  getProjectConfig,
25
- validateProjectConfig,
15
+ handleProjectUpload,
26
16
  pollBuildStatus,
27
- ensureProjectExists,
28
17
  pollDeployStatus,
18
+ validateProjectConfig,
29
19
  } = require('../../lib/projects');
30
20
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
21
+ const { EXIT_CODES } = require('../../lib/enums/exitCodes');
31
22
 
32
23
  const i18nKey = 'cli.commands.project.subcommands.upload';
33
- const { EXIT_CODES } = require('../../lib/enums/exitCodes');
34
24
 
35
25
  exports.command = 'upload [path]';
36
26
  exports.describe = i18n(`${i18nKey}.describe`);
37
27
 
38
- const uploadProjectFiles = async (accountId, projectName, filePath) => {
39
- const spinnies = new Spinnies({
40
- succeedColor: 'white',
41
- });
42
- const accountIdentifier = uiAccountDescription(accountId);
43
-
44
- spinnies.add('upload', {
45
- text: i18n(`${i18nKey}.loading.upload.add`, {
46
- accountIdentifier,
47
- projectName,
48
- }),
49
- });
50
-
51
- let buildId;
52
-
53
- try {
54
- const upload = await uploadProject(accountId, projectName, filePath);
55
-
56
- buildId = upload.buildId;
57
-
58
- spinnies.succeed('upload', {
59
- text: i18n(`${i18nKey}.loading.upload.succeed`, {
60
- accountIdentifier,
61
- projectName,
62
- }),
63
- });
64
-
65
- logger.debug(
66
- i18n(`${i18nKey}.debug.buildCreated`, {
67
- buildId,
68
- projectName,
69
- })
70
- );
71
- } catch (err) {
72
- spinnies.fail('upload', {
73
- text: i18n(`${i18nKey}.loading.upload.fail`, {
74
- accountIdentifier,
75
- projectName,
76
- }),
77
- });
78
-
79
- logApiErrorInstance(
80
- err,
81
- new ApiErrorContext({
82
- accountId,
83
- projectName,
84
- })
85
- );
86
- process.exit(EXIT_CODES.ERROR);
87
- }
88
-
89
- return { buildId };
90
- };
91
-
92
28
  exports.handler = async options => {
93
29
  await loadAndValidateOptions(options);
94
30
 
@@ -101,32 +37,10 @@ exports.handler = async options => {
101
37
 
102
38
  validateProjectConfig(projectConfig, projectDir);
103
39
 
104
- await ensureProjectExists(accountId, projectConfig.name, forceCreate);
105
-
106
- const tempFile = tmp.fileSync({ postfix: '.zip' });
107
-
108
- logger.debug(
109
- i18n(`${i18nKey}.debug.compressing`, {
110
- path: tempFile.name,
111
- })
112
- );
113
-
114
- const output = fs.createWriteStream(tempFile.name);
115
- const archive = archiver('zip');
40
+ await ensureProjectExists(accountId, projectConfig.name, { forceCreate });
116
41
 
117
- output.on('close', async function() {
42
+ const startPolling = async (tempFile, buildId) => {
118
43
  let exitCode = EXIT_CODES.SUCCESS;
119
- logger.debug(
120
- i18n(`${i18nKey}.debug.compressed`, {
121
- byteCount: archive.pointer(),
122
- })
123
- );
124
-
125
- const { buildId } = await uploadProjectFiles(
126
- accountId,
127
- projectConfig.name,
128
- tempFile.name
129
- );
130
44
 
131
45
  const {
132
46
  isAutoDeployEnabled,
@@ -183,21 +97,9 @@ exports.handler = async options => {
183
97
  }
184
98
 
185
99
  process.exit(exitCode);
186
- });
187
-
188
- archive.on('error', function(err) {
189
- throw err;
190
- });
191
-
192
- archive.pipe(output);
193
-
194
- archive.directory(
195
- path.resolve(projectDir, projectConfig.srcDir),
196
- false,
197
- file => (shouldIgnoreFile(file.name) ? false : file)
198
- );
100
+ };
199
101
 
200
- archive.finalize();
102
+ await handleProjectUpload(accountId, projectConfig, projectDir, startPolling);
201
103
  };
202
104
 
203
105
  exports.builder = yargs => {
@@ -1,6 +1,5 @@
1
1
  const { i18n } = require('@hubspot/cli-lib/lib/lang');
2
2
  const { createWatcher } = require('@hubspot/cli-lib/projectsWatch');
3
- const { cancelStagedBuild } = require('@hubspot/cli-lib/api/dfs');
4
3
  const {
5
4
  logApiErrorInstance,
6
5
  ApiErrorContext,
@@ -14,11 +13,17 @@ const {
14
13
  } = require('../../lib/commonOpts');
15
14
  const { trackCommandUsage } = require('../../lib/usageTracking');
16
15
  const {
16
+ ensureProjectExists,
17
17
  getProjectConfig,
18
- validateProjectConfig,
18
+ handleProjectUpload,
19
19
  pollBuildStatus,
20
20
  pollDeployStatus,
21
+ validateProjectConfig,
21
22
  } = require('../../lib/projects');
23
+ const {
24
+ cancelStagedBuild,
25
+ fetchProjectBuilds,
26
+ } = require('@hubspot/cli-lib/api/dfs');
22
27
  const { loadAndValidateOptions } = require('../../lib/validation');
23
28
  const { EXIT_CODES } = require('../../lib/enums/exitCodes');
24
29
 
@@ -76,13 +81,35 @@ exports.handler = async options => {
76
81
 
77
82
  validateProjectConfig(projectConfig, projectDir);
78
83
 
79
- await createWatcher(
84
+ await ensureProjectExists(accountId, projectConfig.name);
85
+
86
+ const { results } = await fetchProjectBuilds(
80
87
  accountId,
81
- projectConfig,
82
- projectDir,
83
- handleBuildStatus,
84
- handleSigInt
88
+ projectConfig.name,
89
+ options
85
90
  );
91
+
92
+ const startWatching = async () => {
93
+ await createWatcher(
94
+ accountId,
95
+ projectConfig,
96
+ projectDir,
97
+ handleBuildStatus,
98
+ handleSigInt
99
+ );
100
+ };
101
+
102
+ // Upload all files if no build exists for this project yet
103
+ if (!results || !results.length) {
104
+ await handleProjectUpload(
105
+ accountId,
106
+ projectConfig,
107
+ projectDir,
108
+ startWatching
109
+ );
110
+ } else {
111
+ await startWatching();
112
+ }
86
113
  };
87
114
 
88
115
  exports.builder = yargs => {
@@ -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
@@ -1,20 +1,20 @@
1
1
  const fs = require('fs-extra');
2
2
  const path = require('path');
3
-
3
+ const archiver = require('archiver');
4
+ const tmp = require('tmp');
4
5
  const chalk = require('chalk');
5
6
  const findup = require('findup-sync');
6
7
  const Spinnies = require('spinnies');
7
8
  const { logger } = require('@hubspot/cli-lib/logger');
8
9
  const { getEnv } = require('@hubspot/cli-lib/lib/config');
9
- const {
10
- createProject: createProjectTemplate,
11
- } = require('@hubspot/cli-lib/projects');
10
+ const { cloneGitHubRepo } = require('@hubspot/cli-lib/github');
12
11
  const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
13
12
  const {
14
13
  ENVIRONMENTS,
15
14
  POLLING_DELAY,
16
15
  PROJECT_TEMPLATES,
17
- PROJECT_TEXT,
16
+ PROJECT_BUILD_TEXT,
17
+ PROJECT_DEPLOY_TEXT,
18
18
  PROJECT_CONFIG_FILE,
19
19
  } = require('@hubspot/cli-lib/lib/constants');
20
20
  const {
@@ -22,42 +22,18 @@ const {
22
22
  getBuildStatus,
23
23
  getDeployStatus,
24
24
  fetchProject,
25
+ uploadProject,
25
26
  } = require('@hubspot/cli-lib/api/dfs');
26
27
  const {
27
28
  logApiErrorInstance,
28
29
  ApiErrorContext,
29
30
  } = require('@hubspot/cli-lib/errorHandlers');
31
+ const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');
30
32
  const { getCwd } = require('@hubspot/cli-lib/path');
31
33
  const { promptUser } = require('./prompts/promptUtils');
32
34
  const { EXIT_CODES } = require('./enums/exitCodes');
33
35
  const { uiLine, uiAccountDescription } = require('../lib/ui');
34
-
35
- const PROJECT_STRINGS = {
36
- BUILD: {
37
- INITIALIZE: (name, numOfComponents) =>
38
- `Building ${chalk.bold(name)}\n\nFound ${numOfComponents} component${
39
- numOfComponents !== 1 ? 's' : ''
40
- } in this project ...\n`,
41
- SUCCESS: name => `Built ${chalk.bold(name)}`,
42
- FAIL: name => `Failed to build ${chalk.bold(name)}`,
43
- SUBTASK_FAIL: (taskId, name) =>
44
- `Build #${taskId} failed because there was a problem\nbuilding ${chalk.bold(
45
- name
46
- )}`,
47
- },
48
- DEPLOY: {
49
- INITIALIZE: (name, numOfComponents) =>
50
- `Deploying ${chalk.bold(name)}\n\nFound ${numOfComponents} component${
51
- numOfComponents !== 1 ? 's' : ''
52
- } in this project ...\n`,
53
- SUCCESS: name => `Deployed ${chalk.bold(name)}`,
54
- FAIL: name => `Failed to deploy ${chalk.bold(name)}`,
55
- SUBTASK_FAIL: (taskId, name) =>
56
- `Deploy for build #${taskId} failed because there was a\nproblem deploying ${chalk.bold(
57
- name
58
- )}`,
59
- },
60
- };
36
+ const { i18n } = require('@hubspot/cli-lib/lib/lang');
61
37
 
62
38
  const writeProjectConfig = (configPath, config) => {
63
39
  try {
@@ -127,14 +103,16 @@ const createProjectConfig = async (projectPath, projectName, template) => {
127
103
  ]);
128
104
 
129
105
  if (!shouldContinue) {
130
- return;
106
+ return false;
131
107
  }
132
108
  }
133
109
 
134
110
  const projectConfigPath = path.join(projectPath, PROJECT_CONFIG_FILE);
135
111
 
136
112
  logger.log(
137
- `Creating project in ${projectPath ? projectPath : 'the current folder'}`
113
+ `Creating project config in ${
114
+ projectPath ? projectPath : 'the current folder'
115
+ }`
138
116
  );
139
117
 
140
118
  if (template === 'none') {
@@ -145,7 +123,7 @@ const createProjectConfig = async (projectPath, projectName, template) => {
145
123
  srcDir: 'src',
146
124
  });
147
125
  } else {
148
- await createProjectTemplate(
126
+ await cloneGitHubRepo(
149
127
  projectPath,
150
128
  'project',
151
129
  PROJECT_TEMPLATES.find(t => t.name === template).repo,
@@ -158,7 +136,7 @@ const createProjectConfig = async (projectPath, projectName, template) => {
158
136
  });
159
137
  }
160
138
 
161
- return projectConfig;
139
+ return true;
162
140
  };
163
141
 
164
142
  const validateProjectConfig = (projectConfig, projectDir) => {
@@ -184,14 +162,18 @@ const validateProjectConfig = (projectConfig, projectDir) => {
184
162
  }
185
163
  };
186
164
 
187
- const ensureProjectExists = async (accountId, projectName, forceCreate) => {
165
+ const ensureProjectExists = async (
166
+ accountId,
167
+ projectName,
168
+ { forceCreate, allowCreate } = { forceCreate: false, allowCreate: true }
169
+ ) => {
188
170
  try {
189
171
  await fetchProject(accountId, projectName);
190
172
  } catch (err) {
191
173
  if (err.statusCode === 404) {
192
174
  let shouldCreateProject = forceCreate;
193
175
 
194
- if (!shouldCreateProject) {
176
+ if (allowCreate && !shouldCreateProject) {
195
177
  const promptResult = await promptUser([
196
178
  {
197
179
  name: 'shouldCreateProject',
@@ -232,12 +214,114 @@ const getProjectDetailUrl = (projectName, accountId) => {
232
214
  return `${baseUrl}/developer-projects/${accountId}/project/${projectName}`;
233
215
  };
234
216
 
235
- const showWelcomeMessage = () => {
217
+ const uploadProjectFiles = async (accountId, projectName, filePath) => {
218
+ const i18nKey = 'cli.commands.project.subcommands.upload';
219
+ const spinnies = new Spinnies({
220
+ succeedColor: 'white',
221
+ });
222
+ const accountIdentifier = uiAccountDescription(accountId);
223
+
224
+ spinnies.add('upload', {
225
+ text: i18n(`${i18nKey}.loading.upload.add`, {
226
+ accountIdentifier,
227
+ projectName,
228
+ }),
229
+ });
230
+
231
+ let buildId;
232
+
233
+ try {
234
+ const upload = await uploadProject(accountId, projectName, filePath);
235
+
236
+ buildId = upload.buildId;
237
+
238
+ spinnies.succeed('upload', {
239
+ text: i18n(`${i18nKey}.loading.upload.succeed`, {
240
+ accountIdentifier,
241
+ projectName,
242
+ }),
243
+ });
244
+
245
+ logger.debug(
246
+ i18n(`${i18nKey}.debug.buildCreated`, {
247
+ buildId,
248
+ projectName,
249
+ })
250
+ );
251
+ } catch (err) {
252
+ spinnies.fail('upload', {
253
+ text: i18n(`${i18nKey}.loading.upload.fail`, {
254
+ accountIdentifier,
255
+ projectName,
256
+ }),
257
+ });
258
+
259
+ logApiErrorInstance(
260
+ err,
261
+ new ApiErrorContext({
262
+ accountId,
263
+ projectName,
264
+ })
265
+ );
266
+ process.exit(EXIT_CODES.ERROR);
267
+ }
268
+
269
+ return { buildId };
270
+ };
271
+
272
+ const handleProjectUpload = async (
273
+ accountId,
274
+ projectConfig,
275
+ projectDir,
276
+ callbackFunc
277
+ ) => {
278
+ const i18nKey = 'cli.commands.project.subcommands.upload';
279
+ const tempFile = tmp.fileSync({ postfix: '.zip' });
280
+
281
+ logger.debug(
282
+ i18n(`${i18nKey}.debug.compressing`, {
283
+ path: tempFile.name,
284
+ })
285
+ );
286
+
287
+ const output = fs.createWriteStream(tempFile.name);
288
+ const archive = archiver('zip');
289
+
290
+ output.on('close', async function() {
291
+ logger.debug(
292
+ i18n(`${i18nKey}.debug.compressed`, {
293
+ byteCount: archive.pointer(),
294
+ })
295
+ );
296
+
297
+ const { buildId } = await uploadProjectFiles(
298
+ accountId,
299
+ projectConfig.name,
300
+ tempFile.name
301
+ );
302
+
303
+ if (callbackFunc) {
304
+ callbackFunc(tempFile, buildId);
305
+ }
306
+ });
307
+
308
+ archive.pipe(output);
309
+
310
+ archive.directory(
311
+ path.resolve(projectDir, projectConfig.srcDir),
312
+ false,
313
+ file => (shouldIgnoreFile(file.name) ? false : file)
314
+ );
315
+
316
+ archive.finalize();
317
+ };
318
+
319
+ const showProjectWelcomeMessage = () => {
236
320
  logger.log('');
237
321
  logger.log(chalk.bold('Welcome to HubSpot Developer Projects!'));
238
- logger.log(
239
- '\n-------------------------------------------------------------\n'
240
- );
322
+ logger.log('\n');
323
+ uiLine();
324
+ logger.log('\n');
241
325
  logger.log(chalk.bold("What's next?\n"));
242
326
  logger.log('🎨 Add components to your project with `hs create`.\n');
243
327
  logger.log(
@@ -249,35 +333,22 @@ const showWelcomeMessage = () => {
249
333
  logger.log(
250
334
  `🔗 Use \`hs project --help\` to learn more about available commands.\n`
251
335
  );
252
- logger.log('-------------------------------------------------------------');
336
+ uiLine();
253
337
  };
254
338
 
255
- const makeGetTaskStatus = taskType => {
256
- let statusFn, statusText, statusStrings;
257
- switch (taskType) {
258
- case 'build':
259
- statusFn = getBuildStatus;
260
- statusText = PROJECT_TEXT.BUILD;
261
- statusStrings = PROJECT_STRINGS.BUILD;
262
- break;
263
- case 'deploy':
264
- statusFn = getDeployStatus;
265
- statusText = PROJECT_TEXT.DEPLOY;
266
- statusStrings = PROJECT_STRINGS.DEPLOY;
267
- break;
268
- default:
269
- logger.error(`Cannot get status for task type ${taskType}`);
270
- }
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
+ };
271
350
 
272
351
  return async (accountId, taskName, taskId, buildId) => {
273
- const isTaskComplete = task => {
274
- if (task.status === statusText.STATES.FAILURE) {
275
- return true;
276
- } else if (task.status === statusText.STATES.SUCCESS) {
277
- return task.isAutoDeployEnabled ? !!task.deployStatusTaskLocator : true;
278
- }
279
- };
280
-
281
352
  const spinnies = new Spinnies({
282
353
  succeedColor: 'white',
283
354
  failColor: 'white',
@@ -296,9 +367,12 @@ const makeGetTaskStatus = taskType => {
296
367
  });
297
368
 
298
369
  for (let subTask of initialTaskStatus[statusText.SUBTASK_KEY]) {
299
- spinnies.add(subTask[statusText.SUBTASK_NAME_KEY], {
300
- text: `${chalk.bold(subTask[statusText.SUBTASK_NAME_KEY])} #${buildId ||
301
- 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`,
302
376
  });
303
377
  }
304
378
 
@@ -312,29 +386,25 @@ const makeGetTaskStatus = taskType => {
312
386
 
313
387
  if (spinnies.hasActiveSpinners()) {
314
388
  subTaskStatus.forEach(subTask => {
315
- if (!spinnies.pick(subTask[statusText.SUBTASK_NAME_KEY])) {
389
+ const subTaskName = subTask[statusText.SUBTASK_NAME_KEY];
390
+
391
+ if (!spinnies.pick(subTaskName)) {
316
392
  return;
317
393
  }
318
394
 
319
- const updatedText = `${chalk.bold(
320
- subTask[statusText.SUBTASK_NAME_KEY]
321
- )} #${taskId} ${statusText.STATUS_TEXT[subTask.status]}\n`;
395
+ const updatedText = `${chalk.bold(subTaskName)} #${taskId} ${
396
+ statusText.STATUS_TEXT[subTask.status]
397
+ }\n`;
322
398
 
323
399
  switch (subTask.status) {
324
400
  case statusText.STATES.SUCCESS:
325
- spinnies.succeed(subTask[statusText.SUBTASK_NAME_KEY], {
326
- text: updatedText,
327
- });
401
+ spinnies.succeed(subTaskName, { text: updatedText });
328
402
  break;
329
403
  case statusText.STATES.FAILURE:
330
- spinnies.fail(subTask[statusText.SUBTASK_NAME_KEY], {
331
- text: updatedText,
332
- });
404
+ spinnies.fail(subTaskName, { text: updatedText });
333
405
  break;
334
406
  default:
335
- spinnies.update(subTask[statusText.SUBTASK_NAME_KEY], {
336
- text: updatedText,
337
- });
407
+ spinnies.update(subTaskName, { text: updatedText });
338
408
  break;
339
409
  }
340
410
  });
@@ -388,15 +458,50 @@ const makeGetTaskStatus = taskType => {
388
458
  };
389
459
  };
390
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
+
391
495
  module.exports = {
392
496
  writeProjectConfig,
393
497
  getProjectConfig,
394
498
  getIsInProject,
499
+ handleProjectUpload,
395
500
  createProjectConfig,
396
501
  validateProjectConfig,
397
- showWelcomeMessage,
502
+ showProjectWelcomeMessage,
398
503
  getProjectDetailUrl,
399
- pollBuildStatus: makeGetTaskStatus('build'),
400
- pollDeployStatus: makeGetTaskStatus('deploy'),
504
+ pollBuildStatus,
505
+ pollDeployStatus,
401
506
  ensureProjectExists,
402
507
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/cli",
3
- "version": "3.0.11-beta.1",
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.11-beta.1",
12
- "@hubspot/serverless-dev-runtime": "^3.0.11-beta.1",
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": "7667862d4e75e8f896b25030956dcfc1dc6124ca"
42
+ "gitHead": "01b4b086b16f92ec7a0c5022ed0beb4de6a16ae7"
43
43
  }