feat(#1167): hive-wide knowledge repo — forge, local clone, bind-mount, webhook
This commit is contained in:
parent
4bb0d7f310
commit
41befe3839
6 changed files with 276 additions and 0 deletions
|
|
@ -94,6 +94,7 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
|
|||
)
|
||||
.route("/dashboard/stream", get(dashboard_stream))
|
||||
.route("/dashboard/history", get(dashboard_history))
|
||||
.route("/webhook/knowledge", post(post_webhook_knowledge))
|
||||
// Anything not matched by the dynamic routes above falls
|
||||
// through to the bundled dashboard dist (GET / →
|
||||
// dist/index.html, /favicon.svg → dist/favicon.svg,
|
||||
|
|
@ -2969,3 +2970,57 @@ async fn get_approval_diff(
|
|||
fn plain_text(body: String) -> Response {
|
||||
(StatusCode::OK, body).into_response()
|
||||
}
|
||||
|
||||
/// Minimal Forgejo push-webhook payload — only the fields we care about.
|
||||
#[derive(Deserialize)]
|
||||
struct PushWebhookPayload {
|
||||
#[serde(rename = "ref")]
|
||||
git_ref: Option<String>,
|
||||
repository: Option<PushWebhookRepo>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PushWebhookRepo {
|
||||
full_name: Option<String>,
|
||||
}
|
||||
|
||||
/// POST `/webhook/knowledge` — Forgejo push webhook for
|
||||
/// `internal/hive-knowledge`. Runs `git pull` on the local clone so
|
||||
/// agents see up-to-date documents on their next turn.
|
||||
///
|
||||
/// Expected Forgejo webhook configuration:
|
||||
/// - URL: `http://127.0.0.1:<dashboard_port>/webhook/knowledge`
|
||||
/// - Event: "Push" (fires on merge commits to main as well)
|
||||
///
|
||||
/// No signature verification for now; the endpoint is loopback-only
|
||||
/// and only triggers a read-only `git pull` on an operator-curated repo.
|
||||
async fn post_webhook_knowledge(
|
||||
axum::extract::Json(payload): axum::extract::Json<PushWebhookPayload>,
|
||||
) -> Response {
|
||||
let expected_repo = format!(
|
||||
"{}/{}",
|
||||
crate::knowledge::ORG,
|
||||
crate::knowledge::REPO
|
||||
);
|
||||
let full_name = payload
|
||||
.repository
|
||||
.as_ref()
|
||||
.and_then(|r| r.full_name.as_deref())
|
||||
.unwrap_or("");
|
||||
if full_name != expected_repo {
|
||||
tracing::debug!(full_name, "webhook/knowledge: ignoring push from unexpected repo");
|
||||
return (StatusCode::OK, "ignored").into_response();
|
||||
}
|
||||
let git_ref = payload.git_ref.as_deref().unwrap_or("");
|
||||
if git_ref != "refs/heads/main" {
|
||||
tracing::debug!(git_ref, "webhook/knowledge: ignoring non-main push");
|
||||
return (StatusCode::OK, "ignored").into_response();
|
||||
}
|
||||
tracing::info!("webhook/knowledge: pull triggered by push to {expected_repo}");
|
||||
tokio::spawn(async {
|
||||
if let Err(e) = crate::knowledge::pull().await {
|
||||
tracing::warn!(error = ?e, "webhook/knowledge: pull failed");
|
||||
}
|
||||
});
|
||||
(StatusCode::OK, "ok").into_response()
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue