automatons_github/task/
create_check_run.rs

1use anyhow::Context;
2use chrono::{DateTime, Utc};
3use serde::Serialize;
4use url::Url;
5
6use automatons::Error;
7
8use crate::client::GitHubClient;
9use crate::resource::{
10    CheckRun, CheckRunConclusion, CheckRunName, CheckRunStatus, GitSha, Login, RepositoryName,
11};
12use crate::task::CheckRunOutputArgs;
13
14/// Create a check run
15///
16/// Creates a new check run for a specific commit in a repository. The GitHub App must have the
17/// `checks:write` permission to create check runs.
18///
19/// In a check suite, GitHub limits the number of check runs with the same name to 1000. Once these
20/// check runs exceed 1000, GitHub will start to automatically delete older check runs.
21///
22/// https://docs.github.com/en/rest/checks/runs#create-a-check-run
23#[derive(Copy, Clone, Debug)]
24pub struct CreateCheckRun<'a> {
25    github_client: &'a GitHubClient,
26    owner: &'a Login,
27    repository: &'a RepositoryName,
28    check_run_args: &'a CreateCheckRunArgs,
29}
30
31/// Input for create check run task
32///
33/// The input for the task that creates a check run represents the different parameters that
34/// GitHub's API accepts.
35///
36/// https://docs.github.com/en/rest/checks/runs#create-a-check-run
37#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Serialize)]
38pub struct CreateCheckRunArgs {
39    /// The name of the check. For example, "code-coverage".
40    pub name: CheckRunName,
41
42    /// The SHA of the commit.
43    pub head_sha: GitSha,
44
45    /// The URL of the integrator's site that has the full details of the check. If the integrator
46    /// does not provide this, then the homepage of the GitHub app is used.
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub details_url: Option<Url>,
49
50    /// A reference for the run on the integrator's system.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub external_id: Option<String>,
53
54    /// The current status. `queued` by default.
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub status: Option<CheckRunStatus>,
57
58    /// The time that the check run began.
59    #[serde(skip_serializing_if = "Option::is_none")]
60    pub started_at: Option<DateTime<Utc>>,
61
62    /// The final conclusion of the check.
63    ///
64    /// Required if you provide `completed_at` or a status of `completed`. Providing a conclusion
65    /// will automatically set the status parameter to `completed`. You cannot change a check run
66    /// conclusion to `stale`, only GitHub can set this.
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub conclusion: Option<CheckRunConclusion>,
69
70    /// The time the check completed.
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub completed_at: Option<DateTime<Utc>>,
73
74    /// Check runs can accept a variety of data in the output object, including a title and summary
75    /// and can optionally provide descriptive details about the run.
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub output: Option<CheckRunOutputArgs>,
78}
79
80impl<'a> CreateCheckRun<'a> {
81    /// Initializes the task
82    pub fn new(
83        github_client: &'a GitHubClient,
84        owner: &'a Login,
85        repository: &'a RepositoryName,
86        check_run_input: &'a CreateCheckRunArgs,
87    ) -> Self {
88        Self {
89            github_client,
90            owner,
91            repository,
92            check_run_args: check_run_input,
93        }
94    }
95
96    /// Create a check run
97    pub async fn execute(&self) -> Result<CheckRun, Error> {
98        let url = format!(
99            "/repos/{}/{}/check-runs",
100            self.owner.get(),
101            self.repository.get(),
102        );
103
104        let check_run = self
105            .github_client
106            .post(&url, Some(self.check_run_args))
107            .await
108            .context("failed to create check run")?;
109
110        Ok(check_run)
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use crate::resource::{CheckRunName, GitSha, Login, RepositoryName};
117    use crate::testing::check_run::mock_create_check_run;
118    use crate::testing::client::github_client;
119    use crate::testing::token::mock_installation_access_tokens;
120
121    use super::{CreateCheckRun, CreateCheckRunArgs};
122
123    fn input() -> CreateCheckRunArgs {
124        CreateCheckRunArgs {
125            name: CheckRunName::new("mighty_readme"),
126            head_sha: GitSha::new("ce587453ced02b1526dfb4cb910479d431683101"),
127            details_url: None,
128            external_id: None,
129            status: None,
130            started_at: None,
131            conclusion: None,
132            completed_at: None,
133            output: None,
134        }
135    }
136
137    #[tokio::test]
138    async fn task_returns_check_run() {
139        let _token_mock = mock_installation_access_tokens();
140        let _content_mock = mock_create_check_run();
141
142        let github_client = github_client();
143        let login = Login::new("github");
144        let repository = RepositoryName::new("hello-world");
145        let check_run_input = input();
146
147        let task = CreateCheckRun::new(&github_client, &login, &repository, &check_run_input);
148
149        let check_run = task.execute().await.unwrap();
150
151        assert_eq!(4, check_run.id().get());
152    }
153
154    #[test]
155    fn trait_send() {
156        fn assert_send<T: Send>() {}
157        assert_send::<CreateCheckRun>();
158    }
159
160    #[test]
161    fn trait_sync() {
162        fn assert_sync<T: Sync>() {}
163        assert_sync::<CreateCheckRun>();
164    }
165}