• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

heathcliff26 / cerberus-mergeguard / 15451311352

04 Jun 2025 07:46PM UTC coverage: 64.277% (-0.02%) from 64.295%
15451311352

push

github

heathcliff26
Add commit information to version output

Signed-off-by: Heathcliff <heathcliff@heathcliff.eu>

9 of 11 new or added lines in 1 file covered. (81.82%)

4 existing lines in 2 files now uncovered.

502 of 781 relevant lines covered (64.28%)

2.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.85
/src/api.rs
1
use crate::{types::*, version};
2
use reqwest::{Client, header, header::HeaderMap, header::HeaderName, header::HeaderValue};
3
use std::error::Error;
4
use tracing::{debug, info};
5

6
/// Get an installation token for the GitHub App.
7
/// API endpoint: POST /app/installations/{installation_id}/access_tokens
8
pub async fn get_installation_token(
5✔
9
    endpoint: &str,
5✔
10
    token: &str,
5✔
11
    installation_id: u64,
5✔
12
) -> Result<TokenResponse, String> {
5✔
13
    let url = format!("{endpoint}/app/installations/{installation_id}/access_tokens");
5✔
14
    info!("Fetching installation token from '{url}'");
5✔
15

16
    let client = new_client_with_common_headers(token)?;
5✔
17
    let response = send_request(client.post(&url)).await?;
5✔
18

19
    let token: TokenResponse = response
4✔
20
        .json()
4✔
21
        .await
4✔
22
        .map_err(|e| format!("Failed to parse response: {e}"))?;
4✔
23

24
    Ok(token)
4✔
25
}
5✔
26

27
/// Fetch all check runs for a commit.
28
/// API endpoint: GET /repos/{owner}/{repo}/commits/{ref}/check-runs
29
pub async fn get_check_runs(
1✔
30
    endpoint: &str,
1✔
31
    token: &str,
1✔
32
    repo: &str,
1✔
33
    commit: &str,
1✔
34
) -> Result<Vec<CheckRun>, String> {
1✔
35
    let url = format!("{endpoint}/repos/{repo}/commits/{commit}/check-runs");
1✔
36
    info!("Fetching check runs from '{url}'");
1✔
37

38
    let client = new_client_with_common_headers(token)?;
1✔
39
    let response = send_request(client.get(&url)).await?;
1✔
40
    let response = receive_body(response).await?;
1✔
41

42
    let check_runs: CheckRunsResponse = match serde_json::from_str(&response) {
1✔
43
        Ok(check_runs) => check_runs,
1✔
44
        Err(e) => {
×
45
            debug!("Response body: '{}'", response);
×
46
            return Err(format!("Failed to parse check-runs response: {e}"));
×
47
        }
48
    };
49

50
    Ok(check_runs.check_runs)
1✔
51
}
1✔
52

53
/// Create a check run for a specific commit.
54
/// API endpoint: POST /repos/{owner}/{repo}/check-runs
55
pub async fn create_check_run(
2✔
56
    endpoint: &str,
2✔
57
    token: &str,
2✔
58
    repo: &str,
2✔
59
    payload: &CheckRun,
2✔
60
) -> Result<(), String> {
2✔
61
    let url = format!("{endpoint}/repos/{repo}/check-runs");
2✔
62
    info!("Creating check-run for '{}' at '{url}'", payload.head_sha);
2✔
63

64
    let client = new_client_with_common_headers(token)?;
2✔
65
    let response = send_request(client.post(&url).json(payload)).await?;
2✔
66
    let response = receive_body(response).await?;
2✔
67

68
    match serde_json::from_str::<CheckRun>(&response) {
2✔
69
        Ok(check_run) => {
2✔
70
            info!(
2✔
71
                "Created check-run '{}' for commit '{}'",
2✔
72
                check_run.id, check_run.head_sha,
73
            );
74
            Ok(())
2✔
75
        }
76
        Err(e) => {
×
77
            debug!("Response body: '{}'", response);
×
78
            Err(format!("Failed to parse check-run response: {e}"))
×
79
        }
80
    }
81
}
2✔
82

