@@ -19,13 +19,17 @@ mod modified_submodule;
1919mod no_mentions;
2020mod no_merges;
2121mod non_default_branch;
22+ mod validate_config;
2223
2324/// Key for the state in the database
24- const CHECK_COMMITS_WARNINGS_KEY : & str = "check-commits-warnings" ;
25+ const CHECK_COMMITS_KEY : & str = "check-commits-warnings" ;
2526
2627/// State stored in the database
2728#[ derive( Debug , Default , serde:: Deserialize , serde:: Serialize , Clone , PartialEq ) ]
28- struct CheckCommitsWarningsState {
29+ struct CheckCommitsState {
30+ /// List of the last errors (comment body, comment node-id).
31+ #[ serde( default ) ]
32+ last_errors : Vec < ( String , String ) > ,
2933 /// List of the last warnings in the most recent comment.
3034 last_warnings : Vec < String > ,
3135 /// ID of the most recent warning comment.
@@ -41,7 +45,10 @@ pub(super) async fn handle(ctx: &Context, event: &Event, config: &Config) -> any
4145
4246 if !matches ! (
4347 event. action,
44- IssuesAction :: Opened | IssuesAction :: Synchronize | IssuesAction :: ReadyForReview
48+ IssuesAction :: Opened
49+ | IssuesAction :: Reopened
50+ | IssuesAction :: Synchronize
51+ | IssuesAction :: ReadyForReview
4552 ) || !event. issue . is_pr ( )
4653 {
4754 return Ok ( ( ) ) ;
@@ -61,6 +68,7 @@ pub(super) async fn handle(ctx: &Context, event: &Event, config: &Config) -> any
6168 let commits = event. issue . commits ( & ctx. github ) . await ?;
6269 let diff = & compare. files ;
6370
71+ let mut errors = Vec :: new ( ) ;
6472 let mut warnings = Vec :: new ( ) ;
6573 let mut labels = Vec :: new ( ) ;
6674
@@ -108,20 +116,51 @@ pub(super) async fn handle(ctx: &Context, event: &Event, config: &Config) -> any
108116 }
109117 }
110118
111- handle_warnings_and_labels ( ctx, event, warnings, labels) . await
119+ // Check if the `triagebot.toml` config is valid
120+ errors. extend (
121+ validate_config:: validate_config ( ctx, & event, diff)
122+ . await
123+ . context ( "validating the the triagebot config" ) ?,
124+ ) ;
125+
126+ handle_new_state ( ctx, event, errors, warnings, labels) . await
112127}
113128
114129// Add, hide or hide&add a comment with the warnings.
115- async fn handle_warnings_and_labels (
130+ async fn handle_new_state (
116131 ctx : & Context ,
117132 event : & IssuesEvent ,
133+ errors : Vec < String > ,
118134 warnings : Vec < String > ,
119135 labels : Vec < String > ,
120136) -> anyhow:: Result < ( ) > {
121137 // Get the state of the warnings for this PR in the database.
122138 let mut db = ctx. db . get ( ) . await ;
123- let mut state: IssueData < ' _ , CheckCommitsWarningsState > =
124- IssueData :: load ( & mut db, & event. issue , CHECK_COMMITS_WARNINGS_KEY ) . await ?;
139+ let mut state: IssueData < ' _ , CheckCommitsState > =
140+ IssueData :: load ( & mut db, & event. issue , CHECK_COMMITS_KEY ) . await ?;
141+
142+ // Handles the errors, post the new ones, hide resolved ones and don't touch the one still active
143+ if !state. data . last_errors . is_empty ( ) || !errors. is_empty ( ) {
144+ let ( errors_to_remove, errors_to_add) =
145+ calculate_error_changes ( & state. data . last_errors , & errors) ;
146+
147+ for error_to_remove in errors_to_remove {
148+ event
149+ . issue
150+ . hide_comment (
151+ & ctx. github ,
152+ & error_to_remove. 1 ,
153+ ReportedContentClassifiers :: Resolved ,
154+ )
155+ . await ?;
156+ state. data . last_errors . retain ( |e| e != & error_to_remove) ;
157+ }
158+
159+ for error_to_add in errors_to_add {
160+ let comment = event. issue . post_comment ( & ctx. github , & error_to_add) . await ?;
161+ state. data . last_errors . push ( ( error_to_add, comment. node_id ) ) ;
162+ }
163+ }
125164
126165 // We only post a new comment when we haven't posted one with the same warnings before.
127166 if !warnings. is_empty ( ) && state. data . last_warnings != warnings {
@@ -225,6 +264,28 @@ fn calculate_label_changes(
225264 ( removals, additions)
226265}
227266
267+ // Calculate the error changes
268+ fn calculate_error_changes (
269+ previous : & Vec < ( String , String ) > ,
270+ current : & Vec < String > ,
271+ ) -> ( Vec < ( String , String ) > , Vec < String > ) {
272+ let previous_set: HashSet < ( String , String ) > = previous. into_iter ( ) . cloned ( ) . collect ( ) ;
273+ let current_set: HashSet < String > = current. into_iter ( ) . cloned ( ) . collect ( ) ;
274+
275+ let removals = previous_set
276+ . iter ( )
277+ . filter ( |( e, _) | !current_set. contains ( e) )
278+ . cloned ( )
279+ . collect ( ) ;
280+ let additions = current_set
281+ . iter ( )
282+ . filter ( |e| !previous_set. iter ( ) . any ( |( e2, _) | e == & e2) )
283+ . cloned ( )
284+ . collect ( ) ;
285+
286+ ( removals, additions)
287+ }
288+
228289#[ cfg( test) ]
229290fn dummy_commit_from_body ( sha : & str , body : & str ) -> GithubCommit {
230291 use chrono:: { DateTime , FixedOffset } ;
0 commit comments