@@ -15,37 +15,9 @@ macro_rules! inform {
15
15
anyhow:: bail!( crate :: handlers:: UserError ( $err. into( ) ) )
16
16
} ;
17
17
}
18
- macro_rules! custom_handlers {
19
- ( $errors: ident -> $( $name: ident: $hd: expr, ) * ) => { {
20
- // Process the handlers concurrently
21
- let results = futures:: join!(
22
- $(
23
- async {
24
- async {
25
- $hd
26
- }
27
- . await
28
- . map_err( |e: anyhow:: Error | {
29
- HandlerError :: Other ( e. context( format!(
30
- "error when processing {} handler" ,
31
- stringify!( $name)
32
- ) ) )
33
- } )
34
- }
35
- ) ,*
36
- ) ;
37
18
38
- // Destructure the results into named variables
39
- let ( $( $name, ) * ) = results;
40
-
41
- // Push errors for each handler
42
- $(
43
- if let Err ( e) = $name {
44
- $errors. push( e) ;
45
- }
46
- ) *
47
- } }
48
- }
19
+ #[ macro_use]
20
+ mod macros;
49
21
50
22
mod assign;
51
23
mod autolabel;
@@ -94,6 +66,41 @@ pub struct Context {
94
66
pub gha_logs : Arc < tokio:: sync:: RwLock < GitHubActionLogsCache > > ,
95
67
}
96
68
69
+ // Handle events that happened on issues
70
+ //
71
+ // This is for events that happen only on issues or pull requests (e.g. label changes or assignments).
72
+ // Each module in the list must contain the functions `parse_input` and `handle_input`.
73
+ issue_handlers ! {
74
+ assign,
75
+ autolabel,
76
+ backport,
77
+ issue_links,
78
+ major_change,
79
+ mentions,
80
+ notify_zulip,
81
+ review_requested,
82
+ pr_tracking,
83
+ }
84
+
85
+ // Handle commands in comments/issues body
86
+ //
87
+ // This is for handlers for commands parsed by the `parser` crate.
88
+ // Each variant of `parser::command::Command` must be in this list,
89
+ // preceded by the module containing the corresponding `handle_command` function
90
+ command_handlers ! {
91
+ assign: Assign ,
92
+ nominate: Nominate ,
93
+ ping: Ping ,
94
+ prioritize: Prioritize ,
95
+ relabel: Relabel ,
96
+ major_change: Second ,
97
+ shortcut: Shortcut ,
98
+ close: Close ,
99
+ note: Note ,
100
+ concern: Concern ,
101
+ transfer: Transfer ,
102
+ }
103
+
97
104
pub async fn handle ( ctx : & Context , host : & str , event : & Event ) -> Vec < HandlerError > {
98
105
let config = config:: get ( & ctx. github , event. repo ( ) ) . await ;
99
106
if let Err ( e) = & config {
@@ -164,220 +171,6 @@ pub async fn handle(ctx: &Context, host: &str, event: &Event) -> Vec<HandlerErro
164
171
errors
165
172
}
166
173
167
- macro_rules! issue_handlers {
168
- ( $( $name: ident, ) * ) => {
169
- async fn handle_issue(
170
- ctx: & Context ,
171
- event: & IssuesEvent ,
172
- config: & Arc <Config >,
173
- errors: & mut Vec <HandlerError >,
174
- ) {
175
- // Process the issue handlers concurrently
176
- let results = futures:: join!(
177
- $(
178
- async {
179
- match $name:: parse_input( ctx, event, config. $name. as_ref( ) ) . await {
180
- Err ( err) => Err ( HandlerError :: Message ( err) ) ,
181
- Ok ( Some ( input) ) => {
182
- if let Some ( config) = & config. $name {
183
- $name:: handle_input( ctx, config, event, input)
184
- . await
185
- . map_err( |e| {
186
- HandlerError :: Other ( e. context( format!(
187
- "error when processing {} handler" ,
188
- stringify!( $name)
189
- ) ) )
190
- } )
191
- } else {
192
- Err ( HandlerError :: Message ( format!(
193
- "The feature `{}` is not enabled in this repository.\n \
194
- To enable it add its section in the `triagebot.toml` \
195
- in the root of the repository.",
196
- stringify!( $name)
197
- ) ) )
198
- }
199
- }
200
- Ok ( None ) => Ok ( ( ) )
201
- }
202
- }
203
- ) ,*
204
- ) ;
205
-
206
- // Destructure the results into named variables
207
- let ( $( $name, ) * ) = results;
208
-
209
- // Push errors for each handler
210
- $(
211
- if let Err ( e) = $name {
212
- errors. push( e) ;
213
- }
214
- ) *
215
- }
216
- }
217
- }
218
-
219
- // Handle events that happened on issues
220
- //
221
- // This is for events that happen only on issues or pull requests (e.g. label changes or assignments).
222
- // Each module in the list must contain the functions `parse_input` and `handle_input`.
223
- issue_handlers ! {
224
- assign,
225
- autolabel,
226
- backport,
227
- issue_links,
228
- major_change,
229
- mentions,
230
- notify_zulip,
231
- review_requested,
232
- pr_tracking,
233
- }
234
-
235
- macro_rules! command_handlers {
236
- ( $( $name: ident: $enum: ident, ) * ) => {
237
- async fn handle_command(
238
- ctx: & Context ,
239
- event: & Event ,
240
- config: & Result <Arc <Config >, ConfigurationError >,
241
- body: & str ,
242
- errors: & mut Vec <HandlerError >,
243
- ) {
244
- match event {
245
- // always handle new PRs / issues
246
- Event :: Issue ( IssuesEvent { action: IssuesAction :: Opened , .. } ) => { } ,
247
- Event :: Issue ( IssuesEvent { action: IssuesAction :: Edited , .. } ) => {
248
- // if the issue was edited, but we don't get a `changes[body]` diff, it means only the title was edited, not the body.
249
- // don't process the same commands twice.
250
- if event. comment_from( ) . is_none( ) {
251
- log:: debug!( "skipping title-only edit event" ) ;
252
- return ;
253
- }
254
- } ,
255
- Event :: Issue ( e) => {
256
- // no change in issue's body for these events, so skip
257
- log:: debug!( "skipping event, issue was {:?}" , e. action) ;
258
- return ;
259
- }
260
- Event :: IssueComment ( e) => {
261
- match e. action {
262
- IssueCommentAction :: Created => { }
263
- IssueCommentAction :: Edited => {
264
- if event. comment_from( ) . is_none( ) {
265
- // We are not entirely sure why this happens.
266
- // Sometimes when someone posts a PR review,
267
- // GitHub sends an "edited" event with no
268
- // changes just before the "created" event.
269
- log:: debug!( "skipping issue comment edit without changes" ) ;
270
- return ;
271
- }
272
- }
273
- IssueCommentAction :: Deleted => {
274
- // don't execute commands again when comment is deleted
275
- log:: debug!( "skipping event, comment was {:?}" , e. action) ;
276
- return ;
277
- }
278
- }
279
- }
280
- Event :: Push ( _) | Event :: Create ( _) => {
281
- log:: debug!( "skipping unsupported event" ) ;
282
- return ;
283
- }
284
- }
285
-
286
- let input = Input :: new( & body, vec![ & ctx. username, "triagebot" ] ) ;
287
- let commands = if let Some ( previous) = event. comment_from( ) {
288
- let prev_commands = Input :: new( & previous, vec![ & ctx. username, "triagebot" ] ) . collect:: <Vec <_>>( ) ;
289
- input. filter( |cmd| !prev_commands. contains( cmd) ) . collect:: <Vec <_>>( )
290
- } else {
291
- input. collect( )
292
- } ;
293
-
294
- log:: info!( "Comment parsed to {:?}" , commands) ;
295
-
296
- if commands. is_empty( ) {
297
- return ;
298
- }
299
-
300
- let config = match config {
301
- Ok ( config) => config,
302
- Err ( e @ ConfigurationError :: Missing ) => {
303
- // r? is conventionally used to mean "hey, can you review"
304
- // even if the repo doesn't have a triagebot.toml. In that
305
- // case, just ignore it.
306
- if commands
307
- . iter( )
308
- . all( |cmd| matches!( cmd, Command :: Assign ( Ok ( AssignCommand :: RequestReview { .. } ) ) ) )
309
- {
310
- return ;
311
- }
312
- return errors. push( HandlerError :: Message ( e. to_string( ) ) ) ;
313
- }
314
- Err ( e @ ConfigurationError :: Toml ( _) ) => {
315
- return errors. push( HandlerError :: Message ( e. to_string( ) ) ) ;
316
- }
317
- Err ( e @ ConfigurationError :: Http ( _) ) => {
318
- return errors. push( HandlerError :: Other ( e. clone( ) . into( ) ) ) ;
319
- }
320
- } ;
321
-
322
- for command in commands {
323
- match command {
324
- $(
325
- Command :: $enum( Ok ( command) ) => {
326
- if let Some ( config) = & config. $name {
327
- $name:: handle_command( ctx, config, event, command)
328
- . await
329
- . unwrap_or_else( |mut err| {
330
- if let Some ( err) = err. downcast_mut:: <UserError >( ) {
331
- errors. push( HandlerError :: Message ( std:: mem:: take( & mut err. 0 ) ) ) ;
332
- } else {
333
- errors. push( HandlerError :: Other ( err. context( format!(
334
- "error when processing {} command handler" ,
335
- stringify!( $name)
336
- ) ) ) ) ;
337
- }
338
- } ) ;
339
- } else {
340
- errors. push( HandlerError :: Message ( format!(
341
- "The feature `{}` is not enabled in this repository.\n \
342
- To enable it add its section in the `triagebot.toml` \
343
- in the root of the repository.",
344
- stringify!( $name)
345
- ) ) ) ;
346
- }
347
- }
348
- Command :: $enum( Err ( err) ) => {
349
- errors. push( HandlerError :: Message ( format!(
350
- "Parsing {} command in [comment]({}) failed: {}" ,
351
- stringify!( $name) ,
352
- event. html_url( ) . expect( "has html url" ) ,
353
- err
354
- ) ) ) ;
355
- } ) *
356
- }
357
- }
358
- }
359
- }
360
- }
361
-
362
- // Handle commands in comments/issues body
363
- //
364
- // This is for handlers for commands parsed by the `parser` crate.
365
- // Each variant of `parser::command::Command` must be in this list,
366
- // preceded by the module containing the corresponding `handle_command` function
367
- command_handlers ! {
368
- assign: Assign ,
369
- nominate: Nominate ,
370
- ping: Ping ,
371
- prioritize: Prioritize ,
372
- relabel: Relabel ,
373
- major_change: Second ,
374
- shortcut: Shortcut ,
375
- close: Close ,
376
- note: Note ,
377
- concern: Concern ,
378
- transfer: Transfer ,
379
- }
380
-
381
174
#[ derive( Debug ) ]
382
175
pub enum HandlerError {
383
176
Message ( String ) ,
0 commit comments