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

dcdpr / jp / 16016611362

02 Jul 2025 05:08AM UTC coverage: 37.175% (-0.5%) from 37.695%
16016611362

Pull #167

github

web-flow
Merge ccd1d1741 into 11ffdeb13
Pull Request #167: chore(tools): Add filesystem tools and enhance GitHub integration

66 of 335 new or added lines in 9 files covered. (19.7%)

5 existing lines in 3 files now uncovered.

3742 of 10066 relevant lines covered (37.17%)

4.68 hits per line

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

0.0
/.config/jp/tools/src/github/create_bug_issue.rs
1
use indoc::formatdoc;
2
use url::Url;
3

4
use super::auth;
5
use crate::{
6
    github::{ORG, REPO},
7
    to_xml, Result,
8
};
9

NEW
10
pub(crate) async fn github_create_bug_issue(
×
NEW
11
    title: String,
×
NEW
12
    description: String,
×
NEW
13
    expected_behavior: String,
×
NEW
14
    actual_behavior: String,
×
NEW
15
    complexity: String,
×
NEW
16
    reproduce: Option<String>,
×
NEW
17
    proposed_solution: Option<String>,
×
NEW
18
    tasks: Option<Vec<String>>,
×
NEW
19
    resource_links: Option<Vec<String>>,
×
NEW
20
    labels: Option<Vec<String>>,
×
NEW
21
    assignees: Option<Vec<String>>,
×
NEW
22
) -> Result<String> {
×
23
    #[derive(serde::Serialize)]
24
    struct Issue {
25
        url: Url,
26
    }
27

NEW
28
    auth().await?;
×
29

NEW
30
    if assignees.as_ref().is_some_and(|v| !v.is_empty()) {
×
NEW
31
        check_assignees(assignees.as_ref()).await?;
×
NEW
32
    }
×
33

NEW
34
    if labels.as_ref().is_some_and(|v| !v.is_empty()) {
×
NEW
35
        check_labels(labels.as_ref()).await?;
×
NEW
36
    }
×
37

NEW
38
    let mut body = formatdoc!(
×
NEW
39
        "{description}
×
NEW
40

×
NEW
41
        ## Expected Behavior
×
NEW
42

×
NEW
43
        {expected_behavior}
×
NEW
44

×
NEW
45
        ## Actual Behavior
×
NEW
46

×
NEW
47
        {actual_behavior}"
×
48
    );
49

NEW
50
    if let Some(reproduce) = reproduce {
×
NEW
51
        body.push_str("\n\n## Reproduce\n\n");
×
NEW
52
        body.push_str(&reproduce);
×
NEW
53
    }
×
54

NEW
55
    if let Some(proposed_solution) = proposed_solution {
×
NEW
56
        body.push_str("\n\n## Proposed Solution\n\n");
×
NEW
57
        body.push_str(&proposed_solution);
×
NEW
58
    }
×
59

NEW
60
    if let Some(tasks) = tasks {
×
NEW
61
        body.push_str("\n\n## Tasks\n");
×
NEW
62
        body.push_str(&tasks.join("\n- [ ] "));
×
NEW
63
    }
×
64

NEW
65
    if let Some(resource_links) = resource_links {
×
NEW
66
        body.push_str("\n\n## Resources\n\n");
×
NEW
67
        body.push_str(&resource_links.join("\n"));
×
NEW
68
    }
×
69

NEW
70
    let mut labels = labels.unwrap_or_default();
×
NEW
71
    labels.push("bug".to_owned());
×
72

NEW
73
    match complexity.as_str() {
×
NEW
74
        "low" => labels.push("good first issue".to_owned()),
×
NEW
75
        "medium" | "high" => {}
×
NEW
76
        _ => return Err("Invalid complexity, must be one of `low`, `medium`, or `high`.".into()),
×
77
    }
78

NEW
79
    let issue = octocrab::instance()
×
NEW
80
        .issues(ORG, REPO)
×
NEW
81
        .create(&title)
×
NEW
82
        .body(&body)
×
NEW
83
        .labels(Some(labels))
×
NEW
84
        .assignees(assignees)
×
NEW
85
        .send()
×
NEW
86
        .await?;
×
87

NEW
88
    to_xml(Issue {
×
NEW
89
        url: issue.html_url,
×
NEW
90
    })
×
NEW
91
}
×
92

