refactor(git_commands): migrate Chronicle emission to CloudEvents
Replace fake Forgejo push webhook construction with structured CloudEvents 1.0 via ChronicleClient. Git commit SHAs are now used as CloudEvent ids for COMMIT_CREATED and PUSH events, enabling direct correlation between Chronicle entries and git history. Event renames: - REPO_CLONED -> GOV_REPO_CLONED - COMMIT_CREATED -> GOV_COMMIT_CREATED - GOVERNED_PUSH -> GOV_PUSH - PR_CREATED -> GOV_PR_CREATED Signed-off-by: Tyler King <tking@guildhouse.dev>
This commit is contained in:
parent
cf744dd909
commit
92464b07c5
1 changed files with 55 additions and 33 deletions
|
|
@ -3,6 +3,7 @@
|
|||
//! Wraps git operations with accord validation, corpus score checks,
|
||||
//! and Chronicle attribution.
|
||||
|
||||
use crate::chronicle_client::ChronicleClient;
|
||||
use crate::session::SessionContext;
|
||||
use crate::traits::OrgCommands;
|
||||
use std::process::Command;
|
||||
|
|
@ -84,21 +85,8 @@ impl GovernedGitCommands {
|
|||
.map(|s| s.to_string())
|
||||
}
|
||||
|
||||
fn emit_chronicle(&self, kind: &str, actor_did: &str, message: &str) -> bool {
|
||||
let body = serde_json::json!({
|
||||
"pusher": {"login": actor_did},
|
||||
"ref": format!("refs/governed/{}", kind),
|
||||
"repository": {"full_name": "platform/git-governance"},
|
||||
"commits": [{"message": format!("{}: {}", kind, message)}],
|
||||
});
|
||||
reqwest::blocking::Client::new()
|
||||
.post(&self.config.chronicle_webhook)
|
||||
.header("X-Forgejo-Event", "push")
|
||||
.json(&body)
|
||||
.timeout(std::time::Duration::from_secs(5))
|
||||
.send()
|
||||
.map(|r| r.status().is_success())
|
||||
.unwrap_or(false)
|
||||
fn chronicle(&self) -> ChronicleClient {
|
||||
ChronicleClient::from_legacy_webhook(&self.config.chronicle_webhook)
|
||||
}
|
||||
|
||||
fn cmd_status(&self, _ctx: &SessionContext) -> anyhow::Result<()> {
|
||||
|
|
@ -149,12 +137,18 @@ impl GovernedGitCommands {
|
|||
if !out.is_empty() {
|
||||
println!("{}", out.trim());
|
||||
}
|
||||
self.emit_chronicle(
|
||||
"REPO_CLONED",
|
||||
&format!("did:web:{}/user/operator", ctx.trust_domain),
|
||||
&format!("repo={}", repo),
|
||||
let actor_did = format!("did:web:{}/user/operator", ctx.trust_domain);
|
||||
self.chronicle().emit(
|
||||
"GOV_REPO_CLONED",
|
||||
&actor_did,
|
||||
&ChronicleClient::generate_id(),
|
||||
serde_json::json!({
|
||||
"kind": "GOV_REPO_CLONED",
|
||||
"description": format!("repo={}", repo),
|
||||
"repo": repo,
|
||||
}),
|
||||
);
|
||||
println!("Cloned. Chronicle: REPO_CLONED");
|
||||
println!("Cloned. Chronicle: GOV_REPO_CLONED");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -226,27 +220,45 @@ impl GovernedGitCommands {
|
|||
println!(" Actor: did:web:{}/user/operator", ctx.trust_domain);
|
||||
println!("--");
|
||||
|
||||
// Emit COMMIT_CREATED for each commit in the push range (0x1704)
|
||||
// Emit GOV_COMMIT_CREATED for each commit in the push range
|
||||
let actor_did = format!("did:web:{}/user/operator", ctx.trust_domain);
|
||||
let chronicle = self.chronicle();
|
||||
let (commit_log, _, _) =
|
||||
Self::git(&["log", &format!("{}/{}..HEAD", remote, branch), "--format=%H|%s", "--no-merges"]);
|
||||
for line in commit_log.lines().filter(|l| !l.is_empty()) {
|
||||
let parts: Vec<&str> = line.splitn(2, '|').collect();
|
||||
if parts.len() >= 2 {
|
||||
self.emit_chronicle(
|
||||
"COMMIT_CREATED",
|
||||
let commit_sha = parts[0].trim();
|
||||
chronicle.emit(
|
||||
"GOV_COMMIT_CREATED",
|
||||
&actor_did,
|
||||
&format!("sha={} msg={}", parts[0], parts[1]),
|
||||
commit_sha,
|
||||
serde_json::json!({
|
||||
"kind": "GOV_COMMIT_CREATED",
|
||||
"description": format!("sha={} msg={}", commit_sha, parts[1]),
|
||||
"git_commit": commit_sha,
|
||||
"message": parts[1],
|
||||
"git_ref": format!("refs/heads/{}", branch),
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Chronicle event
|
||||
// Chronicle: GOV_PUSH
|
||||
let (sha, _, _) = Self::git(&["rev-parse", "HEAD"]);
|
||||
self.emit_chronicle(
|
||||
"GOVERNED_PUSH",
|
||||
let head_sha = sha.trim();
|
||||
chronicle.emit(
|
||||
"GOV_PUSH",
|
||||
&actor_did,
|
||||
&format!("{}@{} -> {}/{}", sha.trim(), branch, remote, branch),
|
||||
head_sha,
|
||||
serde_json::json!({
|
||||
"kind": "GOV_PUSH",
|
||||
"description": format!("{}@{} -> {}/{}", head_sha, branch, remote, branch),
|
||||
"git_commit": head_sha,
|
||||
"git_ref": format!("refs/heads/{}", branch),
|
||||
"remote": remote,
|
||||
"branch": branch,
|
||||
}),
|
||||
);
|
||||
|
||||
// Actual push
|
||||
|
|
@ -257,7 +269,7 @@ impl GovernedGitCommands {
|
|||
if !out.is_empty() {
|
||||
println!("{}", out.trim());
|
||||
}
|
||||
println!("Chronicle: GOVERNED_PUSH recorded");
|
||||
println!("Chronicle: GOV_PUSH recorded");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -296,10 +308,20 @@ impl GovernedGitCommands {
|
|||
let data: serde_json::Value = resp.json().unwrap_or_default();
|
||||
let pr_url = data["html_url"].as_str().unwrap_or("?");
|
||||
println!("PR created: {}", pr_url);
|
||||
self.emit_chronicle(
|
||||
"PR_CREATED",
|
||||
&format!("did:web:{}/user/operator", ctx.trust_domain),
|
||||
&format!("PR: {} ({})", title, pr_url),
|
||||
let actor_did = format!("did:web:{}/user/operator", ctx.trust_domain);
|
||||
let (sha, _, _) = Self::git(&["rev-parse", "HEAD"]);
|
||||
self.chronicle().emit(
|
||||
"GOV_PR_CREATED",
|
||||
&actor_did,
|
||||
sha.trim(),
|
||||
serde_json::json!({
|
||||
"kind": "GOV_PR_CREATED",
|
||||
"description": format!("PR: {} ({})", title, pr_url),
|
||||
"git_commit": sha.trim(),
|
||||
"branch": branch,
|
||||
"pr_url": pr_url,
|
||||
"title": title,
|
||||
}),
|
||||
);
|
||||
}
|
||||
Ok(resp) => eprintln!("PR creation failed: {}", resp.status()),
|
||||
|
|
|
|||
Loading…
Reference in a new issue