1use anyhow::Result;
2use serde::Deserialize;
3
4use super::Client;
5
6#[derive(Debug, Deserialize)]
7pub struct Ruleset {
8 pub id: u64,
9 pub name: String,
10}
11
12impl Client {
13 pub async fn list_rulesets(&self, repo: &str) -> Result<Vec<Ruleset>> {
15 let resp = self
16 .get(&format!("/repos/{}/{repo}/rulesets", self.org))
17 .await?;
18
19 if !resp.status().is_success() {
20 return Ok(Vec::new());
21 }
22
23 Ok(resp.json().await.unwrap_or_default())
24 }
25
26 pub async fn create_copilot_review_ruleset(&self, repo: &str) -> Result<()> {
28 let existing = self.list_rulesets(repo).await?;
29 if existing.iter().any(|r| r.name == "Copilot Code Review") {
30 tracing::info!("Copilot review ruleset already exists for {repo}");
31 return Ok(());
32 }
33
34 let body = serde_json::json!({
35 "name": "Copilot Code Review",
36 "target": "branch",
37 "enforcement": "active",
38 "conditions": {
39 "ref_name": {
40 "include": ["~DEFAULT_BRANCH"],
41 "exclude": []
42 }
43 },
44 "rules": [{
45 "type": "copilot_code_review",
46 "parameters": {
47 "review_on_push": true
48 }
49 }],
50 "bypass_actors": []
51 });
52
53 let resp = self
54 .post_json(&format!("/repos/{}/{repo}/rulesets", self.org), &body)
55 .await?;
56
57 let status = resp.status();
58 if !status.is_success() {
59 let body = resp.text().await.unwrap_or_default();
60 anyhow::bail!(
61 "Failed to create Copilot review ruleset for {repo} (HTTP {status}): {body}"
62 );
63 }
64
65 Ok(())
66 }
67}