@hubspot/cli 3.0.12-beta.0 → 3.0.12
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/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 +3 -1
- package/commands/project/watch.js +24 -9
- package/commands/project.js +2 -0
- package/lib/projects.js +110 -93
- package/lib/serverlessLogs.js +8 -16
- package/lib/ui.js +8 -1
- package/package.json +4 -4
|
@@ -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
|
+
};
|
|
@@ -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;
|
|
@@ -48,6 +48,8 @@ exports.handler = async options => {
|
|
|
48
48
|
status,
|
|
49
49
|
} = await pollBuildStatus(accountId, projectConfig.name, buildId);
|
|
50
50
|
|
|
51
|
+
uiLine();
|
|
52
|
+
|
|
51
53
|
if (status === 'FAILURE') {
|
|
52
54
|
exitCode = EXIT_CODES.ERROR;
|
|
53
55
|
return;
|
|
@@ -26,6 +26,7 @@ const {
|
|
|
26
26
|
} = require('@hubspot/cli-lib/api/dfs');
|
|
27
27
|
const { loadAndValidateOptions } = require('../../lib/validation');
|
|
28
28
|
const { EXIT_CODES } = require('../../lib/enums/exitCodes');
|
|
29
|
+
const { handleKeypress, handleExit } = require('@hubspot/cli-lib/lib/process');
|
|
29
30
|
|
|
30
31
|
const i18nKey = 'cli.commands.project.subcommands.watch';
|
|
31
32
|
|
|
@@ -48,13 +49,13 @@ const handleBuildStatus = async (accountId, projectName, buildId) => {
|
|
|
48
49
|
}
|
|
49
50
|
};
|
|
50
51
|
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const handleUserInput = (accountId, projectName, currentBuildId) => {
|
|
53
|
+
const onTerminate = async () => {
|
|
54
|
+
logger.log(i18n(`${i18nKey}.logs.processExited`));
|
|
55
|
+
|
|
54
56
|
if (currentBuildId) {
|
|
55
57
|
try {
|
|
56
58
|
await cancelStagedBuild(accountId, projectName);
|
|
57
|
-
logger.debug(i18n(`${i18nKey}.debug.buildCancelled`));
|
|
58
59
|
process.exit(EXIT_CODES.SUCCESS);
|
|
59
60
|
} catch (err) {
|
|
60
61
|
logApiErrorInstance(
|
|
@@ -66,13 +67,20 @@ const handleSigInt = (accountId, projectName, currentBuildId) => {
|
|
|
66
67
|
} else {
|
|
67
68
|
process.exit(EXIT_CODES.SUCCESS);
|
|
68
69
|
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
handleExit(onTerminate);
|
|
73
|
+
handleKeypress(key => {
|
|
74
|
+
if ((key.ctrl && key.name === 'c') || key.name === 'q') {
|
|
75
|
+
onTerminate();
|
|
76
|
+
}
|
|
69
77
|
});
|
|
70
78
|
};
|
|
71
79
|
|
|
72
80
|
exports.handler = async options => {
|
|
73
81
|
await loadAndValidateOptions(options);
|
|
74
82
|
|
|
75
|
-
const { path: projectPath } = options;
|
|
83
|
+
const { initialUpload, path: projectPath } = options;
|
|
76
84
|
const accountId = getAccountId(options);
|
|
77
85
|
|
|
78
86
|
trackCommandUsage('project-watch', { projectPath }, accountId);
|
|
@@ -83,11 +91,12 @@ exports.handler = async options => {
|
|
|
83
91
|
|
|
84
92
|
await ensureProjectExists(accountId, projectConfig.name);
|
|
85
93
|
|
|
86
|
-
const { results } = await fetchProjectBuilds(
|
|
94
|
+
const { results: builds } = await fetchProjectBuilds(
|
|
87
95
|
accountId,
|
|
88
96
|
projectConfig.name,
|
|
89
97
|
options
|
|
90
98
|
);
|
|
99
|
+
const hasNoBuilds = !builds || !builds.length;
|
|
91
100
|
|
|
92
101
|
const startWatching = async () => {
|
|
93
102
|
await createWatcher(
|
|
@@ -95,12 +104,12 @@ exports.handler = async options => {
|
|
|
95
104
|
projectConfig,
|
|
96
105
|
projectDir,
|
|
97
106
|
handleBuildStatus,
|
|
98
|
-
|
|
107
|
+
handleUserInput
|
|
99
108
|
);
|
|
100
109
|
};
|
|
101
110
|
|
|
102
111
|
// Upload all files if no build exists for this project yet
|
|
103
|
-
if (
|
|
112
|
+
if (initialUpload || hasNoBuilds) {
|
|
104
113
|
await handleProjectUpload(
|
|
105
114
|
accountId,
|
|
106
115
|
projectConfig,
|
|
@@ -114,10 +123,16 @@ exports.handler = async options => {
|
|
|
114
123
|
|
|
115
124
|
exports.builder = yargs => {
|
|
116
125
|
yargs.positional('path', {
|
|
117
|
-
describe: i18n(`${i18nKey}.describe`),
|
|
126
|
+
describe: i18n(`${i18nKey}.positionals.path.describe`),
|
|
118
127
|
type: 'string',
|
|
119
128
|
});
|
|
120
129
|
|
|
130
|
+
yargs.option('initial-upload', {
|
|
131
|
+
alias: 'i',
|
|
132
|
+
describe: i18n(`${i18nKey}.options.initialUpload.describe`),
|
|
133
|
+
type: 'boolean',
|
|
134
|
+
});
|
|
135
|
+
|
|
121
136
|
yargs.example([
|
|
122
137
|
['$0 project watch myProjectFolder', i18n(`${i18nKey}.examples.default`)],
|
|
123
138
|
]);
|
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
|
@@ -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
|
-
|
|
16
|
+
PROJECT_BUILD_TEXT,
|
|
17
|
+
PROJECT_DEPLOY_TEXT,
|
|
19
18
|
PROJECT_CONFIG_FILE,
|
|
20
19
|
} = require('@hubspot/cli-lib/lib/constants');
|
|
21
20
|
const {
|
|
@@ -33,36 +32,9 @@ const { shouldIgnoreFile } = require('@hubspot/cli-lib/ignoreRules');
|
|
|
33
32
|
const { getCwd } = require('@hubspot/cli-lib/path');
|
|
34
33
|
const { promptUser } = require('./prompts/promptUtils');
|
|
35
34
|
const { EXIT_CODES } = require('./enums/exitCodes');
|
|
36
|
-
const { uiLine, uiAccountDescription } = require('../lib/ui');
|
|
35
|
+
const { uiLine, uiLink, 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 ${
|
|
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
|
|
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
|
|
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 (
|
|
165
|
+
const ensureProjectExists = async (
|
|
166
|
+
accountId,
|
|
167
|
+
projectName,
|
|
168
|
+
{ 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',
|
|
@@ -236,6 +214,11 @@ const getProjectDetailUrl = (projectName, accountId) => {
|
|
|
236
214
|
return `${baseUrl}/developer-projects/${accountId}/project/${projectName}`;
|
|
237
215
|
};
|
|
238
216
|
|
|
217
|
+
const getProjectBuildDetailUrl = (projectName, buildId, accountId) => {
|
|
218
|
+
if (!projectName || !buildId || !accountId) return;
|
|
219
|
+
return `${getProjectDetailUrl(projectName, accountId)}/build/${buildId}`;
|
|
220
|
+
};
|
|
221
|
+
|
|
239
222
|
const uploadProjectFiles = async (accountId, projectName, filePath) => {
|
|
240
223
|
const i18nKey = 'cli.commands.project.subcommands.upload';
|
|
241
224
|
const spinnies = new Spinnies({
|
|
@@ -338,12 +321,12 @@ const handleProjectUpload = async (
|
|
|
338
321
|
archive.finalize();
|
|
339
322
|
};
|
|
340
323
|
|
|
341
|
-
const
|
|
324
|
+
const showProjectWelcomeMessage = () => {
|
|
342
325
|
logger.log('');
|
|
343
326
|
logger.log(chalk.bold('Welcome to HubSpot Developer Projects!'));
|
|
344
|
-
logger.log(
|
|
345
|
-
|
|
346
|
-
);
|
|
327
|
+
logger.log('\n');
|
|
328
|
+
uiLine();
|
|
329
|
+
logger.log('\n');
|
|
347
330
|
logger.log(chalk.bold("What's next?\n"));
|
|
348
331
|
logger.log('🎨 Add components to your project with `hs create`.\n');
|
|
349
332
|
logger.log(
|
|
@@ -355,34 +338,31 @@ const showWelcomeMessage = () => {
|
|
|
355
338
|
logger.log(
|
|
356
339
|
`🔗 Use \`hs project --help\` to learn more about available commands.\n`
|
|
357
340
|
);
|
|
358
|
-
|
|
341
|
+
uiLine();
|
|
359
342
|
};
|
|
360
343
|
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
}
|
|
344
|
+
const makePollTaskStatusFunc = ({
|
|
345
|
+
statusFn,
|
|
346
|
+
statusText,
|
|
347
|
+
statusStrings,
|
|
348
|
+
linkToHubSpot,
|
|
349
|
+
}) => {
|
|
350
|
+
const isTaskComplete = task => {
|
|
351
|
+
if (
|
|
352
|
+
!task[statusText.SUBTASK_KEY].length ||
|
|
353
|
+
task.status === statusText.STATES.FAILURE
|
|
354
|
+
) {
|
|
355
|
+
return true;
|
|
356
|
+
} else if (task.status === statusText.STATES.SUCCESS) {
|
|
357
|
+
return task.isAutoDeployEnabled ? !!task.deployStatusTaskLocator : true;
|
|
358
|
+
}
|
|
359
|
+
};
|
|
377
360
|
|
|
378
|
-
return async (accountId, taskName, taskId
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
return task.isAutoDeployEnabled ? !!task.deployStatusTaskLocator : true;
|
|
384
|
-
}
|
|
385
|
-
};
|
|
361
|
+
return async (accountId, taskName, taskId) => {
|
|
362
|
+
let hubspotLinkText = '';
|
|
363
|
+
if (linkToHubSpot) {
|
|
364
|
+
logger.log(`\n${linkToHubSpot(taskName, taskId, accountId)}\n`);
|
|
365
|
+
}
|
|
386
366
|
|
|
387
367
|
const spinnies = new Spinnies({
|
|
388
368
|
succeedColor: 'white',
|
|
@@ -394,17 +374,23 @@ const makeGetTaskStatus = taskType => {
|
|
|
394
374
|
|
|
395
375
|
const initialTaskStatus = await statusFn(accountId, taskName, taskId);
|
|
396
376
|
|
|
377
|
+
const numOfComponents = initialTaskStatus[statusText.SUBTASK_KEY].length;
|
|
378
|
+
const componentCountText = `\nFound ${numOfComponents} component${
|
|
379
|
+
numOfComponents !== 1 ? 's' : ''
|
|
380
|
+
} in this project ...\n`;
|
|
381
|
+
|
|
397
382
|
spinnies.update('overallTaskStatus', {
|
|
398
|
-
text: statusStrings.INITIALIZE(
|
|
399
|
-
taskName,
|
|
400
|
-
initialTaskStatus[statusText.SUBTASK_KEY].length
|
|
401
|
-
),
|
|
383
|
+
text: `${statusStrings.INITIALIZE(taskName)}${componentCountText}`,
|
|
402
384
|
});
|
|
403
385
|
|
|
404
386
|
for (let subTask of initialTaskStatus[statusText.SUBTASK_KEY]) {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
387
|
+
const subTaskName = subTask[statusText.SUBTASK_NAME_KEY];
|
|
388
|
+
|
|
389
|
+
spinnies.add(subTaskName, {
|
|
390
|
+
text: `${chalk.bold(subTaskName)} #${taskId} ${
|
|
391
|
+
statusText.STATUS_TEXT[statusText.STATES.ENQUEUED]
|
|
392
|
+
}\n`,
|
|
393
|
+
indent: 2,
|
|
408
394
|
});
|
|
409
395
|
}
|
|
410
396
|
|
|
@@ -418,29 +404,25 @@ const makeGetTaskStatus = taskType => {
|
|
|
418
404
|
|
|
419
405
|
if (spinnies.hasActiveSpinners()) {
|
|
420
406
|
subTaskStatus.forEach(subTask => {
|
|
421
|
-
|
|
407
|
+
const subTaskName = subTask[statusText.SUBTASK_NAME_KEY];
|
|
408
|
+
|
|
409
|
+
if (!spinnies.pick(subTaskName)) {
|
|
422
410
|
return;
|
|
423
411
|
}
|
|
424
412
|
|
|
425
|
-
const updatedText = `${chalk.bold(
|
|
426
|
-
|
|
427
|
-
|
|
413
|
+
const updatedText = `${chalk.bold(subTaskName)} #${taskId} ${
|
|
414
|
+
statusText.STATUS_TEXT[subTask.status]
|
|
415
|
+
}\n`;
|
|
428
416
|
|
|
429
417
|
switch (subTask.status) {
|
|
430
418
|
case statusText.STATES.SUCCESS:
|
|
431
|
-
spinnies.succeed(
|
|
432
|
-
text: updatedText,
|
|
433
|
-
});
|
|
419
|
+
spinnies.succeed(subTaskName, { text: updatedText });
|
|
434
420
|
break;
|
|
435
421
|
case statusText.STATES.FAILURE:
|
|
436
|
-
spinnies.fail(
|
|
437
|
-
text: updatedText,
|
|
438
|
-
});
|
|
422
|
+
spinnies.fail(subTaskName, { text: updatedText });
|
|
439
423
|
break;
|
|
440
424
|
default:
|
|
441
|
-
spinnies.update(
|
|
442
|
-
text: updatedText,
|
|
443
|
-
});
|
|
425
|
+
spinnies.update(subTaskName, { text: updatedText });
|
|
444
426
|
break;
|
|
445
427
|
}
|
|
446
428
|
});
|
|
@@ -452,11 +434,11 @@ const makeGetTaskStatus = taskType => {
|
|
|
452
434
|
|
|
453
435
|
if (status === statusText.STATES.SUCCESS) {
|
|
454
436
|
spinnies.succeed('overallTaskStatus', {
|
|
455
|
-
text: statusStrings.SUCCESS(taskName)
|
|
437
|
+
text: `${statusStrings.SUCCESS(taskName)}${hubspotLinkText}`,
|
|
456
438
|
});
|
|
457
439
|
} else if (status === statusText.STATES.FAILURE) {
|
|
458
440
|
spinnies.fail('overallTaskStatus', {
|
|
459
|
-
text: statusStrings.FAIL(taskName)
|
|
441
|
+
text: `${statusStrings.FAIL(taskName)}${hubspotLinkText}`,
|
|
460
442
|
});
|
|
461
443
|
|
|
462
444
|
const failedSubtask = subTaskStatus.filter(
|
|
@@ -466,7 +448,7 @@ const makeGetTaskStatus = taskType => {
|
|
|
466
448
|
uiLine();
|
|
467
449
|
logger.log(
|
|
468
450
|
`${statusStrings.SUBTASK_FAIL(
|
|
469
|
-
|
|
451
|
+
taskId,
|
|
470
452
|
failedSubtask.length === 1
|
|
471
453
|
? failedSubtask[0][statusText.SUBTASK_NAME_KEY]
|
|
472
454
|
: failedSubtask.length + ' components'
|
|
@@ -494,6 +476,40 @@ const makeGetTaskStatus = taskType => {
|
|
|
494
476
|
};
|
|
495
477
|
};
|
|
496
478
|
|
|
479
|
+
const pollBuildStatus = makePollTaskStatusFunc({
|
|
480
|
+
linkToHubSpot: (projectName, buildId, accountId) =>
|
|
481
|
+
uiLink(
|
|
482
|
+
`View build #${buildId} in HubSpot`,
|
|
483
|
+
getProjectBuildDetailUrl(projectName, buildId, accountId),
|
|
484
|
+
{ useColor: true }
|
|
485
|
+
),
|
|
486
|
+
statusFn: getBuildStatus,
|
|
487
|
+
statusText: PROJECT_BUILD_TEXT,
|
|
488
|
+
statusStrings: {
|
|
489
|
+
INITIALIZE: name => `Building ${chalk.bold(name)}`,
|
|
490
|
+
SUCCESS: name => `Built ${chalk.bold(name)}`,
|
|
491
|
+
FAIL: name => `Failed to build ${chalk.bold(name)}`,
|
|
492
|
+
SUBTASK_FAIL: (taskId, name) =>
|
|
493
|
+
`Build #${taskId} failed because there was a problem\nbuilding ${chalk.bold(
|
|
494
|
+
name
|
|
495
|
+
)}`,
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
const pollDeployStatus = makePollTaskStatusFunc({
|
|
500
|
+
statusFn: getDeployStatus,
|
|
501
|
+
statusText: PROJECT_DEPLOY_TEXT,
|
|
502
|
+
statusStrings: {
|
|
503
|
+
INITIALIZE: name => `Deploying ${chalk.bold(name)}`,
|
|
504
|
+
SUCCESS: name => `Deployed ${chalk.bold(name)}`,
|
|
505
|
+
FAIL: name => `Failed to deploy ${chalk.bold(name)}`,
|
|
506
|
+
SUBTASK_FAIL: (taskId, name) =>
|
|
507
|
+
`Deploy for build #${taskId} failed because there was a\nproblem deploying ${chalk.bold(
|
|
508
|
+
name
|
|
509
|
+
)}`,
|
|
510
|
+
},
|
|
511
|
+
});
|
|
512
|
+
|
|
497
513
|
module.exports = {
|
|
498
514
|
writeProjectConfig,
|
|
499
515
|
getProjectConfig,
|
|
@@ -501,9 +517,10 @@ module.exports = {
|
|
|
501
517
|
handleProjectUpload,
|
|
502
518
|
createProjectConfig,
|
|
503
519
|
validateProjectConfig,
|
|
504
|
-
|
|
520
|
+
showProjectWelcomeMessage,
|
|
505
521
|
getProjectDetailUrl,
|
|
506
|
-
|
|
507
|
-
|
|
522
|
+
getProjectBuildDetailUrl,
|
|
523
|
+
pollBuildStatus,
|
|
524
|
+
pollDeployStatus,
|
|
508
525
|
ensureProjectExists,
|
|
509
526
|
};
|
package/lib/serverlessLogs.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const https = require('https');
|
|
2
|
-
const readline = require('readline');
|
|
3
2
|
|
|
4
3
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
5
4
|
const { outputLogs } = require('@hubspot/cli-lib/lib/logs');
|
|
@@ -9,20 +8,12 @@ const {
|
|
|
9
8
|
ApiErrorContext,
|
|
10
9
|
} = require('@hubspot/cli-lib/errorHandlers');
|
|
11
10
|
const { base64EncodeString } = require('@hubspot/cli-lib/lib/encoding');
|
|
11
|
+
const { handleKeypress } = require('@hubspot/cli-lib/lib/process');
|
|
12
|
+
|
|
12
13
|
const { EXIT_CODES } = require('../lib/enums/exitCodes');
|
|
13
14
|
|
|
14
15
|
const TAIL_DELAY = 5000;
|
|
15
16
|
|
|
16
|
-
const handleKeypressToExit = exit => {
|
|
17
|
-
readline.emitKeypressEvents(process.stdin);
|
|
18
|
-
process.stdin.setRawMode(true);
|
|
19
|
-
process.stdin.on('keypress', (str, key) => {
|
|
20
|
-
if (key && ((key.ctrl && key.name == 'c') || key.name === 'escape')) {
|
|
21
|
-
exit(key.name === 'escape' ? 'esc' : 'ctrl+c');
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
|
|
26
17
|
const tailLogs = async ({
|
|
27
18
|
accountId,
|
|
28
19
|
compact,
|
|
@@ -76,12 +67,13 @@ const tailLogs = async ({
|
|
|
76
67
|
}, TAIL_DELAY);
|
|
77
68
|
};
|
|
78
69
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
text: `Stopped polling
|
|
82
|
-
|
|
83
|
-
|
|
70
|
+
handleKeypress(key => {
|
|
71
|
+
if ((key.ctrl && key.name == 'c') || key.name === 'escape') {
|
|
72
|
+
spinnies.succeed('tailLogs', { text: `Stopped polling` });
|
|
73
|
+
process.exit(EXIT_CODES.SUCCESS);
|
|
74
|
+
}
|
|
84
75
|
});
|
|
76
|
+
|
|
85
77
|
await tail(initialAfter);
|
|
86
78
|
};
|
|
87
79
|
|
package/lib/ui.js
CHANGED
|
@@ -22,7 +22,14 @@ const uiLine = () => {
|
|
|
22
22
|
*/
|
|
23
23
|
const uiLink = (linkText, url, options = {}) => {
|
|
24
24
|
if (supportsHyperlinks.stdout) {
|
|
25
|
-
|
|
25
|
+
const result = [
|
|
26
|
+
'\u001B]8;;',
|
|
27
|
+
url,
|
|
28
|
+
'\u0007',
|
|
29
|
+
linkText,
|
|
30
|
+
'\u001B]8;;\u0007',
|
|
31
|
+
].join('');
|
|
32
|
+
return options.useColor ? chalk.cyan(result) : result;
|
|
26
33
|
} else {
|
|
27
34
|
return options.fallback ? `${linkText}: ${url}` : linkText;
|
|
28
35
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "3.0.12
|
|
3
|
+
"version": "3.0.12",
|
|
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
|
|
12
|
-
"@hubspot/serverless-dev-runtime": "^3.0.12
|
|
11
|
+
"@hubspot/cli-lib": "^3.0.12",
|
|
12
|
+
"@hubspot/serverless-dev-runtime": "^3.0.12",
|
|
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": "837c419fdb8dec12ad25c5c2965b9174409a0ee2"
|
|
43
43
|
}
|