Skip to content

Commit 3f3e55f

Browse files
authored
Add labels to issues and PRs (#497)
Closes #396 Signed-off-by: Sergio Castaño Arteaga <tegioz@icloud.com>
1 parent 69db07e commit 3f3e55f

4 files changed

Lines changed: 163 additions & 12 deletions

File tree

src/cfg.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::github::{DynGH, File, TeamSlug, UserName};
2-
use anyhow::{format_err, Result};
2+
use anyhow::{bail, Result};
33
use ignore::gitignore::GitignoreBuilder;
44
use serde::{Deserialize, Serialize};
55
use std::{collections::HashMap, time::Duration};
@@ -119,7 +119,7 @@ impl CfgProfile {
119119
.and_then(|allowed_voters| allowed_voters.teams.as_ref())
120120
{
121121
if !teams.is_empty() {
122-
return Err(format_err!(ERR_TEAMS_NOT_ALLOWED));
122+
bail!(ERR_TEAMS_NOT_ALLOWED);
123123
}
124124
}
125125
}

src/github.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,16 @@ pub(crate) type UserName = String;
3939
#[async_trait]
4040
#[cfg_attr(test, automock)]
4141
pub(crate) trait GH {
42+
/// Add labels to the provided issue.
43+
async fn add_labels(
44+
&self,
45+
inst_id: u64,
46+
owner: &str,
47+
repo: &str,
48+
issue_number: i64,
49+
labels: &[&str],
50+
) -> Result<()>;
51+
4252
/// Create a check run for the head commit in the provided pull request.
4353
async fn create_check_run(
4454
&self,
@@ -119,6 +129,16 @@ pub(crate) trait GH {
119129
body: &str,
120130
) -> Result<CommentId>;
121131

132+
/// Remove label from the provided issue.
133+
async fn remove_label(
134+
&self,
135+
inst_id: u64,
136+
owner: &str,
137+
repo: &str,
138+
issue_number: i64,
139+
label: &str,
140+
) -> Result<()>;
141+
122142
/// Check if the user given is a collaborator of the provided repository.
123143
async fn user_is_collaborator(
124144
&self,
@@ -150,6 +170,27 @@ impl GHApi {
150170

151171
#[async_trait]
152172
impl GH for GHApi {
173+
/// [GH::add_labels]
174+
async fn add_labels(
175+
&self,
176+
inst_id: u64,
177+
owner: &str,
178+
repo: &str,
179+
issue_number: i64,
180+
labels: &[&str],
181+
) -> Result<()> {
182+
let client = self.app_client.installation(InstallationId(inst_id));
183+
let labels = labels
184+
.iter()
185+
.map(ToString::to_string)
186+
.collect::<Vec<String>>();
187+
client
188+
.issues(owner, repo)
189+
.add_labels(issue_number as u64, &labels)
190+
.await?;
191+
Ok(())
192+
}
193+
153194
/// [GH::create_check_run]
154195
async fn create_check_run(
155196
&self,
@@ -383,6 +424,23 @@ impl GH for GHApi {
383424
Ok(comment.id.0 as i64)
384425
}
385426

427+
/// [GH::remove_label]
428+
async fn remove_label(
429+
&self,
430+
inst_id: u64,
431+
owner: &str,
432+
repo: &str,
433+
issue_number: i64,
434+
label: &str,
435+
) -> Result<()> {
436+
let client = self.app_client.installation(InstallationId(inst_id));
437+
client
438+
.issues(owner, repo)
439+
.remove_label(issue_number as u64, label)
440+
.await?;
441+
Ok(())
442+
}
443+
386444
/// [GH::user_is_collaborator]
387445
async fn user_is_collaborator(
388446
&self,

src/processor.rs

Lines changed: 101 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ const STATUS_CHECK_FREQUENCY: Duration = Duration::from_secs(60 * 30);
4040
/// How often we try to auto close votes that have already passed.
4141
const AUTO_CLOSE_FREQUENCY: Duration = Duration::from_secs(60 * 60 * 24);
4242

43+
/// Label used to tag issues/prs where a vote has been created.
44+
const GITVOTE_LABEL: &str = "gitvote";
45+
46+
/// Label used to tag issues/prs where a vote is open.
47+
const VOTE_OPEN_LABEL: &str = "vote open";
48+
4349
/// A votes processor is in charge of creating the requested votes, stopping
4450
/// them at the scheduled time and publishing results, etc.
4551
pub(crate) struct Processor {
@@ -313,6 +319,17 @@ impl Processor {
313319
.await?;
314320
}
315321

322+
// Add "vote open" label to issue/pr
323+
self.gh
324+
.add_labels(
325+
inst_id,
326+
owner,
327+
repo,
328+
i.issue_number,
329+
&[GITVOTE_LABEL, VOTE_OPEN_LABEL],
330+
)
331+
.await?;
332+
316333
debug!(?vote_id, "created");
317334
Ok(())
318335
}
@@ -352,15 +369,23 @@ impl Processor {
352369
.post_comment(inst_id, owner, repo, i.issue_number, &body)
353370
.await?;
354371

355-
// Create check run if needed
356-
if cancelled_vote_id.is_some() && i.is_pull_request {
357-
let check_details = CheckDetails {
358-
status: "completed".to_string(),
359-
conclusion: Some("success".to_string()),
360-
summary: "Vote cancelled".to_string(),
361-
};
372+
// Create check run and remove "vote open" label if the vote was cancelled
373+
if cancelled_vote_id.is_some() {
374+
// Create check run if the vote is on a pull request
375+
if i.is_pull_request {
376+
let check_details = CheckDetails {
377+
status: "completed".to_string(),
378+
conclusion: Some("success".to_string()),
379+
summary: "Vote cancelled".to_string(),
380+
};
381+
self.gh
382+
.create_check_run(inst_id, owner, repo, i.issue_number, &check_details)
383+
.await?;
384+
}
385+
386+
// Remove "vote open" label from issue/pr
362387
self.gh
363-
.create_check_run(inst_id, owner, repo, i.issue_number, &check_details)
388+
.remove_label(inst_id, owner, repo, i.issue_number, VOTE_OPEN_LABEL)
364389
.await?;
365390
}
366391

@@ -469,6 +494,11 @@ impl Processor {
469494
.await?;
470495
}
471496

497+
// Remove "vote open" label from issue/pr
498+
self.gh
499+
.remove_label(inst_id, owner, repo, vote.issue_number, VOTE_OPEN_LABEL)
500+
.await?;
501+
472502
debug!("closed");
473503
Ok(Some(()))
474504
}
@@ -834,6 +864,15 @@ mod tests {
834864
&& body == expected_body.as_str()
835865
})
836866
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(COMMENT_ID))));
867+
gh.expect_add_labels()
868+
.withf(|inst_id, owner, repo, issue_number, labels| {
869+
*inst_id == INST_ID
870+
&& owner == ORG
871+
&& repo == REPO
872+
&& *issue_number == ISSUE_NUM
873+
&& labels == vec![GITVOTE_LABEL, VOTE_OPEN_LABEL]
874+
})
875+
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
837876

838877
let votes_processor = Processor::new(Arc::new(db), Arc::new(gh));
839878
votes_processor
@@ -899,6 +938,15 @@ mod tests {
899938
}),
900939
)
901940
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
941+
gh.expect_add_labels()
942+
.withf(|inst_id, owner, repo, issue_number, labels| {
943+
*inst_id == INST_ID
944+
&& owner == ORG
945+
&& repo == REPO
946+
&& *issue_number == ISSUE_NUM
947+
&& labels == vec![GITVOTE_LABEL, VOTE_OPEN_LABEL]
948+
})
949+
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
902950

903951
let votes_processor = Processor::new(Arc::new(db), Arc::new(gh));
904952
votes_processor
@@ -1007,6 +1055,15 @@ mod tests {
10071055
&& body == expected_body.as_str()
10081056
})
10091057
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(COMMENT_ID))));
1058+
gh.expect_remove_label()
1059+
.with(
1060+
eq(INST_ID),
1061+
eq(ORG),
1062+
eq(REPO),
1063+
eq(ISSUE_NUM),
1064+
eq(VOTE_OPEN_LABEL),
1065+
)
1066+
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
10101067
let event = setup_test_issue_comment_event();
10111068

10121069
let votes_processor = Processor::new(Arc::new(db), Arc::new(gh));
@@ -1049,6 +1106,15 @@ mod tests {
10491106
}),
10501107
)
10511108
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
1109+
gh.expect_remove_label()
1110+
.with(
1111+
eq(INST_ID),
1112+
eq(ORG),
1113+
eq(REPO),
1114+
eq(ISSUE_NUM),
1115+
eq(VOTE_OPEN_LABEL),
1116+
)
1117+
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
10521118
let event = setup_test_pr_event();
10531119

10541120
let votes_processor = Processor::new(Arc::new(db), Arc::new(gh));
@@ -1216,6 +1282,15 @@ mod tests {
12161282
&& body == expected_body.as_str()
12171283
})
12181284
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(COMMENT_ID))));
1285+
gh.expect_remove_label()
1286+
.with(
1287+
eq(INST_ID),
1288+
eq(ORG),
1289+
eq(REPO),
1290+
eq(ISSUE_NUM),
1291+
eq(VOTE_OPEN_LABEL),
1292+
)
1293+
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
12191294

12201295
let votes_processor = Processor::new(Arc::new(db), Arc::new(gh));
12211296
votes_processor.close_finished_vote().await.unwrap();
@@ -1256,6 +1331,15 @@ mod tests {
12561331
}),
12571332
)
12581333
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
1334+
gh.expect_remove_label()
1335+
.with(
1336+
eq(INST_ID),
1337+
eq(ORG),
1338+
eq(REPO),
1339+
eq(ISSUE_NUM),
1340+
eq(VOTE_OPEN_LABEL),
1341+
)
1342+
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
12591343

12601344
let votes_processor = Processor::new(Arc::new(db), Arc::new(gh));
12611345
votes_processor.close_finished_vote().await.unwrap();
@@ -1300,6 +1384,15 @@ mod tests {
13001384
}),
13011385
)
13021386
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
1387+
gh.expect_remove_label()
1388+
.with(
1389+
eq(INST_ID),
1390+
eq(ORG),
1391+
eq(REPO),
1392+
eq(ISSUE_NUM),
1393+
eq(VOTE_OPEN_LABEL),
1394+
)
1395+
.returning(|_, _, _, _, _| Box::pin(future::ready(Ok(()))));
13031396

13041397
let votes_processor = Processor::new(Arc::new(db), Arc::new(gh));
13051398
votes_processor.close_finished_vote().await.unwrap();

src/results.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::{
22
cfg::CfgProfile,
33
github::{DynGH, UserName},
44
};
5-
use anyhow::{format_err, Result};
5+
use anyhow::{bail, Result};
66
use serde::{Deserialize, Serialize};
77
use std::{collections::HashMap, fmt};
88
use time::{format_description::well_known::Rfc3339, OffsetDateTime};
@@ -76,7 +76,7 @@ impl VoteOption {
7676
REACTION_IN_FAVOR => Self::InFavor,
7777
REACTION_AGAINST => Self::Against,
7878
REACTION_ABSTAIN => Self::Abstain,
79-
_ => return Err(format_err!("reaction not supported")),
79+
_ => bail!("reaction not supported"),
8080
};
8181
Ok(vote_option)
8282
}

0 commit comments

Comments
 (0)