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

dcdpr / jp / 22156255693

18 Feb 2026 08:23PM UTC coverage: 55.288% (+1.3%) from 54.027%
22156255693

push

github

web-flow
feat(vet): add cargo vet and remove time crate (#395)

Add `cargo vet`, and remove `time`, which `cargo deny` identified as
vulnerable under
[RUSTSEC-2026-0009](/rustsec/advisory-db/blob/main/crates/time/RUSTSEC-2026-0009.md).
It also had maintenance issues, and has way too many unsafe blocks.

In order to fully excise `time`, we replaced `bat` and `octocrab`, which
necessitated the creation of `jp_github`.

Cargo binstall was also rebuilding itself every run, without proper
caching ("Cache save failed."), so it was removed. 

---------

Co-authored-by: Jay Oster <jay@blipjoy.com>

780 of 1027 new or added lines in 36 files covered. (75.95%)

3 existing lines in 3 files now uncovered.

11606 of 20992 relevant lines covered (55.29%)

117.77 hits per line

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

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

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

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

31
    auth().await?;
×
32

33
    if assignees.as_ref().is_some_and(|v| !v.is_empty()) {
×
34
        check_assignees(assignees.as_deref()).await?;
×
35
    }
×
36

37
    if labels.as_ref().is_some_and(|v| !v.is_empty()) {
×
38
        check_labels(labels.as_deref()).await?;
×
39
    }
×
40

41
    let mut body = formatdoc!(
×
42
        "{description}
43

44
        ## Expected Behavior
45

46
        {expected_behavior}
47

48
        ## Actual Behavior
49

50
        {actual_behavior}"
51
    );
52

53
    if let Some(reproduce) = reproduce {
×
54
        body.push_str("\n\n## Reproduce\n\n");
×
55
        body.push_str(&reproduce);
×
56
    }
×
57

58
    if let Some(proposed_solution) = proposed_solution {
×
59
        body.push_str("\n\n## Proposed Solution\n\n");
×
60
        body.push_str(&proposed_solution);
×
61
    }
×
62

63
    if let Some(tasks) = tasks
×
64
        && !tasks.is_empty()
×
65
    {
×
66
        body.push_str("\n\n## Tasks\n- [ ] ");
×
67
        body.push_str(&tasks.join("\n- [ ] "));
×
68
    }
×
69

70
    if let Some(resource_links) = resource_links
×
71
        && !resource_links.is_empty()
×
72
    {
73
        let resource_links = resource_links
×
74
            .into_iter()
×
75
            .map(|link| {
×
76
                if link.starts_with("http") {
×
77
                    link
×
78
                } else {
79
                    format!("- https://github.com/{ORG}/{REPO}/{link}")
×
80
                }
81
            })
×
82
            .collect::<Vec<_>>()
×
83
            .join("\n");
×
84

85
        body.push_str("\n\n## Resources\n\n");
×
86
        body.push_str(&resource_links);
×
87
    }
×
88

89
    let mut labels = labels.unwrap_or_default().into_vec();
×
90
    labels.push("bug".to_owned());
×
91

92
    match complexity.as_str() {
×
93
        "low" => labels.push("good first issue".to_owned()),
×
94
        "medium" | "high" => {}
×
95
        _ => return Err("Invalid complexity, must be one of `low`, `medium`, or `high`.".into()),
×
96
    }
97

NEW
98
    let issue = jp_github::instance()
×
99
        .issues(ORG, REPO)
×
100
        .create(&title)
×
101
        .body(&body)
×
102
        .labels(Some(labels))
×
103
        .assignees(assignees.map(Into::into))
×
104
        .send()
×
105
        .await?;
×
106

107
    to_xml(Issue {
×
108
        url: issue.html_url,
×
109
    })
×
110
}
×
111

112
async fn check_labels(as_ref: Option<&[String]>) -> Result<()> {
×
NEW
113
    let page = jp_github::instance()
×
114
        .issues(ORG, REPO)
×
115
        .list_labels_for_repo()
×
116
        .send()
×
117
        .await?;
×
118

NEW
119
    let labels = jp_github::instance().all_pages(page).await?;
×
120

121
    let mut invalid_labels = vec![];
×
122
    for label in as_ref.into_iter().flatten() {
×
123
        if labels.iter().any(|l| &l.name == label) {
×
124
            continue;
×
125
        }
×
126

127
        invalid_labels.push(label);
×
128
    }
129

130
    if !invalid_labels.is_empty() {
×
131
        return Err(formatdoc!(
×
132
            "The following labels do not exist on the project, and cannot be assigned to the \
133
             issue:
134

135
             {}
136

137
             Valid labels are:
138

139
             {}",
140
            invalid_labels
×
141
                .iter()
×
142
                .map(|l| format!("- {l}"))
×
143
                .collect::<Vec<_>>()
×
144
                .join("\n"),
×
145
            labels
×
146
                .iter()
×
147
                .map(|l| format!(
×
148
                    "- {}{}",
149
                    l.name,
150
                    l.description
×
151
                        .as_ref()
×
152
                        .map(|d| format!(" ({d})"))
×
153
                        .unwrap_or_default()
×
154
                ))
155
                .collect::<Vec<_>>()
×
156
                .join("\n")
×
157
        )
158
        .into());
×
159
    }
×
160

161
    Ok(())
×
162
}
×
163

164
async fn check_assignees(assignees: Option<&[String]>) -> Result<()> {
×
NEW
165
    let page = jp_github::instance()
×
166
        .repos(ORG, REPO)
×
167
        .list_collaborators()
×
168
        .send()
×
169
        .await?;
×
170

NEW
171
    let collaborators = jp_github::instance().all_pages(page).await?;
×
172

173
    let mut invalid_assignees = vec![];
×
174
    for assignee in assignees.into_iter().flatten() {
×
175
        if collaborators.iter().any(|c| &c.author.login == assignee) {
×
176
            continue;
×
177
        }
×
178

179
        invalid_assignees.push(assignee);
×
180
    }
181

182
    if !invalid_assignees.is_empty() {
×
183
        return Err(formatdoc!(
×
184
            "The following assignees are not collaborators on the project, and cannot be assigned \
185
             to the issue:
186

187
             {}
188

189
             Valid assignees are:
190

191
             {}",
192
            invalid_assignees
×
193
                .iter()
×
194
                .map(|a| format!("- {a}"))
×
195
                .collect::<Vec<_>>()
×
196
                .join("\n"),
×
197
            collaborators
×
198
                .iter()
×
199
                .map(|c| format!("- {}", c.author.login))
×
200
                .collect::<Vec<_>>()
×
201
                .join("\n")
×
202
        )
203
        .into());
×
204
    }
×
205

206
    Ok(())
×
207
}
×
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