Skip to main content

ward/github/
rulesets.rs

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    /// List rulesets for a repository.
14    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    /// Create a Copilot code review ruleset.
27    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}