mirror of
https://github.com/blw1138/cross-py-builder.git
synced 2026-09-07 21:41:09 -05:00
Add controller with web UI and retire CLI
- Add ctrl/ package: Flask app (waitress), SQLite job store, background scheduler that probes configured workers, dispatches via /upload and /checkout_git, fetches artifacts, and serves a single-page UI - Supporting API: /api/jobs (create/list/detail/cancel), /api/workers, /api/capabilities, artifact download, and SSE log stream - Workers keep the existing HTTP API; add /progress/<job_id> endpoint and per-step build tracking for live status - Retire agent_manager.py and the cross-py-builder CLI; controller UI is the primary frontend - Record design in DESIGN.md; add waitress dependency Note: /progress is exposed on the worker but the synchronous /upload and /checkout endpoints block until a build completes, so per-step worker progress is not yet relayed live in the UI. Live streaming needs an async-start worker endpoint as a follow-up.
This commit is contained in:
+112
@@ -0,0 +1,112 @@
|
||||
import json
|
||||
import sqlite3
|
||||
import threading
|
||||
|
||||
import ctrl.settings as settings
|
||||
|
||||
|
||||
_local = threading.local()
|
||||
|
||||
|
||||
def _conn():
|
||||
conn = getattr(_local, "conn", None)
|
||||
if conn is None:
|
||||
conn = sqlite3.connect(settings.DB_PATH, timeout=30)
|
||||
conn.row_factory = sqlite3.Row
|
||||
_local.conn = conn
|
||||
return conn
|
||||
|
||||
|
||||
def init_db():
|
||||
settings.ensure_dirs()
|
||||
conn = _conn()
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS jobs (
|
||||
id TEXT PRIMARY KEY,
|
||||
status TEXT NOT NULL,
|
||||
source_type TEXT NOT NULL,
|
||||
source TEXT,
|
||||
os_req TEXT,
|
||||
cpu_req TEXT,
|
||||
worker_host TEXT,
|
||||
worker_job_id TEXT,
|
||||
worker_url TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
started_at TEXT,
|
||||
finished_at TEXT,
|
||||
log TEXT DEFAULT '',
|
||||
error TEXT,
|
||||
artifacts TEXT DEFAULT '[]'
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
|
||||
def create_job(source_type, source, os_req, cpu_req):
|
||||
import uuid
|
||||
import datetime
|
||||
job_id = "JOB-" + uuid.uuid4().hex[:8]
|
||||
conn = _conn()
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO jobs (id, status, source_type, source, os_req, cpu_req, created_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(job_id, "queued", source_type, source, os_req, cpu_req,
|
||||
datetime.datetime.now().isoformat()),
|
||||
)
|
||||
conn.commit()
|
||||
return job_id
|
||||
|
||||
|
||||
def _row_to_dict(row):
|
||||
d = dict(row)
|
||||
try:
|
||||
d["artifacts"] = json.loads(d.get("artifacts") or "[]")
|
||||
except (TypeError, ValueError):
|
||||
d["artifacts"] = []
|
||||
return d
|
||||
|
||||
|
||||
def get_job(job_id):
|
||||
row = _conn().execute("SELECT * FROM jobs WHERE id = ?", (job_id,)).fetchone()
|
||||
return _row_to_dict(row) if row else None
|
||||
|
||||
|
||||
def list_jobs(limit=100):
|
||||
rows = _conn().execute(
|
||||
"SELECT * FROM jobs ORDER BY created_at DESC LIMIT ?", (limit,)
|
||||
).fetchall()
|
||||
return [_row_to_dict(r) for r in rows]
|
||||
|
||||
|
||||
def update_job(job_id, **fields):
|
||||
if not fields:
|
||||
return
|
||||
cols = ", ".join(f"{k} = ?" for k in fields)
|
||||
values = []
|
||||
for v in fields.values():
|
||||
if isinstance(v, (list, dict)):
|
||||
v = json.dumps(v)
|
||||
values.append(v)
|
||||
conn = _conn()
|
||||
conn.execute(f"UPDATE jobs SET {cols} WHERE id = ?", (*values, job_id))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def append_log(job_id, text):
|
||||
conn = _conn()
|
||||
conn.execute("UPDATE jobs SET log = log || ? WHERE id = ?", (text, job_id))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def set_queued_jobs_for_worker(worker_url):
|
||||
"""Reschedule any jobs currently stuck on a worker back to queued."""
|
||||
conn = _conn()
|
||||
conn.execute(
|
||||
"UPDATE jobs SET status = 'queued', worker_url = NULL WHERE worker_url = ? AND status IN ('dispatching', 'building')",
|
||||
(worker_url,),
|
||||
)
|
||||
conn.commit()
|
||||
Reference in New Issue
Block a user