@hubspot/cli 3.0.9-beta.1 → 3.0.10-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/commands/logs.js +21 -94
- package/commands/marketplaceValidate/validateTheme.js +1 -1
- package/commands/project/deploy.js +41 -11
- package/commands/project/logs.js +196 -0
- package/commands/project/upload.js +86 -13
- package/commands/project.js +2 -0
- package/lib/__tests__/serverlessLogs.js +8 -9
- package/lib/links.js +10 -0
- package/lib/projects.js +163 -81
- package/lib/serverlessLogs.js +7 -9
- package/package.json +5 -4
package/commands/logs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const Spinnies = require('spinnies');
|
|
2
2
|
const {
|
|
3
3
|
addAccountOptions,
|
|
4
4
|
addConfigOptions,
|
|
@@ -14,16 +14,7 @@ const {
|
|
|
14
14
|
checkAndWarnGitInclusion,
|
|
15
15
|
} = require('@hubspot/cli-lib');
|
|
16
16
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
17
|
-
const {
|
|
18
|
-
logServerlessFunctionApiErrorInstance,
|
|
19
|
-
ApiErrorContext,
|
|
20
|
-
} = require('@hubspot/cli-lib/errorHandlers');
|
|
21
17
|
const { outputLogs } = require('@hubspot/cli-lib/lib/logs');
|
|
22
|
-
const {
|
|
23
|
-
getFunctionByPath,
|
|
24
|
-
getAppFunctionLogs,
|
|
25
|
-
getLatestAppFunctionLogs,
|
|
26
|
-
} = require('@hubspot/cli-lib/api/functions');
|
|
27
18
|
const {
|
|
28
19
|
getFunctionLogs,
|
|
29
20
|
getLatestFunctionLog,
|
|
@@ -43,9 +34,11 @@ const loadAndValidateOptions = async options => {
|
|
|
43
34
|
}
|
|
44
35
|
};
|
|
45
36
|
|
|
46
|
-
const
|
|
37
|
+
const handleLogsError = (e, accountId, functionPath) => {
|
|
47
38
|
if (e.statusCode === 404) {
|
|
48
|
-
logger.
|
|
39
|
+
logger.error(
|
|
40
|
+
`No logs were found for the function path '${functionPath}' in account ${accountId}.`
|
|
41
|
+
);
|
|
49
42
|
}
|
|
50
43
|
};
|
|
51
44
|
|
|
@@ -58,95 +51,43 @@ const endpointLog = async (accountId, options) => {
|
|
|
58
51
|
}logs for function with path: ${functionPath}`
|
|
59
52
|
);
|
|
60
53
|
|
|
61
|
-
const functionResp = await getFunctionByPath(accountId, functionPath).catch(
|
|
62
|
-
async e => {
|
|
63
|
-
await logServerlessFunctionApiErrorInstance(
|
|
64
|
-
accountId,
|
|
65
|
-
e,
|
|
66
|
-
new ApiErrorContext({ accountId, functionPath })
|
|
67
|
-
);
|
|
68
|
-
process.exit();
|
|
69
|
-
}
|
|
70
|
-
);
|
|
71
|
-
const functionId = functionResp.id;
|
|
72
|
-
|
|
73
|
-
logger.debug(`Retrieving logs for functionId: ${functionResp.id}`);
|
|
74
|
-
|
|
75
54
|
let logsResp;
|
|
76
55
|
|
|
77
56
|
if (follow) {
|
|
78
|
-
const
|
|
79
|
-
`Waiting for log entries for '${functionPath}' on account '${accountId}'.\n`
|
|
80
|
-
);
|
|
81
|
-
const tailCall = after => getFunctionLogs(accountId, functionId, { after });
|
|
82
|
-
const fetchLatest = () => {
|
|
83
|
-
try {
|
|
84
|
-
getLatestFunctionLog(accountId, functionId);
|
|
85
|
-
} catch (e) {
|
|
86
|
-
handleLatestLogsError(e);
|
|
87
|
-
}
|
|
88
|
-
};
|
|
57
|
+
const spinnies = new Spinnies();
|
|
89
58
|
|
|
90
|
-
|
|
91
|
-
accountId
|
|
92
|
-
compact,
|
|
93
|
-
spinner,
|
|
94
|
-
tailCall,
|
|
95
|
-
fetchLatest,
|
|
59
|
+
spinnies.add('tailLogs', {
|
|
60
|
+
text: `Waiting for log entries for '${functionPath}' on account '${accountId}'.\n`,
|
|
96
61
|
});
|
|
97
|
-
} else if (latest) {
|
|
98
|
-
try {
|
|
99
|
-
logsResp = await getLatestFunctionLog(accountId, functionResp.id);
|
|
100
|
-
} catch (e) {
|
|
101
|
-
handleLatestLogsError(e);
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
logsResp = await getFunctionLogs(accountId, functionResp.id, options);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (logsResp) {
|
|
108
|
-
return outputLogs(logsResp, options);
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const appFunctionLog = async (accountId, options) => {
|
|
113
|
-
const { latest, follow, compact, functionName, appPath } = options;
|
|
114
|
-
|
|
115
|
-
let logsResp;
|
|
116
|
-
|
|
117
|
-
if (follow) {
|
|
118
|
-
const spinner = ora(
|
|
119
|
-
`Waiting for log entries for "${functionName}" on account "${accountId}".\n`
|
|
120
|
-
);
|
|
121
62
|
const tailCall = after =>
|
|
122
|
-
|
|
63
|
+
getFunctionLogs(accountId, functionPath, { after });
|
|
123
64
|
const fetchLatest = () => {
|
|
124
65
|
try {
|
|
125
|
-
|
|
66
|
+
getLatestFunctionLog(accountId, functionPath);
|
|
126
67
|
} catch (e) {
|
|
127
|
-
|
|
68
|
+
handleLogsError(e, accountId, functionPath);
|
|
128
69
|
}
|
|
129
70
|
};
|
|
130
71
|
|
|
131
72
|
await tailLogs({
|
|
132
73
|
accountId,
|
|
133
74
|
compact,
|
|
134
|
-
|
|
75
|
+
spinnies,
|
|
135
76
|
tailCall,
|
|
136
77
|
fetchLatest,
|
|
137
78
|
});
|
|
138
79
|
} else if (latest) {
|
|
139
80
|
try {
|
|
140
|
-
logsResp = await
|
|
141
|
-
accountId,
|
|
142
|
-
functionName,
|
|
143
|
-
appPath
|
|
144
|
-
);
|
|
81
|
+
logsResp = await getLatestFunctionLog(accountId, functionPath);
|
|
145
82
|
} catch (e) {
|
|
146
|
-
|
|
83
|
+
handleLogsError(e, accountId, functionPath);
|
|
147
84
|
}
|
|
148
85
|
} else {
|
|
149
|
-
|
|
86
|
+
try {
|
|
87
|
+
logsResp = await getFunctionLogs(accountId, functionPath, options);
|
|
88
|
+
} catch (e) {
|
|
89
|
+
handleLogsError(e, accountId, functionPath);
|
|
90
|
+
}
|
|
150
91
|
}
|
|
151
92
|
|
|
152
93
|
if (logsResp) {
|
|
@@ -160,17 +101,13 @@ exports.describe = 'get logs for a function';
|
|
|
160
101
|
exports.handler = async options => {
|
|
161
102
|
loadAndValidateOptions(options);
|
|
162
103
|
|
|
163
|
-
const { latest
|
|
104
|
+
const { latest } = options;
|
|
164
105
|
|
|
165
106
|
const accountId = getAccountId(options);
|
|
166
107
|
|
|
167
108
|
trackCommandUsage('logs', { latest }, accountId);
|
|
168
109
|
|
|
169
|
-
|
|
170
|
-
appFunctionLog(accountId, options);
|
|
171
|
-
} else {
|
|
172
|
-
endpointLog(accountId, options);
|
|
173
|
-
}
|
|
110
|
+
endpointLog(accountId, options);
|
|
174
111
|
};
|
|
175
112
|
|
|
176
113
|
exports.builder = yargs => {
|
|
@@ -180,16 +117,6 @@ exports.builder = yargs => {
|
|
|
180
117
|
});
|
|
181
118
|
yargs
|
|
182
119
|
.options({
|
|
183
|
-
appPath: {
|
|
184
|
-
describe: 'path to the app',
|
|
185
|
-
type: 'string',
|
|
186
|
-
hidden: true,
|
|
187
|
-
},
|
|
188
|
-
functionName: {
|
|
189
|
-
describe: 'app function name',
|
|
190
|
-
type: 'string',
|
|
191
|
-
hidden: true,
|
|
192
|
-
},
|
|
193
120
|
latest: {
|
|
194
121
|
alias: 'l',
|
|
195
122
|
describe: 'retrieve most recent log only',
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const path = require('path');
|
|
1
2
|
const {
|
|
2
3
|
addAccountOptions,
|
|
3
4
|
addConfigOptions,
|
|
@@ -17,8 +18,13 @@ const {
|
|
|
17
18
|
ApiErrorContext,
|
|
18
19
|
} = require('@hubspot/cli-lib/errorHandlers');
|
|
19
20
|
const { logger } = require('@hubspot/cli-lib/logger');
|
|
20
|
-
const { deployProject } = require('@hubspot/cli-lib/api/
|
|
21
|
+
const { deployProject, fetchProject } = require('@hubspot/cli-lib/api/dfs');
|
|
22
|
+
const { getCwd } = require('@hubspot/cli-lib/path');
|
|
21
23
|
const { validateAccount } = require('../../lib/validation');
|
|
24
|
+
const {
|
|
25
|
+
getOrCreateProjectConfig,
|
|
26
|
+
pollDeployStatus,
|
|
27
|
+
} = require('../../lib/projects');
|
|
22
28
|
|
|
23
29
|
const loadAndValidateOptions = async options => {
|
|
24
30
|
setLogLevel(options);
|
|
@@ -32,39 +38,56 @@ const loadAndValidateOptions = async options => {
|
|
|
32
38
|
}
|
|
33
39
|
};
|
|
34
40
|
|
|
35
|
-
exports.command = 'deploy
|
|
41
|
+
exports.command = 'deploy [path]';
|
|
36
42
|
exports.describe = false;
|
|
37
43
|
|
|
38
44
|
exports.handler = async options => {
|
|
39
45
|
loadAndValidateOptions(options);
|
|
40
46
|
|
|
41
|
-
const { path: projectPath } = options;
|
|
47
|
+
const { path: projectPath, buildId } = options;
|
|
42
48
|
const accountId = getAccountId(options);
|
|
43
49
|
|
|
44
50
|
trackCommandUsage('project-deploy', { projectPath }, accountId);
|
|
45
51
|
|
|
52
|
+
const cwd = projectPath ? path.resolve(getCwd(), projectPath) : getCwd();
|
|
53
|
+
const projectConfig = await getOrCreateProjectConfig(cwd);
|
|
54
|
+
|
|
46
55
|
logger.debug(`Deploying project at path: ${projectPath}`);
|
|
47
56
|
|
|
57
|
+
const getBuildId = async () => {
|
|
58
|
+
const { latestBuild } = await fetchProject(accountId, projectConfig.name);
|
|
59
|
+
if (latestBuild && latestBuild.buildId) {
|
|
60
|
+
return latestBuild.buildId;
|
|
61
|
+
}
|
|
62
|
+
logger.error('No latest build ID was found.');
|
|
63
|
+
return;
|
|
64
|
+
};
|
|
65
|
+
|
|
48
66
|
try {
|
|
49
|
-
const
|
|
67
|
+
const deployedBuildId = buildId || (await getBuildId());
|
|
68
|
+
|
|
69
|
+
const deployResp = await deployProject(
|
|
70
|
+
accountId,
|
|
71
|
+
projectConfig.name,
|
|
72
|
+
deployedBuildId
|
|
73
|
+
);
|
|
50
74
|
|
|
51
75
|
if (deployResp.error) {
|
|
52
76
|
logger.error(`Deploy error: ${deployResp.error.message}`);
|
|
53
77
|
return;
|
|
54
78
|
}
|
|
55
79
|
|
|
56
|
-
|
|
57
|
-
|
|
80
|
+
await pollDeployStatus(
|
|
81
|
+
accountId,
|
|
82
|
+
projectConfig.name,
|
|
83
|
+
deployResp.id,
|
|
84
|
+
deployedBuildId
|
|
58
85
|
);
|
|
59
86
|
} catch (e) {
|
|
60
87
|
if (e.statusCode === 400) {
|
|
61
88
|
logger.error(e.error.message);
|
|
62
89
|
} else {
|
|
63
|
-
logApiErrorInstance(
|
|
64
|
-
accountId,
|
|
65
|
-
e,
|
|
66
|
-
new ApiErrorContext({ accountId, projectPath })
|
|
67
|
-
);
|
|
90
|
+
logApiErrorInstance(e, new ApiErrorContext({ accountId, projectPath }));
|
|
68
91
|
}
|
|
69
92
|
}
|
|
70
93
|
};
|
|
@@ -75,6 +98,13 @@ exports.builder = yargs => {
|
|
|
75
98
|
type: 'string',
|
|
76
99
|
});
|
|
77
100
|
|
|
101
|
+
yargs.options({
|
|
102
|
+
buildId: {
|
|
103
|
+
describe: 'Project build ID to be deployed',
|
|
104
|
+
type: 'number',
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
78
108
|
yargs.example([
|
|
79
109
|
[
|
|
80
110
|
'$0 project deploy myProjectFolder',
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
const Spinnies = require('spinnies');
|
|
2
|
+
const {
|
|
3
|
+
addAccountOptions,
|
|
4
|
+
addConfigOptions,
|
|
5
|
+
setLogLevel,
|
|
6
|
+
getAccountId,
|
|
7
|
+
addUseEnvironmentOptions,
|
|
8
|
+
} = require('../../lib/commonOpts');
|
|
9
|
+
const { trackCommandUsage } = require('../../lib/usageTracking');
|
|
10
|
+
const { logDebugInfo } = require('../../lib/debugInfo');
|
|
11
|
+
const {
|
|
12
|
+
loadConfig,
|
|
13
|
+
validateConfig,
|
|
14
|
+
checkAndWarnGitInclusion,
|
|
15
|
+
} = require('@hubspot/cli-lib');
|
|
16
|
+
const { logger } = require('@hubspot/cli-lib/logger');
|
|
17
|
+
const { outputLogs } = require('@hubspot/cli-lib/lib/logs');
|
|
18
|
+
const {
|
|
19
|
+
getProjectAppFunctionLogs,
|
|
20
|
+
getLatestProjectAppFunctionLog,
|
|
21
|
+
} = require('@hubspot/cli-lib/api/functions');
|
|
22
|
+
const { validateAccount } = require('../../lib/validation');
|
|
23
|
+
const { tailLogs } = require('../../lib/serverlessLogs');
|
|
24
|
+
|
|
25
|
+
const loadAndValidateOptions = async options => {
|
|
26
|
+
setLogLevel(options);
|
|
27
|
+
logDebugInfo(options);
|
|
28
|
+
const { config: configPath } = options;
|
|
29
|
+
loadConfig(configPath, options);
|
|
30
|
+
checkAndWarnGitInclusion();
|
|
31
|
+
|
|
32
|
+
if (!(validateConfig() && (await validateAccount(options)))) {
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handleLogsError = (e, accountId, projectName, appPath, functionName) => {
|
|
38
|
+
if (e.statusCode === 404) {
|
|
39
|
+
logger.error(
|
|
40
|
+
`No logs were found for the function name '${functionName}' in the app path '${appPath}' within the project '${projectName}' in account ${accountId}.`
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const appFunctionLog = async (accountId, options) => {
|
|
46
|
+
const {
|
|
47
|
+
latest,
|
|
48
|
+
follow,
|
|
49
|
+
compact,
|
|
50
|
+
appPath,
|
|
51
|
+
functionName,
|
|
52
|
+
projectName,
|
|
53
|
+
} = options;
|
|
54
|
+
|
|
55
|
+
let logsResp;
|
|
56
|
+
|
|
57
|
+
if (follow) {
|
|
58
|
+
const spinnies = new Spinnies();
|
|
59
|
+
|
|
60
|
+
spinnies.add('tailLogs', {
|
|
61
|
+
text: `Waiting for log entries for '${functionName}' on account '${accountId}'.\n`,
|
|
62
|
+
});
|
|
63
|
+
const tailCall = after =>
|
|
64
|
+
getProjectAppFunctionLogs(accountId, functionName, projectName, appPath, {
|
|
65
|
+
after,
|
|
66
|
+
});
|
|
67
|
+
const fetchLatest = () => {
|
|
68
|
+
try {
|
|
69
|
+
return getLatestProjectAppFunctionLog(
|
|
70
|
+
accountId,
|
|
71
|
+
functionName,
|
|
72
|
+
projectName,
|
|
73
|
+
appPath
|
|
74
|
+
);
|
|
75
|
+
} catch (e) {
|
|
76
|
+
handleLogsError(e, accountId, projectName, appPath, functionName);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
await tailLogs({
|
|
81
|
+
accountId,
|
|
82
|
+
compact,
|
|
83
|
+
spinnies,
|
|
84
|
+
tailCall,
|
|
85
|
+
fetchLatest,
|
|
86
|
+
});
|
|
87
|
+
} else if (latest) {
|
|
88
|
+
try {
|
|
89
|
+
logsResp = await getLatestProjectAppFunctionLog(
|
|
90
|
+
accountId,
|
|
91
|
+
functionName,
|
|
92
|
+
projectName,
|
|
93
|
+
appPath
|
|
94
|
+
);
|
|
95
|
+
} catch (e) {
|
|
96
|
+
handleLogsError(e, accountId, projectName, appPath, functionName);
|
|
97
|
+
}
|
|
98
|
+
} else {
|
|
99
|
+
try {
|
|
100
|
+
logsResp = await getProjectAppFunctionLogs(
|
|
101
|
+
accountId,
|
|
102
|
+
functionName,
|
|
103
|
+
projectName,
|
|
104
|
+
appPath,
|
|
105
|
+
{}
|
|
106
|
+
);
|
|
107
|
+
} catch (e) {
|
|
108
|
+
handleLogsError(e, accountId, projectName, appPath, functionName);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (logsResp) {
|
|
113
|
+
return outputLogs(logsResp, options);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
exports.command = 'logs [functionName]';
|
|
118
|
+
exports.describe = 'get logs for a function within a project';
|
|
119
|
+
|
|
120
|
+
exports.handler = async options => {
|
|
121
|
+
loadAndValidateOptions(options);
|
|
122
|
+
|
|
123
|
+
const { latest, functionName, projectName, appPath } = options;
|
|
124
|
+
|
|
125
|
+
if (!functionName) {
|
|
126
|
+
logger.error('You must pass a function name to retrieve logs for.');
|
|
127
|
+
process.exit(0);
|
|
128
|
+
} else if (!projectName) {
|
|
129
|
+
logger.error(
|
|
130
|
+
'You must specify a project name using the --projectName argument.'
|
|
131
|
+
);
|
|
132
|
+
process.exit(0);
|
|
133
|
+
} else if (!appPath) {
|
|
134
|
+
logger.error('You must specify an app path using the --appPath argument.');
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const accountId = getAccountId(options);
|
|
139
|
+
|
|
140
|
+
trackCommandUsage('project-logs', { latest }, accountId);
|
|
141
|
+
|
|
142
|
+
appFunctionLog(accountId, options);
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
exports.builder = yargs => {
|
|
146
|
+
yargs.positional('functionName', {
|
|
147
|
+
describe: 'Serverless function name',
|
|
148
|
+
type: 'string',
|
|
149
|
+
});
|
|
150
|
+
yargs
|
|
151
|
+
.options({
|
|
152
|
+
appPath: {
|
|
153
|
+
describe: 'path to the app',
|
|
154
|
+
type: 'string',
|
|
155
|
+
hidden: true,
|
|
156
|
+
},
|
|
157
|
+
projectName: {
|
|
158
|
+
describe: 'name of the project',
|
|
159
|
+
type: 'string',
|
|
160
|
+
hidden: true,
|
|
161
|
+
},
|
|
162
|
+
latest: {
|
|
163
|
+
alias: 'l',
|
|
164
|
+
describe: 'retrieve most recent log only',
|
|
165
|
+
type: 'boolean',
|
|
166
|
+
},
|
|
167
|
+
compact: {
|
|
168
|
+
describe: 'output compact logs',
|
|
169
|
+
type: 'boolean',
|
|
170
|
+
},
|
|
171
|
+
follow: {
|
|
172
|
+
alias: ['t', 'tail', 'f'],
|
|
173
|
+
describe: 'follow logs',
|
|
174
|
+
type: 'boolean',
|
|
175
|
+
},
|
|
176
|
+
limit: {
|
|
177
|
+
alias: ['limit', 'n', 'max-count'],
|
|
178
|
+
describe: 'limit the number of logs to output',
|
|
179
|
+
type: 'number',
|
|
180
|
+
},
|
|
181
|
+
})
|
|
182
|
+
.conflicts('follow', 'limit');
|
|
183
|
+
|
|
184
|
+
yargs.example([
|
|
185
|
+
[
|
|
186
|
+
'$0 project logs my-function --appName="app" --projectName="my-project"',
|
|
187
|
+
'Get 5 most recent logs for function named "my-function" within the app named "app" within the project named "my-project"',
|
|
188
|
+
],
|
|
189
|
+
]);
|
|
190
|
+
|
|
191
|
+
addConfigOptions(yargs, true);
|
|
192
|
+
addAccountOptions(yargs, true);
|
|
193
|
+
addUseEnvironmentOptions(yargs, true);
|
|
194
|
+
|
|
195
|
+
return yargs;
|
|
196
|
+
};
|
|
@@ -31,6 +31,8 @@ const {
|
|
|
31
31
|
getProjectConfig,
|
|
32
32
|
validateProjectConfig,
|
|
33
33
|
pollBuildStatus,
|
|
34
|
+
ensureProjectExists,
|
|
35
|
+
pollDeployStatus,
|
|
34
36
|
} = require('../../lib/projects');
|
|
35
37
|
|
|
36
38
|
const loadAndValidateOptions = async options => {
|
|
@@ -49,7 +51,9 @@ exports.command = 'upload [path]';
|
|
|
49
51
|
exports.describe = false;
|
|
50
52
|
|
|
51
53
|
const uploadProjectFiles = async (accountId, projectName, filePath) => {
|
|
52
|
-
const spinnies = new Spinnies(
|
|
54
|
+
const spinnies = new Spinnies({
|
|
55
|
+
succeedColor: 'white',
|
|
56
|
+
});
|
|
53
57
|
|
|
54
58
|
spinnies.add('upload', {
|
|
55
59
|
text: `Uploading ${chalk.bold(projectName)} project files to ${chalk.bold(
|
|
@@ -57,9 +61,13 @@ const uploadProjectFiles = async (accountId, projectName, filePath) => {
|
|
|
57
61
|
)}`,
|
|
58
62
|
});
|
|
59
63
|
|
|
64
|
+
let buildId;
|
|
65
|
+
|
|
60
66
|
try {
|
|
61
67
|
const upload = await uploadProject(accountId, projectName, filePath);
|
|
62
68
|
|
|
69
|
+
buildId = upload.buildId;
|
|
70
|
+
|
|
63
71
|
spinnies.succeed('upload', {
|
|
64
72
|
text: `Uploaded ${chalk.bold(projectName)} project files to ${chalk.bold(
|
|
65
73
|
accountId
|
|
@@ -67,9 +75,8 @@ const uploadProjectFiles = async (accountId, projectName, filePath) => {
|
|
|
67
75
|
});
|
|
68
76
|
|
|
69
77
|
logger.debug(
|
|
70
|
-
`Project "${projectName}" uploaded and build #${
|
|
78
|
+
`Project "${projectName}" uploaded and build #${buildId} created`
|
|
71
79
|
);
|
|
72
|
-
await pollBuildStatus(accountId, projectName, upload.buildId);
|
|
73
80
|
} catch (err) {
|
|
74
81
|
if (err.statusCode === 404) {
|
|
75
82
|
return logger.error(
|
|
@@ -83,13 +90,16 @@ const uploadProjectFiles = async (accountId, projectName, filePath) => {
|
|
|
83
90
|
)} project files to ${chalk.bold(accountId)}`,
|
|
84
91
|
});
|
|
85
92
|
|
|
86
|
-
logApiErrorInstance(
|
|
87
|
-
|
|
93
|
+
logApiErrorInstance(
|
|
94
|
+
err,
|
|
95
|
+
new ApiErrorContext({
|
|
88
96
|
accountId,
|
|
89
97
|
projectName,
|
|
90
|
-
})
|
|
91
|
-
|
|
98
|
+
})
|
|
99
|
+
);
|
|
92
100
|
}
|
|
101
|
+
|
|
102
|
+
return { buildId };
|
|
93
103
|
};
|
|
94
104
|
|
|
95
105
|
exports.handler = async options => {
|
|
@@ -100,10 +110,14 @@ exports.handler = async options => {
|
|
|
100
110
|
|
|
101
111
|
trackCommandUsage('project-upload', { projectPath }, accountId);
|
|
102
112
|
|
|
103
|
-
const
|
|
104
|
-
|
|
113
|
+
const projectDir = projectPath
|
|
114
|
+
? path.resolve(getCwd(), projectPath)
|
|
115
|
+
: getCwd();
|
|
116
|
+
const projectConfig = await getProjectConfig(projectDir);
|
|
105
117
|
|
|
106
|
-
validateProjectConfig(projectConfig);
|
|
118
|
+
validateProjectConfig(projectConfig, projectDir);
|
|
119
|
+
|
|
120
|
+
await ensureProjectExists(accountId, projectConfig.name);
|
|
107
121
|
|
|
108
122
|
const tempFile = tmp.fileSync({ postfix: '.zip' });
|
|
109
123
|
|
|
@@ -115,7 +129,64 @@ exports.handler = async options => {
|
|
|
115
129
|
output.on('close', async function() {
|
|
116
130
|
logger.debug(`Project files compressed: ${archive.pointer()} bytes`);
|
|
117
131
|
|
|
118
|
-
await uploadProjectFiles(
|
|
132
|
+
const { buildId } = await uploadProjectFiles(
|
|
133
|
+
accountId,
|
|
134
|
+
projectConfig.name,
|
|
135
|
+
tempFile.name
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
const {
|
|
139
|
+
isAutoDeployEnabled,
|
|
140
|
+
deployStatusTaskLocator,
|
|
141
|
+
status,
|
|
142
|
+
subbuildStatuses,
|
|
143
|
+
} = await pollBuildStatus(accountId, projectConfig.name, buildId);
|
|
144
|
+
|
|
145
|
+
if (status === 'FAILURE') {
|
|
146
|
+
const failedSubbuilds = subbuildStatuses.filter(
|
|
147
|
+
subbuild => subbuild.status === 'FAILURE'
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
logger.log('-'.repeat(50));
|
|
151
|
+
logger.log(
|
|
152
|
+
`Build #${buildId} failed because there was a problem\nbuilding ${
|
|
153
|
+
failedSubbuilds.length === 1
|
|
154
|
+
? failedSubbuilds[0].buildName
|
|
155
|
+
: failedSubbuilds.length + ' components'
|
|
156
|
+
}\n`
|
|
157
|
+
);
|
|
158
|
+
logger.log('See below for a summary of errors.');
|
|
159
|
+
logger.log('-'.repeat(50));
|
|
160
|
+
|
|
161
|
+
failedSubbuilds.forEach(subbuild => {
|
|
162
|
+
logger.log(
|
|
163
|
+
`\n--- ${subbuild.buildName} failed to build with the following error ---`
|
|
164
|
+
);
|
|
165
|
+
logger.error(subbuild.errorMessage);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (isAutoDeployEnabled && deployStatusTaskLocator) {
|
|
172
|
+
logger.log(
|
|
173
|
+
`Build #${buildId} succeeded. ${chalk.bold(
|
|
174
|
+
'Automatically deploying'
|
|
175
|
+
)} to ${accountId}`
|
|
176
|
+
);
|
|
177
|
+
await pollDeployStatus(
|
|
178
|
+
accountId,
|
|
179
|
+
projectConfig.name,
|
|
180
|
+
deployStatusTaskLocator.id,
|
|
181
|
+
buildId
|
|
182
|
+
);
|
|
183
|
+
} else {
|
|
184
|
+
logger.log('-'.repeat(50));
|
|
185
|
+
logger.log(chalk.bold(`Build #${buildId} succeeded\n`));
|
|
186
|
+
logger.log('🚀 Ready to take your project live?');
|
|
187
|
+
logger.log(`Run \`${chalk.hex('f5c26b')('hs project deploy')}\``);
|
|
188
|
+
logger.log('-'.repeat(50));
|
|
189
|
+
}
|
|
119
190
|
|
|
120
191
|
try {
|
|
121
192
|
tempFile.removeCallback();
|
|
@@ -131,8 +202,10 @@ exports.handler = async options => {
|
|
|
131
202
|
|
|
132
203
|
archive.pipe(output);
|
|
133
204
|
|
|
134
|
-
archive.directory(
|
|
135
|
-
|
|
205
|
+
archive.directory(
|
|
206
|
+
path.resolve(projectDir, projectConfig.srcDir),
|
|
207
|
+
false,
|
|
208
|
+
file => (shouldIgnoreFile(file.name) ? false : file)
|
|
136
209
|
);
|
|
137
210
|
|
|
138
211
|
archive.finalize();
|
package/commands/project.js
CHANGED
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
const deploy = require('./project/deploy');
|
|
7
7
|
const init = require('./project/init');
|
|
8
8
|
const upload = require('./project/upload');
|
|
9
|
+
const logs = require('./project/logs');
|
|
9
10
|
|
|
10
11
|
exports.command = 'project';
|
|
11
12
|
exports.describe = false; //'Commands for working with projects';
|
|
@@ -19,6 +20,7 @@ exports.builder = yargs => {
|
|
|
19
20
|
yargs.command(deploy).demandCommand(1, '');
|
|
20
21
|
yargs.command(init).demandCommand(0, '');
|
|
21
22
|
yargs.command(upload).demandCommand(0, '');
|
|
23
|
+
yargs.command(logs).demandCommand(1, '');
|
|
22
24
|
|
|
23
25
|
return yargs;
|
|
24
26
|
};
|
|
@@ -11,15 +11,15 @@ const ACCOUNT_ID = 123;
|
|
|
11
11
|
describe('@hubspot/cli/lib/serverlessLogs', () => {
|
|
12
12
|
describe('tailLogs()', () => {
|
|
13
13
|
let stdinMock;
|
|
14
|
-
let
|
|
14
|
+
let spinnies;
|
|
15
15
|
|
|
16
16
|
beforeEach(() => {
|
|
17
17
|
jest.spyOn(process, 'exit').mockImplementation(() => {});
|
|
18
18
|
stdinMock = mockStdIn.stdin();
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
spinnies = {
|
|
20
|
+
succeed: jest.fn(),
|
|
21
|
+
fail: jest.fn(),
|
|
22
|
+
stopAll: jest.fn(),
|
|
23
23
|
};
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -56,7 +56,7 @@ describe('@hubspot/cli/lib/serverlessLogs', () => {
|
|
|
56
56
|
await tailLogs({
|
|
57
57
|
accountId: ACCOUNT_ID,
|
|
58
58
|
compact,
|
|
59
|
-
|
|
59
|
+
spinnies,
|
|
60
60
|
fetchLatest,
|
|
61
61
|
tailCall,
|
|
62
62
|
});
|
|
@@ -114,7 +114,7 @@ describe('@hubspot/cli/lib/serverlessLogs', () => {
|
|
|
114
114
|
await tailLogs({
|
|
115
115
|
accountId: ACCOUNT_ID,
|
|
116
116
|
compact,
|
|
117
|
-
|
|
117
|
+
spinnies,
|
|
118
118
|
fetchLatest,
|
|
119
119
|
tailCall,
|
|
120
120
|
});
|
|
@@ -123,7 +123,6 @@ describe('@hubspot/cli/lib/serverlessLogs', () => {
|
|
|
123
123
|
latestLogResponse,
|
|
124
124
|
expect.objectContaining({ compact })
|
|
125
125
|
);
|
|
126
|
-
expect(spinner.clear).toHaveBeenCalled();
|
|
127
126
|
expect(tailCall).toHaveBeenCalledTimes(2);
|
|
128
127
|
});
|
|
129
128
|
it('handles no logs', async () => {
|
|
@@ -147,7 +146,7 @@ describe('@hubspot/cli/lib/serverlessLogs', () => {
|
|
|
147
146
|
await tailLogs({
|
|
148
147
|
accountId: ACCOUNT_ID,
|
|
149
148
|
compact,
|
|
150
|
-
|
|
149
|
+
spinnies,
|
|
151
150
|
fetchLatest,
|
|
152
151
|
tailCall,
|
|
153
152
|
});
|
package/lib/links.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const supportsHyperlinks = require('supports-hyperlinks');
|
|
1
2
|
const { getEnv } = require('@hubspot/cli-lib/lib/config');
|
|
2
3
|
const { ENVIRONMENTS } = require('@hubspot/cli-lib/lib/constants');
|
|
3
4
|
const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
@@ -120,9 +121,18 @@ const openLink = (accountId, shortcut) => {
|
|
|
120
121
|
logger.success(`We opened ${match.url} in your browser`);
|
|
121
122
|
};
|
|
122
123
|
|
|
124
|
+
const link = (linkText, url) => {
|
|
125
|
+
if (supportsHyperlinks.stdout) {
|
|
126
|
+
return ['\u001B]8;;', url, '\u0007', linkText, '\u001B]8;;\u0007'].join('');
|
|
127
|
+
} else {
|
|
128
|
+
return `${linkText}: ${url}`;
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
|
|
123
132
|
module.exports = {
|
|
124
133
|
getSiteLinks,
|
|
125
134
|
getSiteLinksAsArray,
|
|
126
135
|
logSiteLinks,
|
|
127
136
|
openLink,
|
|
137
|
+
link,
|
|
128
138
|
};
|
package/lib/projects.js
CHANGED
|
@@ -11,15 +11,43 @@ const { getHubSpotWebsiteOrigin } = require('@hubspot/cli-lib/lib/urls');
|
|
|
11
11
|
const {
|
|
12
12
|
ENVIRONMENTS,
|
|
13
13
|
POLLING_DELAY,
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
PROJECT_OVERALL_STATUS,
|
|
15
|
+
PROJECT_TEXT,
|
|
16
16
|
} = require('@hubspot/cli-lib/lib/constants');
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
getBuildStatus,
|
|
19
|
+
getDeployStatus,
|
|
20
|
+
fetchProject,
|
|
21
|
+
createProject,
|
|
22
|
+
} = require('@hubspot/cli-lib/api/dfs');
|
|
23
|
+
const {
|
|
24
|
+
logApiErrorInstance,
|
|
25
|
+
ApiErrorContext,
|
|
26
|
+
} = require('@hubspot/cli-lib/errorHandlers');
|
|
18
27
|
|
|
19
|
-
const
|
|
28
|
+
const PROJECT_STRINGS = {
|
|
29
|
+
BUILD: {
|
|
30
|
+
INITIALIZE: (name, numOfComponents) =>
|
|
31
|
+
`Building ${chalk.bold(
|
|
32
|
+
name
|
|
33
|
+
)}\n\nFound ${numOfComponents} components in this project ...\n`,
|
|
34
|
+
SUCCESS: name => `Built ${chalk.bold(name)}`,
|
|
35
|
+
FAIL: name => `Failed to build ${chalk.bold(name)}`,
|
|
36
|
+
},
|
|
37
|
+
DEPLOY: {
|
|
38
|
+
INITIALIZE: (name, numOfComponents) =>
|
|
39
|
+
`Deploying ${chalk.bold(
|
|
40
|
+
name
|
|
41
|
+
)}\n\nFound ${numOfComponents} components in this project ...\n`,
|
|
42
|
+
SUCCESS: name => `Deployed ${chalk.bold(name)}`,
|
|
43
|
+
FAIL: name => `Failed to deploy ${chalk.bold(name)}`,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const isTaskComplete = task => {
|
|
20
48
|
return (
|
|
21
|
-
|
|
22
|
-
|
|
49
|
+
task.status === PROJECT_OVERALL_STATUS.SUCCESS ||
|
|
50
|
+
task.status === PROJECT_OVERALL_STATUS.FAILURE
|
|
23
51
|
);
|
|
24
52
|
};
|
|
25
53
|
|
|
@@ -86,7 +114,7 @@ const getOrCreateProjectConfig = async projectPath => {
|
|
|
86
114
|
return projectConfig;
|
|
87
115
|
};
|
|
88
116
|
|
|
89
|
-
const validateProjectConfig = projectConfig => {
|
|
117
|
+
const validateProjectConfig = (projectConfig, projectDir) => {
|
|
90
118
|
if (!projectConfig) {
|
|
91
119
|
logger.error(
|
|
92
120
|
`Project config not found. Try running 'hs project init' first.`
|
|
@@ -101,7 +129,7 @@ const validateProjectConfig = projectConfig => {
|
|
|
101
129
|
process.exit(1);
|
|
102
130
|
}
|
|
103
131
|
|
|
104
|
-
if (!fs.existsSync(projectConfig.srcDir)) {
|
|
132
|
+
if (!fs.existsSync(path.resolve(projectDir, projectConfig.srcDir))) {
|
|
105
133
|
logger.error(
|
|
106
134
|
`Project source directory '${projectConfig.srcDir}' does not exist.`
|
|
107
135
|
);
|
|
@@ -109,6 +137,37 @@ const validateProjectConfig = projectConfig => {
|
|
|
109
137
|
}
|
|
110
138
|
};
|
|
111
139
|
|
|
140
|
+
const ensureProjectExists = async (accountId, projectName) => {
|
|
141
|
+
try {
|
|
142
|
+
await fetchProject(accountId, projectName);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (err.statusCode === 404) {
|
|
145
|
+
const { shouldCreateProject } = await prompt([
|
|
146
|
+
{
|
|
147
|
+
name: 'shouldCreateProject',
|
|
148
|
+
message: `The project ${projectName} does not exist in ${accountId}. Would you like to create it?`,
|
|
149
|
+
type: 'confirm',
|
|
150
|
+
},
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
if (shouldCreateProject) {
|
|
154
|
+
try {
|
|
155
|
+
return createProject(accountId, projectName);
|
|
156
|
+
} catch (err) {
|
|
157
|
+
return logApiErrorInstance(err, new ApiErrorContext({ accountId }));
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
return logger.log(
|
|
161
|
+
`Your project ${chalk.bold(
|
|
162
|
+
projectName
|
|
163
|
+
)} could not be found in ${chalk.bold(accountId)}.`
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
logApiErrorInstance(err, new ApiErrorContext({ accountId }));
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
112
171
|
const getProjectDetailUrl = (projectName, accountId) => {
|
|
113
172
|
if (!projectName) return;
|
|
114
173
|
|
|
@@ -146,86 +205,107 @@ const showWelcomeMessage = (projectName, accountId) => {
|
|
|
146
205
|
);
|
|
147
206
|
};
|
|
148
207
|
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
208
|
+
const makeGetTaskStatus = taskType => {
|
|
209
|
+
let statusFn, statusText, statusStrings;
|
|
210
|
+
switch (taskType) {
|
|
211
|
+
case 'build':
|
|
212
|
+
statusFn = getBuildStatus;
|
|
213
|
+
statusText = PROJECT_TEXT.BUILD;
|
|
214
|
+
statusStrings = PROJECT_STRINGS.BUILD;
|
|
215
|
+
break;
|
|
216
|
+
case 'deploy':
|
|
217
|
+
statusFn = getDeployStatus;
|
|
218
|
+
statusText = PROJECT_TEXT.DEPLOY;
|
|
219
|
+
statusStrings = PROJECT_STRINGS.DEPLOY;
|
|
220
|
+
break;
|
|
221
|
+
default:
|
|
222
|
+
logger.error(`Cannot get status for task type ${taskType}`);
|
|
223
|
+
}
|
|
152
224
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
225
|
+
return async (accountId, taskName, taskId) => {
|
|
226
|
+
const spinnies = new Spinnies({
|
|
227
|
+
succeedColor: 'white',
|
|
228
|
+
failColor: 'white',
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
spinnies.add('overallTaskStatus', { text: 'Beginning' });
|
|
158
232
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
233
|
+
const initialTaskStatus = await statusFn(accountId, taskName, taskId);
|
|
234
|
+
|
|
235
|
+
spinnies.update('overallTaskStatus', {
|
|
236
|
+
text: statusStrings.INITIALIZE(
|
|
237
|
+
taskName,
|
|
238
|
+
initialTaskStatus[statusText.SUBTASK_KEY].length
|
|
239
|
+
),
|
|
164
240
|
});
|
|
165
|
-
}
|
|
166
241
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
spinnies.update(subBuild.buildName, {
|
|
193
|
-
text: updatedText,
|
|
194
|
-
});
|
|
195
|
-
break;
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
}
|
|
242
|
+
for (let subTask of initialTaskStatus[statusText.SUBTASK_KEY]) {
|
|
243
|
+
spinnies.add(subTask[statusText.SUBTASK_NAME_KEY], {
|
|
244
|
+
text: `${chalk.bold(subTask[statusText.SUBTASK_NAME_KEY])} #${taskId} ${
|
|
245
|
+
statusText.STATUS_TEXT[statusText.STATES.ENQUEUED]
|
|
246
|
+
}\n`,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return new Promise((resolve, reject) => {
|
|
251
|
+
const pollInterval = setInterval(async () => {
|
|
252
|
+
const taskStatus = await statusFn(accountId, taskName, taskId).catch(
|
|
253
|
+
reject
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const { status, [statusText.SUBTASK_KEY]: subTaskStatus } = taskStatus;
|
|
257
|
+
|
|
258
|
+
if (spinnies.hasActiveSpinners()) {
|
|
259
|
+
subTaskStatus.forEach(subTask => {
|
|
260
|
+
if (!spinnies.pick(subTask[statusText.SUBTASK_NAME_KEY])) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const updatedText = `${chalk.bold(
|
|
265
|
+
subTask[statusText.SUBTASK_NAME_KEY]
|
|
266
|
+
)} #${taskId} ${statusText.STATUS_TEXT[subTask.status]}\n`;
|
|
199
267
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (subBuild.status === PROJECT_BUILD_STATUS.FAILURE) {
|
|
217
|
-
logger.error(
|
|
218
|
-
`${chalk.bold(subBuild.buildName)} failed to build. ${
|
|
219
|
-
subBuild.errorMessage
|
|
220
|
-
}.`
|
|
221
|
-
);
|
|
268
|
+
switch (subTask.status) {
|
|
269
|
+
case statusText.STATES.SUCCESS:
|
|
270
|
+
spinnies.succeed(subTask[statusText.SUBTASK_NAME_KEY], {
|
|
271
|
+
text: updatedText,
|
|
272
|
+
});
|
|
273
|
+
break;
|
|
274
|
+
case statusText.STATES.FAILURE:
|
|
275
|
+
spinnies.fail(subTask.buildName, {
|
|
276
|
+
text: updatedText,
|
|
277
|
+
});
|
|
278
|
+
break;
|
|
279
|
+
default:
|
|
280
|
+
spinnies.update(subTask.buildName, {
|
|
281
|
+
text: updatedText,
|
|
282
|
+
});
|
|
283
|
+
break;
|
|
222
284
|
}
|
|
223
285
|
});
|
|
286
|
+
|
|
287
|
+
if (isTaskComplete(taskStatus)) {
|
|
288
|
+
subTaskStatus.forEach(subBuild => {
|
|
289
|
+
spinnies.remove(subBuild[statusText.SUBTASK_NAME_KEY]);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
if (status === statusText.STATES.SUCCESS) {
|
|
293
|
+
spinnies.succeed('overallTaskStatus', {
|
|
294
|
+
text: statusStrings.SUCCESS(taskName),
|
|
295
|
+
});
|
|
296
|
+
} else if (status === statusText.STATES.FAILURE) {
|
|
297
|
+
spinnies.fail('overallTaskStatus', {
|
|
298
|
+
text: statusStrings.FAIL(taskName),
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
clearInterval(pollInterval);
|
|
303
|
+
resolve(taskStatus);
|
|
304
|
+
}
|
|
224
305
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
});
|
|
306
|
+
}, POLLING_DELAY);
|
|
307
|
+
});
|
|
308
|
+
};
|
|
229
309
|
};
|
|
230
310
|
|
|
231
311
|
module.exports = {
|
|
@@ -234,5 +314,7 @@ module.exports = {
|
|
|
234
314
|
getOrCreateProjectConfig,
|
|
235
315
|
validateProjectConfig,
|
|
236
316
|
showWelcomeMessage,
|
|
237
|
-
pollBuildStatus,
|
|
317
|
+
pollBuildStatus: makeGetTaskStatus('build'),
|
|
318
|
+
pollDeployStatus: makeGetTaskStatus('deploy'),
|
|
319
|
+
ensureProjectExists,
|
|
238
320
|
};
|
package/lib/serverlessLogs.js
CHANGED
|
@@ -17,7 +17,7 @@ const handleKeypressToExit = exit => {
|
|
|
17
17
|
process.stdin.setRawMode(true);
|
|
18
18
|
process.stdin.on('keypress', (str, key) => {
|
|
19
19
|
if (key && ((key.ctrl && key.name == 'c') || key.name === 'escape')) {
|
|
20
|
-
exit();
|
|
20
|
+
exit(key.name === 'escape' ? 'esc' : 'ctrl+c');
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
23
|
};
|
|
@@ -25,14 +25,12 @@ const handleKeypressToExit = exit => {
|
|
|
25
25
|
const tailLogs = async ({
|
|
26
26
|
accountId,
|
|
27
27
|
compact,
|
|
28
|
-
|
|
28
|
+
spinnies,
|
|
29
29
|
fetchLatest,
|
|
30
30
|
tailCall,
|
|
31
31
|
}) => {
|
|
32
32
|
let initialAfter;
|
|
33
33
|
|
|
34
|
-
spinner.start();
|
|
35
|
-
|
|
36
34
|
try {
|
|
37
35
|
const latestLog = await fetchLatest();
|
|
38
36
|
initialAfter = base64EncodeString(latestLog.id);
|
|
@@ -54,8 +52,7 @@ const tailLogs = async ({
|
|
|
54
52
|
latestLog = await tailCall(after);
|
|
55
53
|
nextAfter = latestLog.paging.next.after;
|
|
56
54
|
} catch (e) {
|
|
57
|
-
|
|
58
|
-
spinner.clear();
|
|
55
|
+
spinnies.fail('tailLogs', { text: 'Stopped polling due to error.' });
|
|
59
56
|
if (e.statusCode !== 404) {
|
|
60
57
|
logApiErrorInstance(
|
|
61
58
|
e,
|
|
@@ -68,7 +65,6 @@ const tailLogs = async ({
|
|
|
68
65
|
}
|
|
69
66
|
|
|
70
67
|
if (latestLog && latestLog.results.length) {
|
|
71
|
-
spinner.clear();
|
|
72
68
|
outputLogs(latestLog, {
|
|
73
69
|
compact,
|
|
74
70
|
});
|
|
@@ -79,8 +75,10 @@ const tailLogs = async ({
|
|
|
79
75
|
}, TAIL_DELAY);
|
|
80
76
|
};
|
|
81
77
|
|
|
82
|
-
handleKeypressToExit(
|
|
83
|
-
|
|
78
|
+
handleKeypressToExit(exitKey => {
|
|
79
|
+
spinnies.succeed('tailLogs', {
|
|
80
|
+
text: `Stopped polling because "${exitKey}" was pressed.`,
|
|
81
|
+
});
|
|
84
82
|
process.exit();
|
|
85
83
|
});
|
|
86
84
|
await tail(initialAfter);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hubspot/cli",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.10-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.10-beta.1",
|
|
12
|
+
"@hubspot/serverless-dev-runtime": "^3.0.10-beta.1",
|
|
13
13
|
"archiver": "^5.3.0",
|
|
14
14
|
"chalk": "^4.1.0",
|
|
15
15
|
"express": "^4.17.1",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"ora": "^4.0.3",
|
|
21
21
|
"shelljs": "0.8.3",
|
|
22
22
|
"spinnies": "^0.5.1",
|
|
23
|
+
"supports-hyperlinks": "^2.2.0",
|
|
23
24
|
"tmp": "^0.2.1",
|
|
24
25
|
"update-notifier": "3.0.1",
|
|
25
26
|
"yargs": "15.4.1"
|
|
@@ -37,5 +38,5 @@
|
|
|
37
38
|
"publishConfig": {
|
|
38
39
|
"access": "public"
|
|
39
40
|
},
|
|
40
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "d3426a88d8355cdae053f02853a535c09ba84a7b"
|
|
41
42
|
}
|