Files
Clément SAILLANT 5efb380da5 feat: automate yiacad PR review lane (#18)
* feat: automate yiacad pr review lane

* fix: stabilize yiacad review and spec gates

* fix: scope kicad exports to repo hardware

* fix: ignore kicad block library sources in exports

* feat: Add infra VPS monitoring runbook and healthcheck scripts

- Created INFRA_VPS_RUNBOOK_2026.md detailing operational procedures for monitoring VPS services.
- Added infra_vps_healthcheck.sh script for automated health checks on DNS, TLS, TCP, and HTTP for VPS services.
- Introduced infra_vps_security_audit.sh for non-intrusive security checks on external VPS services.
- Established JSON schema for infra VPS inventory in infra_vps.schema.json.
- Developed integration for runtime status reporting in the Next.js API route.
- Implemented Playwright tests for smoke testing the application and ensuring core functionalities.
- Updated Makefile for development dependencies and testing commands.
- Created various test files for unit and end-to-end testing across different components.

* feat(agentics): update mesh agents, gates, prompts, and workflows
2026-03-29 17:14:16 +02:00

131 lines
3.2 KiB
TypeScript

import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
import { extname, join, resolve } from "node:path";
import { enqueueEdaJob } from "@/lib/eda-queue";
type CiRun = {
id: string;
pipeline: string;
engine: string;
status: string;
summary: string;
degradedReasons: string[];
artifactCount: number;
queuedAt: string;
startedAt: string | null;
completedAt: string | null;
};
const APP_ROOT = process.cwd();
const PROJECT_ROOT = join(APP_ROOT, "project");
const CI_ROOT = join(PROJECT_ROOT, ".ci");
const CI_RUNS_FILE = join(CI_ROOT, "runs.json");
async function readJson<T>(path: string, fallback: T): Promise<T> {
try {
return JSON.parse(await readFile(path, "utf8")) as T;
} catch {
return fallback;
}
}
async function writeCiRuns(runs: CiRun[]) {
await mkdir(CI_ROOT, { recursive: true });
await writeFile(CI_RUNS_FILE, JSON.stringify(runs, null, 2), "utf8");
}
function pipelineEngine(pipeline: string) {
switch (pipeline) {
case "kicad-headless":
case "step-export":
return "kicad";
case "kibot":
return "kibot";
case "kiauto-checks":
return "kiauto";
default:
return "yiacad";
}
}
async function findFirstProjectFilePath(
absoluteDir: string,
extensions: string[]
): Promise<string | null> {
async function walk(dir: string): Promise<string | null> {
const items = await readdir(dir, { withFileTypes: true });
for (const item of items.sort((left, right) => left.name.localeCompare(right.name))) {
const nextPath = join(dir, item.name);
if (item.isDirectory()) {
const nested = await walk(nextPath);
if (nested) {
return nested;
}
continue;
}
if (extensions.includes(extname(item.name).toLowerCase())) {
return resolve(nextPath);
}
}
return null;
}
return walk(absoluteDir);
}
export async function enqueueCi(pipeline: string) {
const nextRun: CiRun = {
id: `${Date.now()}`,
pipeline,
engine: pipelineEngine(pipeline),
status: "queued",
summary: `Queued ${pipeline} in the YiACAD CI orchestrator.`,
degradedReasons: [],
artifactCount: 0,
queuedAt: new Date().toISOString(),
startedAt: null,
completedAt: null
};
const queue = await readJson<CiRun[]>(CI_RUNS_FILE, []);
await writeCiRuns([nextRun, ...queue]);
try {
await enqueueEdaJob({
runId: nextRun.id,
pipeline: pipeline as
| "kicad-headless"
| "kibot"
| "kiauto-checks"
| "step-export",
projectRoot: PROJECT_ROOT,
boardPath: await findFirstProjectFilePath(PROJECT_ROOT, [".kicad_pcb"]),
schematicPath: await findFirstProjectFilePath(PROJECT_ROOT, [".kicad_sch"]),
freecadDocumentPath: await findFirstProjectFilePath(PROJECT_ROOT, [".fcstd"])
});
} catch (error) {
await writeCiRuns([
{
...nextRun,
status: "queue_failed",
summary:
error instanceof Error
? `Queue enqueue failed: ${error.message}`
: "Queue enqueue failed."
},
...queue
]);
if (error instanceof Error) {
throw new Error(`Redis queue enqueue failed: ${error.message}`);
}
throw new Error("Redis queue enqueue failed.");
}
return nextRun;
}