@@ -15,37 +15,9 @@ macro_rules! inform {
1515 anyhow:: bail!( crate :: handlers:: UserError ( $err. into( ) ) )
1616 } ;
1717}
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- ) ;
3718
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;
4921
5022mod assign;
5123mod autolabel;
@@ -94,6 +66,41 @@ pub struct Context {
9466 pub gha_logs : Arc < tokio:: sync:: RwLock < GitHubActionLogsCache > > ,
9567}
9668
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+
97104pub async fn handle ( ctx : & Context , host : & str , event : & Event ) -> Vec < HandlerError > {
98105 let config = config:: get ( & ctx. github , event. repo ( ) ) . await ;
99106 if let Err ( e) = & config {
@@ -164,220 +171,6 @@ pub async fn handle(ctx: &Context, host: &str, event: &Event) -> Vec<HandlerErro
164171 errors
165172}
166173
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-
381174#[ derive( Debug ) ]
382175pub enum HandlerError {
383176 Message ( String ) ,
0 commit comments