NEW
93
async fn check_labels(as_ref: Option<&Vec<String>>) -> Result<()> {
×
NEW
94
    let page = octocrab::instance()
×
NEW
95
        .issues(ORG, REPO)
×
NEW
96
        .list_labels_for_repo()
×
NEW
97
        .send()
×
NEW
98
        .await?;
×
99

NEW
100
    let labels = octocrab::instance().all_pages(page).await?;
×
101

NEW
102
    let mut invalid_labels = vec![];
×
NEW
103
    for label in as_ref.into_iter().flatten() {
×
NEW
104
        if labels.iter().any(|l| &l.name == label) {
×
NEW
105
            continue;
×
NEW
106
        }
×
107

NEW
108
        invalid_labels.push(label);
×
109
    }
110

NEW
111
    if !invalid_labels.is_empty() {
×
NEW
112
        return Err(formatdoc!(
×
NEW
113
            "The following labels do not exist on the project, and cannot be assigned to the \
×
NEW
114
             issue:
×
NEW
115

×
NEW
116
             {}
×
NEW
117

×
NEW
118
             Valid labels are:
×
NEW
119

×
NEW
120
             {}",
×
NEW
121
            invalid_labels
×
NEW
122
                .iter()
×
NEW
123
                .map(|l| format!("- {l}"))
×
NEW
124
                .collect::<Vec<_>>()
×
NEW
125
                .join("\n"),
×
NEW
126
            labels
×
NEW
127
                .iter()
×
NEW
128
                .map(|l| format!(
×
NEW
129
                    "- {}{}",
×
130
                    l.name,
NEW
131
                    l.description
×
NEW
132
                        .as_ref()
×
NEW
133
                        .map(|d| format!(" ({d})"))
×
NEW
134
                        .unwrap_or_default()
×
135
                ))
NEW
136
                .collect::<Vec<_>>()
×
NEW
137
                .join("\n")
×
138
        )
NEW
139
        .into());
×
NEW
140
    }
×
141

NEW
142
    Ok(())
×
NEW
143
}
×
144

NEW
145
async fn check_assignees(assignees: Option<&Vec<String>>) -> Result<()> {
×
NEW
146
    let page = octocrab::instance()
×
NEW
147
        .repos(ORG, REPO)
×
NEW
148
        .list_collaborators()
×
NEW
149
        .send()
×
NEW
150
        .await?;
×
151

NEW
152
    let collaborators = octocrab::instance().all_pages(page).await?;
×
153

NEW
154
    let mut invalid_assignees = vec![];
×
NEW
155
    for assignee in assignees.into_iter().flatten() {
×
NEW
156
        if collaborators.iter().any(|c| &c.author.login == assignee) {
×
NEW
157
            continue;
×
NEW
158
        }
×
159

NEW
160
        invalid_assignees.push(assignee);
×
161
    }
162

NEW
163
    if !invalid_assignees.is_empty() {
×
NEW
164
        return Err(formatdoc!(
×
NEW
165
            "The following assignees are not collaborators on the project, and cannot be assigned \
×
NEW
166
             to the issue:
×
NEW
167

×
NEW
168
             {}
×
NEW
169

×
NEW
170
             Valid assignees are:
×
NEW
171

×
NEW
172
             {}",
×
NEW
173
            invalid_assignees
×
NEW
174
                .iter()
×
NEW
175
                .map(|a| format!("- {a}"))
×
NEW
176
                .collect::<Vec<_>>()
×
NEW
177
                .join("\n"),
×
NEW
178
            collaborators
×
NEW
179
                .iter()
×
NEW
180
                .map(|c| format!("- {}", c.author.login))
×
NEW
181
                .collect::<Vec<_>>()
×
NEW
182
                .join("\n")
×
183
        )
NEW
184
        .into());
×
NEW
185
    }
×
186

NEW
187
    Ok(())
×
NEW
188
}
×
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