83
/// Update a check run for a specific commit.
84
/// API endpoint: PATCH /repos/{owner}/{repo}/check-runs/{check_run_id}
85
pub async fn update_check_run(
×
86
    endpoint: &str,
×
87
    token: &str,
×
88
    repo: &str,
×
89
    payload: &CheckRun,
×
90
) -> Result<(), String> {
×
91
    let url = format!("{endpoint}/repos/{repo}/check-runs/{}", payload.id);
×
92
    info!("Updating check-run for '{}' at '{url}'", payload.head_sha);
×
93

94
    let client = new_client_with_common_headers(token)?;
×
95
    let response = send_request(client.patch(&url).json(payload)).await?;
×
96
    let response = receive_body(response).await?;
×
97

98
    match serde_json::from_str::<CheckRun>(&response) {
×
99
        Ok(check_run) => {
×
100
            info!(
×
101
                "Updated check-run '{}' for commit '{}'",
×
102
                check_run.id, check_run.head_sha,
103
            );
104
            Ok(())
×
105
        }
106
        Err(e) => {
×
107
            debug!("Response body: '{}'", response);
×
108
            Err(format!("Failed to parse check-run response: {e}"))
×
109
        }
110
    }
111
}
×
112

113
fn new_client_with_common_headers(token: &str) -> Result<Client, String> {
8✔
114
    let mut headers = HeaderMap::new();
8✔
115
    headers.insert(
8✔
116
        header::ACCEPT,
8✔
117
        HeaderValue::from_static("application/vnd.github+json"),
8✔
118
    );
8✔
119
    headers.insert(
8✔
120
        HeaderName::from_static("x-github-api-version"),
8✔
121
        HeaderValue::from_static("2022-11-28"),
8✔
122
    );
8✔
123
    headers.insert(header::USER_AGENT, HeaderValue::from_static(version::NAME));
8✔
124
    if !token.is_empty() {
8✔
125
        let bearer = format!("Bearer {token}");
8✔
126
        let bearer =
8✔
127
            HeaderValue::from_str(&bearer).map_err(|_| "Invalid bearer token".to_string())?;
8✔
128
        headers.insert(header::AUTHORIZATION, bearer);
8✔
129
    }
×
130
    Client::builder()
8✔
131
        .default_headers(headers)
8✔
132
        .build()
8✔
133
        .map_err(|e| format!("Failed to create http request: {e}"))
8✔
134
}
8✔
135

136
async fn send_request(builder: reqwest::RequestBuilder) -> Result<reqwest::Response, String> {
8✔
137
    let response = builder.send().await.map_err(|e| full_error_stack(&e))?;
8✔
138

139
    if !response.status().is_success() {
8✔
140
        let status = response.status();
1✔
141

1✔
142
        debug!(
1✔
UNCOV
143
            "Request failed with: status='{}', body='{}'",
×
UNCOV
144
            status,
×
UNCOV
145
            response.text().await.unwrap_or_default(),
×
146
        );
147
        return Err(format!("Request failed with status: {}", status));
1✔
148
    }
7✔
149
    Ok(response)
7✔
150
}
8✔
151

152
async fn receive_body(response: reqwest::Response) -> Result<String, String> {
3✔
153
    response
3✔
154
        .text()
3✔
155
        .await
3✔
156
        .map_err(|e| format!("Failed to read response body: {e}"))
3✔
157
}
3✔
158

159
fn full_error_stack(mut e: &dyn Error) -> String {
×
160
    let mut s = format!("{e}");
×
161
    while let Some(src) = e.source() {
×
162
        s.push_str(&format!(": {}", src));
×
163
        e = src;
×
164
    }
×
165
    s
×
166
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc