@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 +3 -0
- package/commands/create/api-sample.js +2 -2
- package/commands/create/app.js +2 -2
- package/commands/create/react-app.js +2 -2
- package/commands/create/vue-app.js +2 -2
- package/commands/create/webpack-serverless.js +2 -2
- package/commands/create/website-theme.js +2 -2
- package/commands/project/create.js +6 -1
- package/commands/project/download.js +127 -0
- package/commands/project/upload.js +8 -106
- package/commands/project/watch.js +34 -7
- package/commands/project.js +2 -0
- package/lib/projects.js +191 -86
- package/package.json +4 -4
package/bin/hubspot
ADDED
|
@@ -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 {
|
|
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
|
|
63
|
+
const created = await cloneGitHubRepo(
|
|
64
64
|
filePath,
|
|
65
65
|
assetType,
|
|
66
66
|
sampleType,
|
package/commands/create/app.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
const {
|
|
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
|
-
|
|
7
|
+
cloneGitHubRepo(dest, assetType, 'crm-card-weather-app', '', options),
|
|
8
8
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const {
|
|
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
|
-
|
|
6
|
+
cloneGitHubRepo(dest, assetType, 'cms-react-boilerplate', '', options),
|
|
7
7
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
const {
|
|
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
|
-
|
|
6
|
+
cloneGitHubRepo(dest, assetType, 'cms-vue-boilerplate', '', options),
|
|
7
7
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const {
|
|
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
|
-
|
|
6
|
+
cloneGitHubRepo(
|
|
7
7
|
dest,
|
|
8
8
|
assetType,
|
|
9
9
|
'cms-webpack-serverless-boilerplate',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const {
|
|
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
|
-
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
84
|
+
await ensureProjectExists(accountId, projectConfig.name);
|
|
85
|
+
|
|
86
|
+
const { results } = await fetchProjectBuilds(
|
|
80
87
|
accountId,
|
|
81
|
-
projectConfig,
|
|
82
|
-
|
|
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 => {
|
package/commands/project.js
CHANGED
|
@@ -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
|
-
|
|
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 ${
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
336
|
+
uiLine();
|
|
253
337
|
};
|
|
254
338
|
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
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
|
-
|
|
321
|
-
|
|
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(
|
|
326
|
-
text: updatedText,
|
|
327
|
-
});
|
|
401
|
+
spinnies.succeed(subTaskName, { text: updatedText });
|
|
328
402
|
break;
|
|
329
403
|
case statusText.STATES.FAILURE:
|
|
330
|
-
spinnies.fail(
|
|
331
|
-
text: updatedText,
|
|
332
|
-
});
|
|
404
|
+
spinnies.fail(subTaskName, { text: updatedText });
|
|
333
405
|
break;
|
|
334
406
|
default:
|
|
335
|
-
spinnies.update(
|
|
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
|
-
|
|
502
|
+
showProjectWelcomeMessage,
|
|
398
503
|
getProjectDetailUrl,
|
|
399
|
-
pollBuildStatus
|
|
400
|
-
pollDeployStatus
|
|
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.
|
|
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
|
-
"@hubspot/serverless-dev-runtime": "^3.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": "
|
|
42
|
+
"gitHead": "01b4b086b16f92ec7a0c5022ed0beb4de6a16ae7"
|
|
43
43
|
}
|