11use anyhow:: Context as _;
22
33use crate :: config:: RangeDiffConfig ;
4+ use crate :: db:: issue_data:: IssueData ;
5+ use crate :: github:: CommitBase ;
46use crate :: github:: GithubCompare ;
57use crate :: github:: IssueRepository ;
68use crate :: github:: IssuesEvent ;
9+ use crate :: github:: ReportedContentClassifiers ;
710use crate :: handlers:: Context ;
811
12+ /// Key for the state in the database
13+ const RANGE_DIFF_LINK_KEY : & str = "range-diff-link" ;
14+
15+ /// State stored in the database
16+ #[ derive( Debug , Default , serde:: Deserialize , serde:: Serialize , Clone , PartialEq ) ]
17+ struct RangeDiffLinkState {
18+ /// ID of the most recent range-diff comment.
19+ last_comment : Option < String > ,
20+ }
21+
922pub ( super ) async fn handle_event (
1023 ctx : & Context ,
1124 host : & str ,
@@ -23,25 +36,45 @@ pub(super) async fn handle_event(
2336 } ;
2437
2538 let base = event. issue . base . as_ref ( ) . context ( "no base ref" ) ?;
26- let org = event. repository . owner ( ) ;
27- let repo = event. repository . name ( ) ;
39+ let issue_repo = IssueRepository {
40+ organization : event. repository . owner ( ) . to_string ( ) ,
41+ repository : event. repository . name ( ) . to_string ( ) ,
42+ } ;
2843
2944 let compare_before = ctx
3045 . github
31- . compare (
32- & IssueRepository {
33- organization : org. to_string ( ) ,
34- repository : repo. to_string ( ) ,
35- } ,
36- & base. sha ,
37- & before,
38- )
46+ . compare ( & issue_repo, & base. sha , & before)
3947 . await
4048 . context ( "failed to get the before compare" ) ?;
4149
50+ if let Some ( message) = changed_base_commit (
51+ host,
52+ & issue_repo,
53+ base,
54+ & compare_before,
55+ compare_after,
56+ before,
57+ after,
58+ ) {
59+ // Rebase detected, post a comment linking to our range-diff.
60+ post_new_comment ( ctx, event, message) . await ?;
61+ }
62+
63+ Ok ( ( ) )
64+ }
65+
66+ fn changed_base_commit (
67+ host : & str ,
68+ issue_repo : & IssueRepository ,
69+ base : & CommitBase ,
70+ compare_before : & GithubCompare ,
71+ compare_after : & GithubCompare ,
72+ oldhead : & str ,
73+ newhead : & str ,
74+ ) -> Option < String > {
4275 // Does the merge_base_commits differs? No, not a force-push with rebase.
4376 if compare_before. merge_base_commit . sha == compare_after. merge_base_commit . sha {
44- return Ok ( ( ) ) ;
77+ return None ;
4578 }
4679
4780 let protocol = if host. starts_with ( "localhost:" ) {
@@ -51,13 +84,137 @@ pub(super) async fn handle_event(
5184 } ;
5285
5386 let branch = & base. git_ref ;
54- let ( oldbase, oldhead ) = ( & compare_before. merge_base_commit . sha , before ) ;
55- let ( newbase, newhead ) = ( & compare_after. merge_base_commit . sha , after ) ;
87+ let oldbase = & compare_before. merge_base_commit . sha ;
88+ let newbase = & compare_after. merge_base_commit . sha ;
5689
57- // Rebase detected, post a comment to our range-diff.
58- event. issue . post_comment ( & ctx. github ,
59- & format ! ( r#"This PR was rebased onto a different {branch} commit! Check out the changes with our [`range-diff`]({protocol}://{host}/gh-range-diff/{org}/{repo}/{oldbase}..{oldhead}/{newbase}..{newhead})."# )
60- ) . await . context ( "failed to post range-diff comment" ) ?;
90+ let message = format ! (
91+ r#"This PR was rebased onto a different {branch} commit. Here's a [range-diff]({protocol}://{host}/gh-range-diff/{issue_repo}/{oldbase}..{oldhead}/{newbase}..{newhead}) highlighting what actually changed.
92+
93+ *Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.*"#
94+ ) ;
95+
96+ Some ( message)
97+ }
98+
99+ async fn post_new_comment (
100+ ctx : & Context ,
101+ event : & IssuesEvent ,
102+ message : String ,
103+ ) -> anyhow:: Result < ( ) > {
104+ let mut db = ctx. db . get ( ) . await ;
105+ let mut state: IssueData < ' _ , RangeDiffLinkState > =
106+ IssueData :: load ( & mut db, & event. issue , RANGE_DIFF_LINK_KEY ) . await ?;
107+
108+ // Hide previous range-diff comment.
109+ if let Some ( last_comment) = state. data . last_comment {
110+ event
111+ . issue
112+ . hide_comment (
113+ & ctx. github ,
114+ & last_comment,
115+ ReportedContentClassifiers :: Outdated ,
116+ )
117+ . await
118+ . context ( "failed to hide previous range-diff comment" ) ?;
119+ }
120+
121+ // Post new range-diff comment and remember it.
122+ state. data . last_comment = Some (
123+ event
124+ . issue
125+ . post_comment ( & ctx. github , & message)
126+ . await
127+ . context ( "failed to post range-diff comment" ) ?
128+ . node_id ,
129+ ) ;
130+
131+ state. save ( ) . await ?;
61132
62133 Ok ( ( ) )
63134}
135+
136+ #[ cfg( test) ]
137+ mod tests {
138+ use crate :: handlers:: check_commits:: dummy_commit_from_body;
139+
140+ use super :: changed_base_commit;
141+
142+ #[ test]
143+ fn unchanged_base_commit ( ) {
144+ assert_eq ! (
145+ changed_base_commit(
146+ "mytriagebot.com" ,
147+ & crate :: github:: IssueRepository {
148+ organization: "rust-lang" . to_string( ) ,
149+ repository: "rust" . to_string( )
150+ } ,
151+ & crate :: github:: CommitBase {
152+ sha: "sha-base-commit" . to_string( ) ,
153+ git_ref: "master" . to_string( ) ,
154+ repo: None
155+ } ,
156+ & crate :: github:: GithubCompare {
157+ base_commit: dummy_commit_from_body( "base-commit-sha" , "base-commit-body" ) ,
158+ merge_base_commit: dummy_commit_from_body(
159+ "same-merge-commit" ,
160+ "merge-commit-body"
161+ ) ,
162+ files: vec![ ]
163+ } ,
164+ & crate :: github:: GithubCompare {
165+ base_commit: dummy_commit_from_body( "base-commit-sha" , "base-commit-body" ) ,
166+ merge_base_commit: dummy_commit_from_body(
167+ "same-merge-commit" ,
168+ "merge-commit-body"
169+ ) ,
170+ files: vec![ ]
171+ } ,
172+ "oldhead" ,
173+ "newhead"
174+ ) ,
175+ None
176+ ) ;
177+ }
178+
179+ #[ test]
180+ fn changed_base_commit_ ( ) {
181+ assert_eq ! (
182+ changed_base_commit(
183+ "mytriagebot.com" ,
184+ & crate :: github:: IssueRepository {
185+ organization: "rust-lang" . to_string( ) ,
186+ repository: "rust" . to_string( )
187+ } ,
188+ & crate :: github:: CommitBase {
189+ sha: "sha-base-commit" . to_string( ) ,
190+ git_ref: "master" . to_string( ) ,
191+ repo: None
192+ } ,
193+ & crate :: github:: GithubCompare {
194+ base_commit: dummy_commit_from_body( "base-commit-sha" , "base-commit-body" ) ,
195+ merge_base_commit: dummy_commit_from_body(
196+ "before-merge-commit" ,
197+ "merge-commit-body"
198+ ) ,
199+ files: vec![ ]
200+ } ,
201+ & crate :: github:: GithubCompare {
202+ base_commit: dummy_commit_from_body( "base-commit-sha" , "base-commit-body" ) ,
203+ merge_base_commit: dummy_commit_from_body(
204+ "after-merge-commit" ,
205+ "merge-commit-body"
206+ ) ,
207+ files: vec![ ]
208+ } ,
209+ "oldhead" ,
210+ "newhead"
211+ ) ,
212+ Some (
213+ r#"This PR was rebased onto a different master commit. Here's a [range-diff](https://mytriagebot.com/gh-range-diff/rust-lang/rust/before-merge-commit..oldhead/after-merge-commit..newhead) highlighting what actually changed.
214+
215+ *Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.*"#
216+ . to_string( )
217+ )
218+ ) ;
219+ }
220+ }
0 commit comments