@eve-horizon/cli 0.2.0 → 0.2.5
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/dist/commands/admin.js +15 -18
- package/dist/commands/auth.js +317 -42
- package/dist/commands/build.js +350 -0
- package/dist/commands/env.js +196 -7
- package/dist/commands/manifest.js +86 -0
- package/dist/commands/org.js +2 -1
- package/dist/commands/profile.js +2 -1
- package/dist/commands/system.js +124 -3
- package/dist/index.js +8 -0
- package/dist/lib/help.js +209 -8
- package/package.json +1 -1
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleBuild = handleBuild;
|
|
4
|
+
const args_1 = require("../lib/args");
|
|
5
|
+
const client_1 = require("../lib/client");
|
|
6
|
+
const output_1 = require("../lib/output");
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
// Entry point
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
async function handleBuild(subcommand, positionals, flags, context) {
|
|
11
|
+
const json = Boolean(flags.json);
|
|
12
|
+
switch (subcommand) {
|
|
13
|
+
case 'create':
|
|
14
|
+
return handleCreate(flags, context, json);
|
|
15
|
+
case 'list':
|
|
16
|
+
return handleList(positionals, flags, context, json);
|
|
17
|
+
case 'show':
|
|
18
|
+
return handleShow(positionals, flags, context, json);
|
|
19
|
+
case 'run':
|
|
20
|
+
return handleRun(positionals, flags, context, json);
|
|
21
|
+
case 'runs':
|
|
22
|
+
return handleRuns(positionals, flags, context, json);
|
|
23
|
+
case 'logs':
|
|
24
|
+
return handleLogs(positionals, flags, context, json);
|
|
25
|
+
case 'artifacts':
|
|
26
|
+
return handleArtifacts(positionals, flags, context, json);
|
|
27
|
+
case 'diagnose':
|
|
28
|
+
return handleDiagnose(positionals, flags, context, json);
|
|
29
|
+
case 'cancel':
|
|
30
|
+
return handleCancel(positionals, flags, context, json);
|
|
31
|
+
default:
|
|
32
|
+
throw new Error('Usage: eve build <create|list|show|run|runs|logs|artifacts|diagnose|cancel>\n' +
|
|
33
|
+
' create --project <id> --ref <sha> [--services <list>] - create a build spec\n' +
|
|
34
|
+
' list [--project <id>] - list build specs\n' +
|
|
35
|
+
' show <build_id> - show build spec details\n' +
|
|
36
|
+
' run <build_id> - start a build run\n' +
|
|
37
|
+
' runs <build_id> - list runs for a build\n' +
|
|
38
|
+
' logs <build_id> [--run <id>] - show build logs\n' +
|
|
39
|
+
' artifacts <build_id> - list build artifacts\n' +
|
|
40
|
+
' diagnose <build_id> - show full build state\n' +
|
|
41
|
+
' cancel <build_id> - cancel active build run');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Subcommands
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
async function handleCreate(flags, context, json) {
|
|
48
|
+
const projectId = (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
49
|
+
const ref = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
50
|
+
const manifestHash = (0, args_1.getStringFlag)(flags, ['manifest-hash', 'manifest']);
|
|
51
|
+
const services = (0, args_1.getStringFlag)(flags, ['services']);
|
|
52
|
+
if (!projectId || !ref || !manifestHash) {
|
|
53
|
+
throw new Error('Usage: eve build create --project <id> --ref <sha> --manifest-hash <hash> [--services <s1,s2>]');
|
|
54
|
+
}
|
|
55
|
+
const body = { git_sha: ref, manifest_hash: manifestHash };
|
|
56
|
+
if (services) {
|
|
57
|
+
body.services = services.split(',').map(s => s.trim());
|
|
58
|
+
}
|
|
59
|
+
const build = await (0, client_1.requestJson)(context, `/projects/${projectId}/builds`, { method: 'POST', body });
|
|
60
|
+
if (json) {
|
|
61
|
+
(0, output_1.outputJson)(build, json);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
console.log(`Build created: ${build.id}`);
|
|
65
|
+
console.log(` Project: ${build.project_id}`);
|
|
66
|
+
console.log(` SHA: ${build.git_sha}`);
|
|
67
|
+
if (build.manifest_hash) {
|
|
68
|
+
console.log(` Manifest: ${build.manifest_hash.substring(0, 12)}...`);
|
|
69
|
+
}
|
|
70
|
+
console.log(` Created: ${build.created_at}`);
|
|
71
|
+
}
|
|
72
|
+
async function handleList(positionals, flags, context, json) {
|
|
73
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
74
|
+
if (!projectId) {
|
|
75
|
+
throw new Error('Usage: eve build list [--project <id>]');
|
|
76
|
+
}
|
|
77
|
+
const query = buildQuery({
|
|
78
|
+
limit: (0, args_1.getStringFlag)(flags, ['limit']),
|
|
79
|
+
offset: (0, args_1.getStringFlag)(flags, ['offset']),
|
|
80
|
+
});
|
|
81
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/builds${query}`);
|
|
82
|
+
if (json) {
|
|
83
|
+
(0, output_1.outputJson)(response, json);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (response.data.length === 0) {
|
|
87
|
+
console.log('No builds found.');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
formatBuildList(response.data);
|
|
91
|
+
}
|
|
92
|
+
async function handleShow(positionals, flags, context, json) {
|
|
93
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
94
|
+
if (!buildId) {
|
|
95
|
+
throw new Error('Usage: eve build show <build_id>');
|
|
96
|
+
}
|
|
97
|
+
const build = await (0, client_1.requestJson)(context, `/builds/${buildId}`);
|
|
98
|
+
if (json) {
|
|
99
|
+
(0, output_1.outputJson)(build, json);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
formatBuildDetail(build);
|
|
103
|
+
}
|
|
104
|
+
async function handleRun(positionals, flags, context, json) {
|
|
105
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
106
|
+
if (!buildId) {
|
|
107
|
+
throw new Error('Usage: eve build run <build_id>');
|
|
108
|
+
}
|
|
109
|
+
const run = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs`, { method: 'POST' });
|
|
110
|
+
if (json) {
|
|
111
|
+
(0, output_1.outputJson)(run, json);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
console.log(`Build run started: ${run.id}`);
|
|
115
|
+
console.log(` Build: ${run.build_id}`);
|
|
116
|
+
console.log(` Status: ${run.status}`);
|
|
117
|
+
if (run.backend) {
|
|
118
|
+
console.log(` Backend: ${run.backend}`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async function handleRuns(positionals, flags, context, json) {
|
|
122
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
123
|
+
if (!buildId) {
|
|
124
|
+
throw new Error('Usage: eve build runs <build_id>');
|
|
125
|
+
}
|
|
126
|
+
const query = buildQuery({
|
|
127
|
+
limit: (0, args_1.getStringFlag)(flags, ['limit']),
|
|
128
|
+
offset: (0, args_1.getStringFlag)(flags, ['offset']),
|
|
129
|
+
});
|
|
130
|
+
const response = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs${query}`);
|
|
131
|
+
if (json) {
|
|
132
|
+
(0, output_1.outputJson)(response, json);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (response.data.length === 0) {
|
|
136
|
+
console.log('No runs found for this build.');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
formatRunList(response.data);
|
|
140
|
+
}
|
|
141
|
+
async function handleLogs(positionals, flags, context, json) {
|
|
142
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
143
|
+
const runId = (0, args_1.getStringFlag)(flags, ['run']);
|
|
144
|
+
if (!buildId) {
|
|
145
|
+
throw new Error('Usage: eve build logs <build_id> [--run <run_id>]');
|
|
146
|
+
}
|
|
147
|
+
const query = runId ? `?run_id=${encodeURIComponent(runId)}` : '';
|
|
148
|
+
const logs = await (0, client_1.requestJson)(context, `/builds/${buildId}/logs${query}`);
|
|
149
|
+
if (json) {
|
|
150
|
+
(0, output_1.outputJson)(logs, json);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (logs.logs.length === 0) {
|
|
154
|
+
console.log('No logs available.');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
for (const entry of logs.logs) {
|
|
158
|
+
const line = entry.line;
|
|
159
|
+
if (typeof line.message === 'string') {
|
|
160
|
+
console.log(line.message);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (Array.isArray(line.lines)) {
|
|
164
|
+
for (const item of line.lines) {
|
|
165
|
+
if (typeof item === 'string') {
|
|
166
|
+
console.log(item);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
console.log(JSON.stringify(line));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async function handleArtifacts(positionals, flags, context, json) {
|
|
175
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
176
|
+
if (!buildId) {
|
|
177
|
+
throw new Error('Usage: eve build artifacts <build_id>');
|
|
178
|
+
}
|
|
179
|
+
const response = await (0, client_1.requestJson)(context, `/builds/${buildId}/artifacts`);
|
|
180
|
+
if (json) {
|
|
181
|
+
(0, output_1.outputJson)(response, json);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (response.data.length === 0) {
|
|
185
|
+
console.log('No artifacts found for this build.');
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
formatArtifactList(response.data);
|
|
189
|
+
}
|
|
190
|
+
async function handleCancel(positionals, flags, context, json) {
|
|
191
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
192
|
+
if (!buildId) {
|
|
193
|
+
throw new Error('Usage: eve build cancel <build_id>');
|
|
194
|
+
}
|
|
195
|
+
const result = await (0, client_1.requestJson)(context, `/builds/${buildId}/cancel`, { method: 'POST' });
|
|
196
|
+
if (json) {
|
|
197
|
+
(0, output_1.outputJson)(result, json);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
console.log(`Build cancelled: ${buildId}`);
|
|
201
|
+
}
|
|
202
|
+
async function handleDiagnose(positionals, flags, context, json) {
|
|
203
|
+
const buildId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['id']);
|
|
204
|
+
if (!buildId) {
|
|
205
|
+
throw new Error('Usage: eve build diagnose <build_id>');
|
|
206
|
+
}
|
|
207
|
+
const spec = await (0, client_1.requestJson)(context, `/builds/${buildId}`);
|
|
208
|
+
const runs = await (0, client_1.requestJson)(context, `/builds/${buildId}/runs?limit=20&offset=0`);
|
|
209
|
+
const artifacts = await (0, client_1.requestJson)(context, `/builds/${buildId}/artifacts`);
|
|
210
|
+
const latestRun = runs.data[0];
|
|
211
|
+
const logs = latestRun
|
|
212
|
+
? await (0, client_1.requestJson)(context, `/builds/${buildId}/logs?run_id=${encodeURIComponent(latestRun.id)}`)
|
|
213
|
+
: { logs: [] };
|
|
214
|
+
const payload = { spec, runs: runs.data, artifacts: artifacts.data, logs: logs.logs };
|
|
215
|
+
if (json) {
|
|
216
|
+
(0, output_1.outputJson)(payload, json);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
console.log(`Build diagnose: ${spec.id}`);
|
|
220
|
+
formatBuildDetail(spec);
|
|
221
|
+
console.log('');
|
|
222
|
+
if (runs.data.length > 0) {
|
|
223
|
+
formatRunList(runs.data);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
console.log('No runs found.');
|
|
227
|
+
}
|
|
228
|
+
console.log('');
|
|
229
|
+
if (artifacts.data.length > 0) {
|
|
230
|
+
formatArtifactList(artifacts.data);
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
console.log('No artifacts found.');
|
|
234
|
+
}
|
|
235
|
+
console.log('');
|
|
236
|
+
if (logs.logs.length > 0) {
|
|
237
|
+
console.log('Recent logs:');
|
|
238
|
+
for (const entry of logs.logs.slice(-50)) {
|
|
239
|
+
const line = entry.line;
|
|
240
|
+
if (typeof line.message === 'string') {
|
|
241
|
+
console.log(line.message);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (Array.isArray(line.lines)) {
|
|
245
|
+
for (const item of line.lines) {
|
|
246
|
+
if (typeof item === 'string') {
|
|
247
|
+
console.log(item);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
console.log(JSON.stringify(line));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
console.log('No logs found.');
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// ---------------------------------------------------------------------------
|
|
260
|
+
// Formatters
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
function formatBuildList(builds) {
|
|
263
|
+
console.log('Build ID'.padEnd(30) + 'SHA'.padEnd(12) + 'Created By'.padEnd(20) + 'Created');
|
|
264
|
+
console.log('-'.repeat(90));
|
|
265
|
+
for (const build of builds) {
|
|
266
|
+
const id = build.id.padEnd(30);
|
|
267
|
+
const sha = build.git_sha.substring(0, 8).padEnd(12);
|
|
268
|
+
const creator = (build.created_by ?? '-').padEnd(20);
|
|
269
|
+
const created = new Date(build.created_at).toLocaleString();
|
|
270
|
+
console.log(`${id}${sha}${creator}${created}`);
|
|
271
|
+
}
|
|
272
|
+
console.log('');
|
|
273
|
+
console.log(`Total: ${builds.length} builds`);
|
|
274
|
+
}
|
|
275
|
+
function formatBuildDetail(build) {
|
|
276
|
+
console.log(`Build: ${build.id}`);
|
|
277
|
+
console.log(` Project: ${build.project_id}`);
|
|
278
|
+
console.log(` SHA: ${build.git_sha}`);
|
|
279
|
+
console.log(` Manifest Hash: ${build.manifest_hash}`);
|
|
280
|
+
console.log(` Created By: ${build.created_by ?? '-'}`);
|
|
281
|
+
console.log(` Created: ${build.created_at}`);
|
|
282
|
+
if (build.services_json) {
|
|
283
|
+
console.log(` Services: ${JSON.stringify(build.services_json)}`);
|
|
284
|
+
}
|
|
285
|
+
if (build.inputs_json) {
|
|
286
|
+
console.log(` Inputs: ${JSON.stringify(build.inputs_json)}`);
|
|
287
|
+
}
|
|
288
|
+
if (build.registry_json) {
|
|
289
|
+
console.log(` Registry: ${JSON.stringify(build.registry_json)}`);
|
|
290
|
+
}
|
|
291
|
+
if (build.cache_json) {
|
|
292
|
+
console.log(` Cache: ${JSON.stringify(build.cache_json)}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function formatRunList(runs) {
|
|
296
|
+
console.log('Run ID'.padEnd(30) + 'Status'.padEnd(15) + 'Backend'.padEnd(15) + 'Started');
|
|
297
|
+
console.log('-'.repeat(80));
|
|
298
|
+
for (const run of runs) {
|
|
299
|
+
const id = run.id.padEnd(30);
|
|
300
|
+
const status = run.status.padEnd(15);
|
|
301
|
+
const backend = (run.backend ?? '-').padEnd(15);
|
|
302
|
+
const started = run.started_at ? new Date(run.started_at).toLocaleString() : '-';
|
|
303
|
+
console.log(`${id}${status}${backend}${started}`);
|
|
304
|
+
if (run.error_message) {
|
|
305
|
+
console.log(` Error: ${run.error_message}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
console.log('');
|
|
309
|
+
console.log(`Total: ${runs.length} runs`);
|
|
310
|
+
}
|
|
311
|
+
function formatArtifactList(artifacts) {
|
|
312
|
+
console.log('Service'.padEnd(25) + 'Image'.padEnd(50) + 'Size');
|
|
313
|
+
console.log('-'.repeat(90));
|
|
314
|
+
for (const artifact of artifacts) {
|
|
315
|
+
const service = artifact.service_name.padEnd(25);
|
|
316
|
+
const image = artifact.image_ref.padEnd(50);
|
|
317
|
+
const size = artifact.size_bytes ? formatBytes(artifact.size_bytes) : '-';
|
|
318
|
+
console.log(`${service}${image}${size}`);
|
|
319
|
+
if (artifact.digest) {
|
|
320
|
+
console.log(` Digest: ${artifact.digest}`);
|
|
321
|
+
}
|
|
322
|
+
if (artifact.platforms_json && artifact.platforms_json.length > 0) {
|
|
323
|
+
console.log(` Platforms: ${artifact.platforms_json.join(', ')}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
console.log('');
|
|
327
|
+
console.log(`Total: ${artifacts.length} artifacts`);
|
|
328
|
+
}
|
|
329
|
+
function formatBytes(bytes) {
|
|
330
|
+
if (bytes < 1024)
|
|
331
|
+
return `${bytes} B`;
|
|
332
|
+
if (bytes < 1024 * 1024)
|
|
333
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
334
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
335
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
336
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
337
|
+
}
|
|
338
|
+
// ---------------------------------------------------------------------------
|
|
339
|
+
// Utilities
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
function buildQuery(params) {
|
|
342
|
+
const search = new URLSearchParams();
|
|
343
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
344
|
+
if (value === undefined || value === '')
|
|
345
|
+
return;
|
|
346
|
+
search.set(key, value);
|
|
347
|
+
});
|
|
348
|
+
const query = search.toString();
|
|
349
|
+
return query ? `?${query}` : '';
|
|
350
|
+
}
|
package/dist/commands/env.js
CHANGED
|
@@ -55,6 +55,8 @@ async function handleEnv(subcommand, positionals, flags, context) {
|
|
|
55
55
|
return handleDeploy(positionals, flags, context, json);
|
|
56
56
|
case 'logs':
|
|
57
57
|
return handleLogs(positionals, flags, context, json);
|
|
58
|
+
case 'diagnose':
|
|
59
|
+
return handleDiagnose(positionals, flags, context, json);
|
|
58
60
|
case 'delete':
|
|
59
61
|
return handleDelete(positionals, flags, context, json);
|
|
60
62
|
default:
|
|
@@ -64,6 +66,7 @@ async function handleEnv(subcommand, positionals, flags, context) {
|
|
|
64
66
|
' create <name> --type=<type> [options] - create an environment\n' +
|
|
65
67
|
' deploy <env> --ref <sha> [--direct] [--inputs <json>] - deploy to an environment\n' +
|
|
66
68
|
' logs <project> <env> <service> [--since <seconds>] [--tail <n>] [--grep <text>] - get service logs\n' +
|
|
69
|
+
' diagnose <project> <env> - diagnose deployment health and events\n' +
|
|
67
70
|
' delete <name> [--project=<id>] [--force] - delete an environment');
|
|
68
71
|
}
|
|
69
72
|
}
|
|
@@ -106,11 +109,12 @@ async function handleShow(positionals, flags, context, json) {
|
|
|
106
109
|
throw new Error('Usage: eve env show <project> <name> [--project=<id>] [--name=<name>]');
|
|
107
110
|
}
|
|
108
111
|
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}`);
|
|
112
|
+
const health = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/health`);
|
|
109
113
|
if (json) {
|
|
110
|
-
(0, output_1.outputJson)(response, json);
|
|
114
|
+
(0, output_1.outputJson)({ ...response, health }, json);
|
|
111
115
|
}
|
|
112
116
|
else {
|
|
113
|
-
formatEnvironmentDetails(response);
|
|
117
|
+
formatEnvironmentDetails(response, health);
|
|
114
118
|
}
|
|
115
119
|
}
|
|
116
120
|
/**
|
|
@@ -157,7 +161,7 @@ async function handleCreate(positionals, flags, context, json) {
|
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
163
|
/**
|
|
160
|
-
* eve env deploy [project] <name> --ref <sha> [--direct] [--inputs <json>]
|
|
164
|
+
* eve env deploy [project] <name> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>]
|
|
161
165
|
* Deploy to an environment
|
|
162
166
|
* If project is in profile, can use: eve env deploy <name> --ref <sha>
|
|
163
167
|
*/
|
|
@@ -190,7 +194,7 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
190
194
|
envName = flagName;
|
|
191
195
|
}
|
|
192
196
|
if (!projectId || !envName) {
|
|
193
|
-
throw new Error('Usage: eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--project=<id>]');
|
|
197
|
+
throw new Error('Usage: eve env deploy <env> --ref <sha> [--direct] [--inputs <json>] [--image-tag <tag>] [--project=<id>]');
|
|
194
198
|
}
|
|
195
199
|
// --ref flag is now required
|
|
196
200
|
const gitSha = (0, args_1.getStringFlag)(flags, ['ref']);
|
|
@@ -222,6 +226,8 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
222
226
|
'Example: --inputs \'{"release_id":"rel_xxx","smoke_test":false}\'');
|
|
223
227
|
}
|
|
224
228
|
}
|
|
229
|
+
// Parse --image-tag flag (optional)
|
|
230
|
+
const imageTag = (0, args_1.getStringFlag)(flags, ['image-tag', 'image_tag', 'imageTag']);
|
|
225
231
|
// POST deployment
|
|
226
232
|
const body = {
|
|
227
233
|
git_sha: gitSha,
|
|
@@ -233,6 +239,9 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
233
239
|
if (inputs) {
|
|
234
240
|
body.inputs = inputs;
|
|
235
241
|
}
|
|
242
|
+
if (imageTag) {
|
|
243
|
+
body.image_tag = imageTag;
|
|
244
|
+
}
|
|
236
245
|
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/deploy`, {
|
|
237
246
|
method: 'POST',
|
|
238
247
|
body,
|
|
@@ -241,11 +250,46 @@ async function handleDeploy(positionals, flags, context, json) {
|
|
|
241
250
|
(0, output_1.outputJson)(response, json);
|
|
242
251
|
}
|
|
243
252
|
else {
|
|
253
|
+
if (response.pipeline_run) {
|
|
254
|
+
console.log('');
|
|
255
|
+
console.log('Pipeline deployment queued.');
|
|
256
|
+
console.log(` Pipeline Run: ${response.pipeline_run.run.id}`);
|
|
257
|
+
console.log(` Pipeline: ${response.pipeline_run.run.pipeline_name}`);
|
|
258
|
+
console.log(` Status: ${response.pipeline_run.run.status}`);
|
|
259
|
+
console.log(` Environment: ${response.environment.name}`);
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
244
262
|
console.log('');
|
|
245
|
-
console.log(`Deployment
|
|
246
|
-
|
|
263
|
+
console.log(`Deployment submitted.`);
|
|
264
|
+
if (response.release) {
|
|
265
|
+
console.log(` Release ID: ${response.release.id}`);
|
|
266
|
+
}
|
|
247
267
|
console.log(` Environment: ${response.environment.name}`);
|
|
248
268
|
console.log(` Namespace: ${response.environment.namespace || '(none)'}`);
|
|
269
|
+
if (response.deployment_status?.k8s_status) {
|
|
270
|
+
const status = response.deployment_status.k8s_status;
|
|
271
|
+
const readiness = `${status.available_replicas}/${status.desired_replicas}`;
|
|
272
|
+
console.log(` Status: ${response.deployment_status.state} (${readiness} ready)`);
|
|
273
|
+
}
|
|
274
|
+
else if (response.deployment_status?.state) {
|
|
275
|
+
console.log(` Status: ${response.deployment_status.state}`);
|
|
276
|
+
}
|
|
277
|
+
const warnings = response.warnings ?? getDeploymentWarnings(response.deployment_status);
|
|
278
|
+
if (warnings.length > 0) {
|
|
279
|
+
console.log('');
|
|
280
|
+
console.log('Warnings:');
|
|
281
|
+
for (const warning of warnings) {
|
|
282
|
+
console.log(` - ${warning}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
const watchFlag = (0, args_1.toBoolean)(flags.watch);
|
|
286
|
+
const shouldWatch = (watchFlag ?? true)
|
|
287
|
+
&& response.deployment_status?.state !== 'ready';
|
|
288
|
+
if (shouldWatch) {
|
|
289
|
+
const timeoutRaw = (0, args_1.getStringFlag)(flags, ['timeout']);
|
|
290
|
+
const timeoutSeconds = timeoutRaw ? parseInt(timeoutRaw, 10) : 120;
|
|
291
|
+
await watchDeploymentStatus(context, projectId, envName, Number.isFinite(timeoutSeconds) ? timeoutSeconds : 120);
|
|
292
|
+
}
|
|
249
293
|
}
|
|
250
294
|
}
|
|
251
295
|
/**
|
|
@@ -277,6 +321,26 @@ async function handleLogs(positionals, flags, context, json) {
|
|
|
277
321
|
console.log(`[${entry.timestamp}] ${entry.line}`);
|
|
278
322
|
}
|
|
279
323
|
}
|
|
324
|
+
/**
|
|
325
|
+
* eve env diagnose <project> <env>
|
|
326
|
+
* Diagnose deployment health for an environment (k8s-only)
|
|
327
|
+
*/
|
|
328
|
+
async function handleDiagnose(positionals, flags, context, json) {
|
|
329
|
+
const projectId = positionals[0] ?? (0, args_1.getStringFlag)(flags, ['project']) ?? context.projectId;
|
|
330
|
+
const envName = positionals[1] ?? (0, args_1.getStringFlag)(flags, ['env', 'name']);
|
|
331
|
+
if (!projectId || !envName) {
|
|
332
|
+
throw new Error('Usage: eve env diagnose <project> <env> [--events <n>]');
|
|
333
|
+
}
|
|
334
|
+
const query = buildQuery({
|
|
335
|
+
events: (0, args_1.getStringFlag)(flags, ['events']),
|
|
336
|
+
});
|
|
337
|
+
const response = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/diagnose${query}`);
|
|
338
|
+
if (json) {
|
|
339
|
+
(0, output_1.outputJson)(response, json);
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
formatEnvDiagnose(response);
|
|
343
|
+
}
|
|
280
344
|
/**
|
|
281
345
|
* eve env delete <name> [--project=<id>] [--force]
|
|
282
346
|
* Delete an environment
|
|
@@ -399,7 +463,7 @@ function formatEnvironmentsTable(environments) {
|
|
|
399
463
|
/**
|
|
400
464
|
* Format a single environment's details
|
|
401
465
|
*/
|
|
402
|
-
function formatEnvironmentDetails(env) {
|
|
466
|
+
function formatEnvironmentDetails(env, health) {
|
|
403
467
|
console.log(`Environment: ${env.name}`);
|
|
404
468
|
console.log('');
|
|
405
469
|
console.log(` ID: ${env.id}`);
|
|
@@ -408,6 +472,19 @@ function formatEnvironmentDetails(env) {
|
|
|
408
472
|
console.log(` Namespace: ${env.namespace || '(none)'}`);
|
|
409
473
|
console.log(` Database Ref: ${env.db_ref || '(none)'}`);
|
|
410
474
|
console.log(` Current Release: ${env.current_release_id || '(none)'}`);
|
|
475
|
+
if (health) {
|
|
476
|
+
console.log('');
|
|
477
|
+
console.log(` Deployment Status: ${health.status}`);
|
|
478
|
+
if (health.deployment) {
|
|
479
|
+
console.log(` Deployment Ready: ${health.deployment.available_replicas}/${health.deployment.desired_replicas}`);
|
|
480
|
+
}
|
|
481
|
+
if (health.warnings && health.warnings.length > 0) {
|
|
482
|
+
console.log(' Warnings:');
|
|
483
|
+
for (const warning of health.warnings) {
|
|
484
|
+
console.log(` - ${warning}`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
411
488
|
if (env.overrides && Object.keys(env.overrides).length > 0) {
|
|
412
489
|
console.log('');
|
|
413
490
|
console.log(' Overrides:');
|
|
@@ -419,6 +496,118 @@ function formatEnvironmentDetails(env) {
|
|
|
419
496
|
console.log(` Created: ${formatDate(env.created_at)}`);
|
|
420
497
|
console.log(` Updated: ${formatDate(env.updated_at)}`);
|
|
421
498
|
}
|
|
499
|
+
function formatEnvDiagnose(report) {
|
|
500
|
+
console.log(`Environment Diagnose: ${report.env_name}`);
|
|
501
|
+
console.log('');
|
|
502
|
+
console.log(` Namespace: ${report.namespace || '(none)'}`);
|
|
503
|
+
console.log(` Status: ${report.status}`);
|
|
504
|
+
console.log(` Ready: ${report.ready ? 'yes' : 'no'}`);
|
|
505
|
+
console.log(` K8s: ${report.k8s_available ? 'available' : 'unavailable'}`);
|
|
506
|
+
if (report.warnings && report.warnings.length > 0) {
|
|
507
|
+
console.log('');
|
|
508
|
+
console.log('Warnings:');
|
|
509
|
+
for (const warning of report.warnings) {
|
|
510
|
+
console.log(` - ${warning}`);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (report.deployments.length > 0) {
|
|
514
|
+
console.log('');
|
|
515
|
+
console.log('Deployments:');
|
|
516
|
+
const nameWidth = Math.max(4, ...report.deployments.map((d) => d.name.length));
|
|
517
|
+
const readyWidth = Math.max(5, ...report.deployments.map((d) => `${d.available_replicas}/${d.desired_replicas}`.length));
|
|
518
|
+
const header = [
|
|
519
|
+
padRight('Name', nameWidth),
|
|
520
|
+
padRight('Ready', readyWidth),
|
|
521
|
+
'Status',
|
|
522
|
+
].join(' ');
|
|
523
|
+
console.log(header);
|
|
524
|
+
console.log('-'.repeat(header.length));
|
|
525
|
+
for (const deployment of report.deployments) {
|
|
526
|
+
const readiness = `${deployment.available_replicas}/${deployment.desired_replicas}`;
|
|
527
|
+
const status = deployment.ready ? 'ready' : 'not-ready';
|
|
528
|
+
console.log([
|
|
529
|
+
padRight(deployment.name, nameWidth),
|
|
530
|
+
padRight(readiness, readyWidth),
|
|
531
|
+
status,
|
|
532
|
+
].join(' '));
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (report.pods.length > 0) {
|
|
536
|
+
console.log('');
|
|
537
|
+
console.log('Pods:');
|
|
538
|
+
const nameWidth = Math.max(4, ...report.pods.map((p) => p.name.length));
|
|
539
|
+
const phaseWidth = Math.max(5, ...report.pods.map((p) => p.phase.length));
|
|
540
|
+
const restartsWidth = Math.max(8, ...report.pods.map((p) => String(p.restarts).length));
|
|
541
|
+
const header = [
|
|
542
|
+
padRight('Name', nameWidth),
|
|
543
|
+
padRight('Phase', phaseWidth),
|
|
544
|
+
padRight('Restarts', restartsWidth),
|
|
545
|
+
'Ready',
|
|
546
|
+
'Age',
|
|
547
|
+
].join(' ');
|
|
548
|
+
console.log(header);
|
|
549
|
+
console.log('-'.repeat(header.length));
|
|
550
|
+
for (const pod of report.pods) {
|
|
551
|
+
console.log([
|
|
552
|
+
padRight(pod.name, nameWidth),
|
|
553
|
+
padRight(pod.phase, phaseWidth),
|
|
554
|
+
padRight(String(pod.restarts), restartsWidth),
|
|
555
|
+
pod.ready ? 'yes' : 'no',
|
|
556
|
+
pod.age,
|
|
557
|
+
].join(' '));
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
if (report.events.length > 0) {
|
|
561
|
+
console.log('');
|
|
562
|
+
console.log('Events:');
|
|
563
|
+
for (const event of report.events) {
|
|
564
|
+
const timestamp = event.timestamp ?? 'unknown';
|
|
565
|
+
const reason = event.reason ?? 'Unknown';
|
|
566
|
+
const message = event.message ?? '';
|
|
567
|
+
console.log(` [${timestamp}] ${event.type} ${reason}: ${message}`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
function getDeploymentWarnings(status) {
|
|
572
|
+
if (!status)
|
|
573
|
+
return [];
|
|
574
|
+
const warnings = [];
|
|
575
|
+
if (status.state !== 'ready') {
|
|
576
|
+
warnings.push(`Deployment state: ${status.state}`);
|
|
577
|
+
}
|
|
578
|
+
if (status.k8s_status) {
|
|
579
|
+
const { available_replicas, desired_replicas, ready, conditions } = status.k8s_status;
|
|
580
|
+
if (!ready) {
|
|
581
|
+
warnings.push(`Deployment replicas not ready (${available_replicas}/${desired_replicas})`);
|
|
582
|
+
}
|
|
583
|
+
for (const condition of conditions) {
|
|
584
|
+
if (condition.status !== 'True' && condition.message) {
|
|
585
|
+
warnings.push(`${condition.type}: ${condition.message}`);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return Array.from(new Set(warnings));
|
|
590
|
+
}
|
|
591
|
+
async function watchDeploymentStatus(context, projectId, envName, timeoutSeconds) {
|
|
592
|
+
const start = Date.now();
|
|
593
|
+
const pollIntervalMs = 3000;
|
|
594
|
+
console.log('');
|
|
595
|
+
console.log('Watching deployment status...');
|
|
596
|
+
while ((Date.now() - start) / 1000 < timeoutSeconds) {
|
|
597
|
+
const health = await (0, client_1.requestJson)(context, `/projects/${projectId}/envs/${envName}/health`);
|
|
598
|
+
const elapsed = Math.floor((Date.now() - start) / 1000);
|
|
599
|
+
const readiness = health.deployment
|
|
600
|
+
? `${health.deployment.available_replicas}/${health.deployment.desired_replicas}`
|
|
601
|
+
: 'n/a';
|
|
602
|
+
console.log(` [${elapsed}s] ${health.status} (${readiness} ready)`);
|
|
603
|
+
if (health.ready) {
|
|
604
|
+
console.log(' Deployment is ready.');
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
608
|
+
}
|
|
609
|
+
console.log(` Timeout after ${timeoutSeconds}s. Run "eve env diagnose ${projectId} ${envName}" for details.`);
|
|
610
|
+
}
|
|
422
611
|
/**
|
|
423
612
|
* Format a date string for display
|
|
424
613
|
*/
|