@solidactions/cli 1.1.0 → 1.2.0
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/run-list.js +114 -6
- package/dist/index.js +1 -0
- package/package.json +1 -1
|
@@ -26,6 +26,8 @@ async function runs(projectName, options = {}) {
|
|
|
26
26
|
params.workflow = options.workflow;
|
|
27
27
|
if (options.detailed)
|
|
28
28
|
params.detailed = '1';
|
|
29
|
+
if (options.hasErrors)
|
|
30
|
+
params.has_errors = '1';
|
|
29
31
|
const response = await axios_1.default.get(`${config.host}/api/v1/runs`, {
|
|
30
32
|
headers: (0, api_1.getApiHeaders)(config),
|
|
31
33
|
params,
|
|
@@ -97,8 +99,10 @@ function displayDetailedList(runsList, projectName) {
|
|
|
97
99
|
const status = run.execution_status || run.status || '?';
|
|
98
100
|
const statusColor = getStatusColor(status);
|
|
99
101
|
const exitStr = run.exit_code !== null && run.exit_code !== undefined ? ` (exit ${run.exit_code})` : '';
|
|
102
|
+
const isSilentFailure = hasRunErrors(run) && isSuccessStatus(status);
|
|
100
103
|
console.log('');
|
|
101
|
-
|
|
104
|
+
const silentTag = isSilentFailure ? chalk_1.default.yellow(' [DEGRADED]') : '';
|
|
105
|
+
console.log(chalk_1.default.bold(` Run #${run.id}`) + chalk_1.default.gray(` — ${run.workflow_name || '?'} (${run.project_name || '?'})`) + silentTag);
|
|
102
106
|
console.log(` Status: ${statusColor(status)}${chalk_1.default.gray(exitStr)}`);
|
|
103
107
|
console.log(` Trigger: ${chalk_1.default.gray(run.triggered_by || '-')}`);
|
|
104
108
|
// Timeline
|
|
@@ -108,18 +112,38 @@ function displayDetailedList(runsList, projectName) {
|
|
|
108
112
|
console.log(` Started: ${chalk_1.default.gray(formatTs(run.timeline.started))}`);
|
|
109
113
|
console.log(` Completed: ${chalk_1.default.gray(formatTs(run.timeline.completed))}`);
|
|
110
114
|
}
|
|
111
|
-
// Steps
|
|
115
|
+
// Steps detail
|
|
112
116
|
if (run.steps && run.steps.length > 0) {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
displayStepsDetail(run.steps);
|
|
118
|
+
}
|
|
119
|
+
// Workflow output
|
|
120
|
+
if (run.output) {
|
|
121
|
+
const outputStr = JSON.stringify(unwrapOutput(run.output), null, 2);
|
|
122
|
+
const lines = outputStr.split('\n');
|
|
123
|
+
if (lines.length === 1) {
|
|
124
|
+
console.log(` Output: ${chalk_1.default.gray(truncate(outputStr, 60))}`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
console.log(` Output:`);
|
|
128
|
+
for (const line of lines.slice(0, 5)) {
|
|
129
|
+
console.log(chalk_1.default.gray(` ${line}`));
|
|
130
|
+
}
|
|
131
|
+
if (lines.length > 5) {
|
|
132
|
+
console.log(chalk_1.default.gray(` ... (${lines.length - 5} more lines)`));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Workflow error
|
|
137
|
+
if (run.error) {
|
|
138
|
+
const errMsg = typeof run.error === 'string' ? run.error : JSON.stringify(run.error);
|
|
139
|
+
console.log(` ${chalk_1.default.bold.red('Error:')} ${chalk_1.default.red(truncate(errMsg, 60))}`);
|
|
116
140
|
}
|
|
117
141
|
// Logs snippet (first errors or last 3 lines)
|
|
118
142
|
if (run.logs && typeof run.logs === 'string' && run.logs.trim()) {
|
|
119
143
|
const lines = run.logs.trim().split('\n');
|
|
120
144
|
const errorLines = lines.filter((l) => /error|fail|exception/i.test(l));
|
|
121
145
|
if (errorLines.length > 0) {
|
|
122
|
-
console.log(`
|
|
146
|
+
console.log(` Logs:`);
|
|
123
147
|
for (const line of errorLines.slice(0, 3)) {
|
|
124
148
|
console.log(chalk_1.default.red(` ${truncate(line, 80)}`));
|
|
125
149
|
}
|
|
@@ -129,7 +153,91 @@ function displayDetailedList(runsList, projectName) {
|
|
|
129
153
|
console.log('');
|
|
130
154
|
console.log(chalk_1.default.gray(`Showing ${runsList.length} run(s)`));
|
|
131
155
|
}
|
|
156
|
+
function displayStepsDetail(steps) {
|
|
157
|
+
const completed = steps.filter((s) => s.completed_at || s.completed_at_epoch_ms).length;
|
|
158
|
+
const totalDuration = steps.reduce((sum, s) => sum + (s.duration_ms || 0), 0);
|
|
159
|
+
const errorCount = steps.filter((s) => s.error || s.status === 'error' || s.status === 'failed').length;
|
|
160
|
+
const retryCount = steps.filter((s) => s.retried || s.status === 'retried').length;
|
|
161
|
+
let summary = `${completed}/${steps.length} completed (${formatDuration(totalDuration)} total)`;
|
|
162
|
+
if (errorCount > 0)
|
|
163
|
+
summary += chalk_1.default.red(` · ${errorCount} errored`);
|
|
164
|
+
if (retryCount > 0)
|
|
165
|
+
summary += chalk_1.default.yellow(` · ${retryCount} retried`);
|
|
166
|
+
console.log(` Steps: ${chalk_1.default.gray(summary)}`);
|
|
167
|
+
// Per-step detail table
|
|
168
|
+
console.log(chalk_1.default.gray(` ${'NAME'.padEnd(24)}${'STATUS'.padEnd(12)}${'DURATION'.padEnd(10)}OUTPUT`));
|
|
169
|
+
console.log(chalk_1.default.gray(` ${'─'.repeat(70)}`));
|
|
170
|
+
for (const step of steps) {
|
|
171
|
+
const name = truncate(step.name || '?', 23).padEnd(24);
|
|
172
|
+
const stepStatus = getStepStatus(step);
|
|
173
|
+
const stepStatusColor = getStepStatusColor(stepStatus);
|
|
174
|
+
const duration = formatDuration(step.duration_ms);
|
|
175
|
+
const output = step.output ? truncate(JSON.stringify(unwrapOutput(step.output)), 40) : '-';
|
|
176
|
+
console.log(` ${name}${stepStatusColor(stepStatus.padEnd(12))}${chalk_1.default.gray(duration.padEnd(10))}${chalk_1.default.gray(output)}`);
|
|
177
|
+
if (step.error) {
|
|
178
|
+
const errMsg = typeof step.error === 'string' ? step.error : JSON.stringify(step.error);
|
|
179
|
+
console.log(chalk_1.default.red(` error: ${truncate(errMsg, 70)}`));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
132
183
|
// ─── Utility ───────────────────────────────────────────────────────────────
|
|
184
|
+
function unwrapOutput(output) {
|
|
185
|
+
if (output && output.__solidactions_serializer === 'superjson' && output.json) {
|
|
186
|
+
return output.json;
|
|
187
|
+
}
|
|
188
|
+
return output;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Determine if a run has any errors (step errors, retries, or non-empty error arrays in output).
|
|
192
|
+
*/
|
|
193
|
+
function hasRunErrors(run) {
|
|
194
|
+
// Check step-level errors
|
|
195
|
+
if (run.steps && run.steps.some((s) => s.error || s.status === 'error' || s.status === 'failed' || s.retried || s.status === 'retried')) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
// Check workflow-level error
|
|
199
|
+
if (run.error)
|
|
200
|
+
return true;
|
|
201
|
+
// Check output.errors array
|
|
202
|
+
const output = unwrapOutput(run.output);
|
|
203
|
+
if (output && Array.isArray(output.errors) && output.errors.length > 0)
|
|
204
|
+
return true;
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
function isSuccessStatus(status) {
|
|
208
|
+
const s = status?.toLowerCase();
|
|
209
|
+
return s === 'completed' || s === 'success';
|
|
210
|
+
}
|
|
211
|
+
function getStepStatus(step) {
|
|
212
|
+
if (step.status)
|
|
213
|
+
return step.status;
|
|
214
|
+
if (step.error)
|
|
215
|
+
return 'error';
|
|
216
|
+
if (step.completed_at || step.completed_at_epoch_ms)
|
|
217
|
+
return 'completed';
|
|
218
|
+
if (step.started_at || step.started_at_epoch_ms)
|
|
219
|
+
return 'running';
|
|
220
|
+
return 'pending';
|
|
221
|
+
}
|
|
222
|
+
function getStepStatusColor(status) {
|
|
223
|
+
switch (status?.toLowerCase()) {
|
|
224
|
+
case 'completed':
|
|
225
|
+
case 'success':
|
|
226
|
+
return chalk_1.default.green;
|
|
227
|
+
case 'running':
|
|
228
|
+
return chalk_1.default.blue;
|
|
229
|
+
case 'pending':
|
|
230
|
+
case 'queued':
|
|
231
|
+
return chalk_1.default.yellow;
|
|
232
|
+
case 'failed':
|
|
233
|
+
case 'error':
|
|
234
|
+
return chalk_1.default.red;
|
|
235
|
+
case 'retried':
|
|
236
|
+
return chalk_1.default.yellow;
|
|
237
|
+
default:
|
|
238
|
+
return chalk_1.default.gray;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
133
241
|
/**
|
|
134
242
|
* Parse --since value into epoch ms.
|
|
135
243
|
* Accepts: "1h", "30m", "2d", "1w", or ISO date string.
|
package/dist/index.js
CHANGED
|
@@ -126,6 +126,7 @@ runCmd
|
|
|
126
126
|
.option('--since <duration>', 'Filter to runs since (e.g., 1h, 30m, 2d, 1w)')
|
|
127
127
|
.option('--workflow <name>', 'Filter by workflow name')
|
|
128
128
|
.option('--detailed', 'Include timeline, steps, and logs per run (default limit: 5)')
|
|
129
|
+
.option('--has-errors', 'Show only runs with errors (step errors, retries, or degraded results)')
|
|
129
130
|
.option('--json', 'Output as JSON')
|
|
130
131
|
.action((projectName, options) => {
|
|
131
132
|
(0, run_list_1.runs)(projectName, options);
|