kin: KIN-083 Healthcheck claude CLI auth: перед запуском pipeline проверять что claude залогинен (быстрый claude -p 'ok' --output-format json, проверить is_error и 'Not logged in'). Если не залогинен — не запускать pipeline, а показать ошибку 'Claude CLI requires login' в GUI с инструкцией.
This commit is contained in:
parent
a80679ae72
commit
bfc8f1c0bb
18 changed files with 1390 additions and 57 deletions
34
core/db.py
34
core/db.py
|
|
@ -457,13 +457,20 @@ def _seed_default_hooks(conn: sqlite3.Connection):
|
|||
Creates rebuild-frontend hook only when:
|
||||
- project 'kin' exists in the projects table
|
||||
- the hook doesn't already exist (no duplicate)
|
||||
|
||||
Also updates existing hooks to the correct command/config if outdated.
|
||||
"""
|
||||
kin_exists = conn.execute(
|
||||
"SELECT 1 FROM projects WHERE id = 'kin'"
|
||||
kin_row = conn.execute(
|
||||
"SELECT path FROM projects WHERE id = 'kin'"
|
||||
).fetchone()
|
||||
if not kin_exists:
|
||||
if not kin_row or not kin_row["path"]:
|
||||
return
|
||||
|
||||
_PROJECT_PATH = kin_row["path"].rstrip("/")
|
||||
_REBUILD_SCRIPT = f"{_PROJECT_PATH}/scripts/rebuild-frontend.sh"
|
||||
_REBUILD_TRIGGER = "web/frontend/*"
|
||||
_REBUILD_WORKDIR = _PROJECT_PATH
|
||||
|
||||
exists = conn.execute(
|
||||
"SELECT 1 FROM hooks"
|
||||
" WHERE project_id = 'kin'"
|
||||
|
|
@ -472,12 +479,25 @@ def _seed_default_hooks(conn: sqlite3.Connection):
|
|||
).fetchone()
|
||||
if not exists:
|
||||
conn.execute(
|
||||
"""INSERT INTO hooks (project_id, name, event, command, enabled)
|
||||
"""INSERT INTO hooks
|
||||
(project_id, name, event, trigger_module_path, command,
|
||||
working_dir, timeout_seconds, enabled)
|
||||
VALUES ('kin', 'rebuild-frontend', 'pipeline_completed',
|
||||
'cd /Users/grosfrumos/projects/kin/web/frontend && npm run build',
|
||||
1)"""
|
||||
?, ?, ?, 300, 1)""",
|
||||
(_REBUILD_TRIGGER, _REBUILD_SCRIPT, _REBUILD_WORKDIR),
|
||||
)
|
||||
conn.commit()
|
||||
else:
|
||||
# Migrate existing hook: set trigger_module_path, correct command, working_dir
|
||||
conn.execute(
|
||||
"""UPDATE hooks
|
||||
SET trigger_module_path = ?,
|
||||
command = ?,
|
||||
working_dir = ?,
|
||||
timeout_seconds = 300
|
||||
WHERE project_id = 'kin' AND name = 'rebuild-frontend'""",
|
||||
(_REBUILD_TRIGGER, _REBUILD_SCRIPT, _REBUILD_WORKDIR),
|
||||
)
|
||||
conn.commit()
|
||||
|
||||
# Enable autocommit for kin project (opt-in, idempotent)
|
||||
conn.execute(
|
||||
|
|
|
|||
|
|
@ -115,9 +115,14 @@ def run_hooks(
|
|||
task_id: str | None,
|
||||
event: str,
|
||||
task_modules: list[dict],
|
||||
changed_files: list[str] | None = None,
|
||||
) -> list[HookResult]:
|
||||
"""Run matching hooks for the given event and module list.
|
||||
|
||||
If changed_files is provided, trigger_module_path is matched against
|
||||
the actual git-changed file paths (more precise than task_modules).
|
||||
Falls back to task_modules matching when changed_files is None.
|
||||
|
||||
Never raises — hook failures are logged but don't affect the pipeline.
|
||||
"""
|
||||
hooks = get_hooks(conn, project_id, event=event)
|
||||
|
|
@ -125,10 +130,13 @@ def run_hooks(
|
|||
for hook in hooks:
|
||||
if hook["trigger_module_path"] is not None:
|
||||
pattern = hook["trigger_module_path"]
|
||||
matched = any(
|
||||
fnmatch.fnmatch(m.get("path", ""), pattern)
|
||||
for m in task_modules
|
||||
)
|
||||
if changed_files is not None:
|
||||
matched = any(fnmatch.fnmatch(f, pattern) for f in changed_files)
|
||||
else:
|
||||
matched = any(
|
||||
fnmatch.fnmatch(m.get("path", ""), pattern)
|
||||
for m in task_modules
|
||||
)
|
||||
if not matched:
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -99,6 +99,15 @@ def get_project(conn: sqlite3.Connection, id: str) -> dict | None:
|
|||
return _row_to_dict(row)
|
||||
|
||||
|
||||
def delete_project(conn: sqlite3.Connection, id: str) -> None:
|
||||
"""Delete a project and all its related data (modules, decisions, tasks)."""
|
||||
# Delete tables that have FK references to tasks BEFORE deleting tasks
|
||||
for table in ("modules", "agent_logs", "decisions", "pipelines", "tasks"):
|
||||
conn.execute(f"DELETE FROM {table} WHERE project_id = ?", (id,))
|
||||
conn.execute("DELETE FROM projects WHERE id = ?", (id,))
|
||||
conn.commit()
|
||||
|
||||
|
||||
def get_effective_mode(conn: sqlite3.Connection, project_id: str, task_id: str) -> str:
|
||||
"""Return effective execution mode: 'auto' or 'review'.
|
||||
|
||||
|
|
@ -381,17 +390,26 @@ def add_module(
|
|||
) -> dict:
|
||||
"""Register a project module."""
|
||||
cur = conn.execute(
|
||||
"""INSERT INTO modules (project_id, name, type, path, description,
|
||||
"""INSERT OR IGNORE INTO modules (project_id, name, type, path, description,
|
||||
owner_role, dependencies)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
||||
(project_id, name, type, path, description, owner_role,
|
||||
_json_encode(dependencies)),
|
||||
)
|
||||
created = cur.rowcount > 0
|
||||
conn.commit()
|
||||
row = conn.execute(
|
||||
"SELECT * FROM modules WHERE id = ?", (cur.lastrowid,)
|
||||
).fetchone()
|
||||
return _row_to_dict(row)
|
||||
if cur.lastrowid:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM modules WHERE id = ?", (cur.lastrowid,)
|
||||
).fetchone()
|
||||
else:
|
||||
row = conn.execute(
|
||||
"SELECT * FROM modules WHERE project_id = ? AND name = ?",
|
||||
(project_id, name),
|
||||
).fetchone()
|
||||
result = _row_to_dict(row)
|
||||
result["_created"] = created
|
||||
return result
|
||||
|
||||
|
||||
def get_modules(conn: sqlite3.Connection, project_id: str) -> list[dict]:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue