Skip to content

Commit a918c03

Browse files
Run commands on just-opened issue bodies
1 parent 89cd553 commit a918c03

File tree

5 files changed

+117
-51
lines changed

5 files changed

+117
-51
lines changed

src/github.rs

+54
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ pub struct Issue {
5555
pub number: u64,
5656
pub body: String,
5757
title: String,
58+
html_url: String,
5859
user: User,
5960
labels: Vec<Label>,
6061
assignees: Vec<User>,
@@ -280,6 +281,34 @@ pub struct IssueCommentEvent {
280281
pub repository: Repository,
281282
}
282283

284+
#[derive(PartialEq, Eq, Debug, serde::Deserialize)]
285+
#[serde(rename_all = "lowercase")]
286+
pub enum IssuesAction {
287+
Opened,
288+
Edited,
289+
Deleted,
290+
Transferred,
291+
Pinned,
292+
Unpinned,
293+
Closed,
294+
Reopened,
295+
Assigned,
296+
Unassigned,
297+
Labeled,
298+
Unlabeled,
299+
Locked,
300+
Unlocked,
301+
Milestoned,
302+
Demilestoned,
303+
}
304+
305+
#[derive(Debug, serde::Deserialize)]
306+
pub struct IssuesEvent {
307+
pub action: IssuesAction,
308+
pub issue: Issue,
309+
pub repository: Repository,
310+
}
311+
283312
#[derive(Debug, serde::Deserialize)]
284313
pub struct Repository {
285314
pub full_name: String,
@@ -288,18 +317,43 @@ pub struct Repository {
288317
#[derive(Debug)]
289318
pub enum Event {
290319
IssueComment(IssueCommentEvent),
320+
Issue(IssuesEvent),
291321
}
292322

293323
impl Event {
294324
pub fn repo_name(&self) -> &str {
295325
match self {
296326
Event::IssueComment(event) => &event.repository.full_name,
327+
Event::Issue(event) => &event.repository.full_name,
297328
}
298329
}
299330

300331
pub fn issue(&self) -> Option<&Issue> {
301332
match self {
302333
Event::IssueComment(event) => Some(&event.issue),
334+
Event::Issue(event) => Some(&event.issue),
335+
}
336+
}
337+
338+
/// This will both extract from IssueComment events but also Issue events
339+
pub fn comment_body(&self) -> Option<&str> {
340+
match self {
341+
Event::Issue(e) => Some(&e.issue.body),
342+
Event::IssueComment(e) => Some(&e.comment.body),
343+
}
344+
}
345+
346+
pub fn html_url(&self) -> Option<&str> {
347+
match self {
348+
Event::Issue(e) => Some(&e.issue.html_url),
349+
Event::IssueComment(e) => Some(&e.comment.html_url),
350+
}
351+
}
352+
353+
pub fn user(&self) -> &User {
354+
match self {
355+
Event::Issue(e) => &e.issue.user,
356+
Event::IssueComment(e) => &e.comment.user,
303357
}
304358
}
305359
}

src/handlers/assign.rs

+23-23
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,28 @@ impl Handler for AssignmentHandler {
3333
type Config = AssignConfig;
3434

3535
fn parse_input(&self, ctx: &Context, event: &Event) -> Result<Option<Self::Input>, Error> {
36-
#[allow(irrefutable_let_patterns)]
37-
let event = if let Event::IssueComment(e) = event {
38-
e
36+
let body = if let Some(b) = event.comment_body() {
37+
b
3938
} else {
4039
// not interested in other events
4140
return Ok(None);
4241
};
4342

44-
let mut input = Input::new(&event.comment.body, &ctx.username);
43+
if let Event::Issue(e) = event {
44+
if e.action != github::IssuesAction::Opened {
45+
// skip events other than opening the issue to avoid retriggering commands in the
46+
// issue body
47+
return Ok(None);
48+
}
49+
}
50+
51+
let mut input = Input::new(&body, &ctx.username);
4552
match input.parse_command() {
4653
Command::Assign(Ok(command)) => Ok(Some(command)),
4754
Command::Assign(Err(err)) => {
4855
failure::bail!(
4956
"Parsing assign command in [comment]({}) failed: {}",
50-
event.comment.html_url,
57+
event.html_url().expect("has html url"),
5158
err
5259
);
5360
}
@@ -62,27 +69,18 @@ impl Handler for AssignmentHandler {
6269
event: &Event,
6370
cmd: AssignCommand,
6471
) -> Result<(), Error> {
65-
#[allow(irrefutable_let_patterns)]
66-
let event = if let Event::IssueComment(e) = event {
67-
e
72+
let is_team_member = if let Err(_) | Ok(false) = event.user().is_team_member(&ctx.github) {
73+
false
6874
} else {
69-
// not interested in other events
70-
return Ok(());
75+
true
7176
};
7277

73-
let is_team_member =
74-
if let Err(_) | Ok(false) = event.comment.user.is_team_member(&ctx.github) {
75-
false
76-
} else {
77-
true
78-
};
79-
80-
let e = EditIssueBody::new(&event.issue, "ASSIGN");
78+
let e = EditIssueBody::new(&event.issue().unwrap(), "ASSIGN");
8179

8280
let to_assign = match cmd {
83-
AssignCommand::Own => event.comment.user.login.clone(),
81+
AssignCommand::Own => event.user().login.clone(),
8482
AssignCommand::User { username } => {
85-
if !is_team_member && username != event.comment.user.login {
83+
if !is_team_member && username != event.user().login {
8684
failure::bail!("Only Rust team members can assign other users");
8785
}
8886
username.clone()
@@ -119,18 +117,20 @@ impl Handler for AssignmentHandler {
119117

120118
e.apply(&ctx.github, String::new(), &data)?;
121119

122-
match event.issue.set_assignee(&ctx.github, &to_assign) {
120+
match event.issue().unwrap().set_assignee(&ctx.github, &to_assign) {
123121
Ok(()) => return Ok(()), // we are done
124122
Err(github::AssignmentError::InvalidAssignee) => {
125123
event
126-
.issue
124+
.issue()
125+
.unwrap()
127126
.set_assignee(&ctx.github, &ctx.username)
128127
.context("self-assignment failed")?;
129128
e.apply(
130129
&ctx.github,
131130
format!(
132131
"This issue has been assigned to @{} via [this comment]({}).",
133-
to_assign, event.comment.html_url
132+
to_assign,
133+
event.html_url().unwrap()
134134
),
135135
&data,
136136
)?;

src/handlers/relabel.rs

+21-18
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::{
1515
interactions::ErrorComment,
1616
};
1717
use failure::Error;
18-
use parser::command::relabel::{RelabelCommand, LabelDelta};
18+
use parser::command::relabel::{LabelDelta, RelabelCommand};
1919
use parser::command::{Command, Input};
2020

2121
pub(super) struct RelabelHandler;
@@ -25,21 +25,29 @@ impl Handler for RelabelHandler {
2525
type Config = RelabelConfig;
2626

2727
fn parse_input(&self, ctx: &Context, event: &Event) -> Result<Option<Self::Input>, Error> {
28-
#[allow(irrefutable_let_patterns)]
29-
let event = if let Event::IssueComment(e) = event {
30-
e
28+
let body = if let Some(b) = event.comment_body() {
29+
b
3130
} else {
3231
// not interested in other events
3332
return Ok(None);
3433
};
3534

36-
let mut input = Input::new(&event.comment.body, &ctx.username);
35+
if let Event::Issue(e) = event {
36+
if e.action != github::IssuesAction::Opened {
37+
// skip events other than opening the issue to avoid retriggering commands in the
38+
// issue body
39+
return Ok(None);
40+
}
41+
}
42+
43+
let mut input = Input::new(&body, &ctx.username);
3744
match input.parse_command() {
3845
Command::Relabel(Ok(command)) => Ok(Some(command)),
3946
Command::Relabel(Err(err)) => {
4047
failure::bail!(
4148
"Parsing label command in [comment]({}) failed: {}",
42-
event.comment.html_url, err
49+
event.html_url().expect("has html url"),
50+
err
4351
);
4452
}
4553
_ => Ok(None),
@@ -53,20 +61,12 @@ impl Handler for RelabelHandler {
5361
event: &Event,
5462
input: RelabelCommand,
5563
) -> Result<(), Error> {
56-
#[allow(irrefutable_let_patterns)]
57-
let event = if let Event::IssueComment(e) = event {
58-
e
59-
} else {
60-
// not interested in other events
61-
return Ok(());
62-
};
63-
64-
let mut issue_labels = event.issue.labels().to_owned();
64+
let mut issue_labels = event.issue().unwrap().labels().to_owned();
6565
let mut changed = false;
6666
for delta in &input.0 {
6767
let name = delta.label().as_str();
68-
if let Err(msg) = check_filter(name, config, &event.comment.user, &ctx.github) {
69-
ErrorComment::new(&event.issue, msg.to_string()).post(&ctx.github)?;
68+
if let Err(msg) = check_filter(name, config, &event.user(), &ctx.github) {
69+
ErrorComment::new(&event.issue().unwrap(), msg.to_string()).post(&ctx.github)?;
7070
return Ok(());
7171
}
7272
match delta {
@@ -88,7 +88,10 @@ impl Handler for RelabelHandler {
8888
}
8989

9090
if changed {
91-
event.issue.set_labels(&ctx.github, issue_labels)?;
91+
event
92+
.issue()
93+
.unwrap()
94+
.set_labels(&ctx.github, issue_labels)?;
9295
}
9396

9497
Ok(())

src/handlers/tracking_issue.rs

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![cfg(empty)]
12
use crate::{
23
github::GithubClient,
34
registry::{Event, Handler},
@@ -84,16 +85,8 @@ impl TrackingIssueHandler {
8485

8586
impl Handler for TrackingIssueHandler {
8687
fn handle_event(&self, event: &Event) -> Result<(), Error> {
87-
#[allow(irrefutable_let_patterns)]
88-
let event = if let Event::IssueComment(e) = event {
89-
e
90-
} else {
91-
// not interested in other events
92-
return Ok(());
93-
};
94-
95-
self.handle_create(&event)?;
96-
self.handle_link(&event)?;
88+
//self.handle_create(&event)?;
89+
//self.handle_link(&event)?;
9790
Ok(())
9891
}
9992
}

src/main.rs

+16
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use payload::SignedPayload;
2323

2424
enum EventName {
2525
IssueComment,
26+
Issue,
2627
Other,
2728
}
2829

@@ -36,6 +37,7 @@ impl<'a, 'r> request::FromRequest<'a, 'r> for EventName {
3637
};
3738
let ev = match ev {
3839
"issue_comment" => EventName::IssueComment,
40+
"issues" => EventName::Issue,
3941
_ => EventName::Other,
4042
};
4143
Outcome::Success(ev)
@@ -83,6 +85,20 @@ fn webhook(
8385
return Err(err.into());
8486
}
8587
}
88+
EventName::Issue => {
89+
let payload = payload
90+
.deserialize::<github::IssuesEvent>()
91+
.context("IssueCommentEvent failed to deserialize")
92+
.map_err(Error::from)?;
93+
94+
let event = github::Event::Issue(payload);
95+
if let Err(err) = handlers::handle(&ctx, &event) {
96+
if let Some(issue) = event.issue() {
97+
ErrorComment::new(issue, err.to_string()).post(&ctx.github)?;
98+
}
99+
return Err(err.into());
100+
}
101+
}
86102
// Other events need not be handled
87103
EventName::Other => {}
88104
}

0 commit comments

Comments
 (0)