feat(workflow): add health check status detector
This commit is contained in:
parent
61846b5d2a
commit
0d61ac70fc
|
|
@ -11,7 +11,8 @@
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
"migrate": "ts-node-esm src/backend/db/migrate.ts",
|
"migrate": "ts-node-esm src/backend/db/migrate.ts",
|
||||||
"workflow:run": "ts-node scripts/run-workflow.ts",
|
"workflow:run": "ts-node scripts/run-workflow.ts",
|
||||||
"workflow:schedule": "ts-node scripts/schedule-workflow.ts"
|
"workflow:schedule": "ts-node scripts/schedule-workflow.ts",
|
||||||
|
"workflow:health-check": "ts-node scripts/check-workflow-health.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
#!/usr/bin/env ts-node
|
||||||
|
import * as path from 'path';
|
||||||
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
|
type WorkflowOverallStatus = 'idle' | 'running' | 'blocked' | 'failed' | 'completed';
|
||||||
|
|
||||||
|
type WorkflowStatusFile = {
|
||||||
|
overallStatus?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DEFAULT_STATUS_PATH = path.join(process.cwd(), 'status', 'workflow-status.json');
|
||||||
|
const HEALTHY_STATES = new Set<WorkflowOverallStatus>(['running', 'completed', 'idle']);
|
||||||
|
const UNHEALTHY_STATES = new Set<WorkflowOverallStatus>(['failed', 'blocked']);
|
||||||
|
|
||||||
|
function getStatusPathFromArgs(argv: string[]): string {
|
||||||
|
for (let i = 0; i < argv.length; i += 1) {
|
||||||
|
if (argv[i] === '--status' && argv[i + 1]) {
|
||||||
|
return argv[i + 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return DEFAULT_STATUS_PATH;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function readOverallStatus(statusPath: string): Promise<string> {
|
||||||
|
const raw = await fs.readFile(statusPath, 'utf8');
|
||||||
|
const parsed = JSON.parse(raw) as WorkflowStatusFile;
|
||||||
|
return String(parsed.overallStatus ?? '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkWorkflowHealth(statusPath = DEFAULT_STATUS_PATH): Promise<number> {
|
||||||
|
try {
|
||||||
|
const overallStatus = await readOverallStatus(statusPath);
|
||||||
|
|
||||||
|
if (HEALTHY_STATES.has(overallStatus as WorkflowOverallStatus)) {
|
||||||
|
console.log(JSON.stringify({ ok: true, status: overallStatus }));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UNHEALTHY_STATES.has(overallStatus as WorkflowOverallStatus)) {
|
||||||
|
console.log(JSON.stringify({ ok: false, status: overallStatus }));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(JSON.stringify({ ok: false, status: overallStatus || 'unknown', error: 'unknown_status' }));
|
||||||
|
return 1;
|
||||||
|
} catch (error) {
|
||||||
|
const message = error instanceof Error ? error.message : String(error);
|
||||||
|
console.log(JSON.stringify({ ok: false, error: 'status_read_failed', message }));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
|
const statusPath = getStatusPathFromArgs(process.argv.slice(2));
|
||||||
|
checkWorkflowHealth(statusPath).then((code) => {
|
||||||
|
process.exitCode = code;
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue