@@ -3,11 +3,13 @@ mod api;
33mod tests;
44
55use self :: api:: { BranchProtectionOp , TeamPrivacy , TeamRole } ;
6- use crate :: github:: api:: { GithubRead , Login , PushAllowanceActor , RepoPermission , RepoSettings } ;
6+ use crate :: github:: api:: {
7+ GithubRead , Login , PushAllowanceActor , Repo , RepoPermission , RepoSettings ,
8+ } ;
79use log:: debug;
810use rust_team_data:: v1:: Bot ;
911use std:: collections:: { HashMap , HashSet } ;
10- use std:: fmt:: Write ;
12+ use std:: fmt:: { Display , Formatter , Write } ;
1113
1214pub ( crate ) use self :: api:: { GitHubApiRead , GitHubWrite , HttpClient } ;
1315
@@ -26,7 +28,7 @@ pub(crate) fn create_diff(
2628type OrgName = String ;
2729type RepoName = String ;
2830
29- #[ derive( Clone , Debug ) ]
31+ #[ derive( Clone , Debug , PartialEq ) ]
3032enum GithubApp {
3133 RenovateBot ,
3234}
@@ -40,20 +42,34 @@ impl GithubApp {
4042 }
4143}
4244
45+ impl Display for GithubApp {
46+ fn fmt ( & self , f : & mut Formatter < ' _ > ) -> std:: fmt:: Result {
47+ match self {
48+ GithubApp :: RenovateBot => f. write_str ( "RenovateBot" ) ,
49+ }
50+ }
51+ }
52+
4353#[ derive( Clone , Debug ) ]
44- struct GithubAppInstallation {
54+ struct OrgAppInstallation {
4555 app : GithubApp ,
4656 installation_id : u64 ,
4757 repositories : HashSet < RepoName > ,
4858}
4959
60+ #[ derive( Clone , Debug , PartialEq ) ]
61+ struct AppInstallation {
62+ app : GithubApp ,
63+ installation_id : u64 ,
64+ }
65+
5066struct SyncGitHub {
5167 github : Box < dyn GithubRead > ,
5268 teams : Vec < rust_team_data:: v1:: Team > ,
5369 repos : Vec < rust_team_data:: v1:: Repo > ,
5470 usernames_cache : HashMap < u64 , String > ,
5571 org_owners : HashMap < OrgName , HashSet < u64 > > ,
56- org_apps : HashMap < OrgName , Vec < GithubAppInstallation > > ,
72+ org_apps : HashMap < OrgName , Vec < OrgAppInstallation > > ,
5773}
5874
5975impl SyncGitHub {
@@ -88,7 +104,7 @@ impl SyncGitHub {
88104 for org in & orgs {
89105 org_owners. insert ( ( * org) . to_string ( ) , github. org_owners ( org) ?) ;
90106
91- let mut installations: Vec < GithubAppInstallation > = vec ! [ ] ;
107+ let mut installations: Vec < OrgAppInstallation > = vec ! [ ] ;
92108
93109 for installation in github. org_app_installations ( org) ? {
94110 if let Some ( app) = GithubApp :: from_id ( installation. app_id ) {
@@ -98,7 +114,7 @@ impl SyncGitHub {
98114 {
99115 repositories. insert ( repo_installation. name ) ;
100116 }
101- installations. push ( GithubAppInstallation {
117+ installations. push ( OrgAppInstallation {
102118 app,
103119 installation_id : installation. installation_id ,
104120 repositories,
@@ -299,6 +315,7 @@ impl SyncGitHub {
299315 } ,
300316 permissions,
301317 branch_protections,
318+ app_installations : self . diff_app_installations ( expected_repo, & [ ] ) ?,
302319 } ) ) ;
303320 }
304321 } ;
@@ -392,6 +409,52 @@ impl SyncGitHub {
392409 Ok ( branch_protection_diffs)
393410 }
394411
412+ fn diff_app_installations (
413+ & self ,
414+ expected_repo : & rust_team_data:: v1:: Repo ,
415+ existing_installations : & [ AppInstallation ] ,
416+ ) -> anyhow:: Result < Vec < AppInstallationDiff > > {
417+ let mut diff = vec ! [ ] ;
418+ let mut found_apps = Vec :: new ( ) ;
419+
420+ // Find apps that should be enabled on the repository
421+ for app in expected_repo. bots . iter ( ) . filter_map ( |bot| match bot {
422+ Bot :: Renovate => Some ( GithubApp :: RenovateBot ) ,
423+ _ => None ,
424+ } ) {
425+ // Find installation ID of this app on GitHub
426+ let gh_installation = self
427+ . org_apps
428+ . get ( & expected_repo. org )
429+ . and_then ( |installations| {
430+ installations
431+ . iter ( )
432+ . find ( |installation| installation. app == app)
433+ . map ( |i| i. installation_id )
434+ } ) ;
435+ let Some ( gh_installation) = gh_installation else {
436+ log:: warn!( "Application {app} should be enabled for repository {}/{}, but it is not installed on GitHub" , expected_repo. org, expected_repo. name) ;
437+ continue ;
438+ } ;
439+ let installation = AppInstallation {
440+ app,
441+ installation_id : gh_installation,
442+ } ;
443+ found_apps. push ( installation. clone ( ) ) ;
444+
445+ if !existing_installations. contains ( & installation) {
446+ diff. push ( AppInstallationDiff :: Add ( installation) ) ;
447+ }
448+ }
449+ for existing in existing_installations {
450+ if !found_apps. contains ( existing) {
451+ diff. push ( AppInstallationDiff :: Remove ( existing. clone ( ) ) ) ;
452+ }
453+ }
454+
455+ Ok ( diff)
456+ }
457+
395458 fn expected_role ( & self , org : & str , user : u64 ) -> TeamRole {
396459 if let Some ( true ) = self
397460 . org_owners
@@ -499,6 +562,7 @@ fn bot_name(bot: &Bot) -> Option<&str> {
499562 Bot :: RustTimer => Some ( "rust-timer" ) ,
500563 Bot :: Rustbot => Some ( "rustbot" ) ,
501564 Bot :: Rfcbot => Some ( "rfcbot" ) ,
565+ Bot :: Renovate => None ,
502566 }
503567}
504568
@@ -619,6 +683,7 @@ struct CreateRepoDiff {
619683 settings : RepoSettings ,
620684 permissions : Vec < RepoPermissionAssignmentDiff > ,
621685 branch_protections : Vec < ( String , api:: BranchProtection ) > ,
686+ app_installations : Vec < AppInstallationDiff > ,
622687}
623688
624689impl CreateRepoDiff {
@@ -636,6 +701,11 @@ impl CreateRepoDiff {
636701 }
637702 . apply ( sync, & self . org , & self . name , & repo. id ) ?;
638703 }
704+
705+ for installation in & self . app_installations {
706+ installation. apply ( sync, & repo) ?;
707+ }
708+
639709 Ok ( ( ) )
640710 }
641711}
@@ -664,6 +734,10 @@ impl std::fmt::Display for CreateRepoDiff {
664734 writeln ! ( & mut f, " {branch_name}" ) ?;
665735 log_branch_protection ( branch_protection, None , & mut f) ?;
666736 }
737+ writeln ! ( f, " App Installations:" ) ?;
738+ for diff in & self . app_installations {
739+ write ! ( f, "{diff}" ) ?;
740+ }
667741 Ok ( ( ) )
668742 }
669743}
@@ -930,6 +1004,38 @@ enum BranchProtectionDiffOperation {
9301004 Delete ( String ) ,
9311005}
9321006
1007+ enum AppInstallationDiff {
1008+ Add ( AppInstallation ) ,
1009+ Remove ( AppInstallation ) ,
1010+ }
1011+
1012+ impl AppInstallationDiff {
1013+ fn apply ( & self , sync : & GitHubWrite , repo : & Repo ) -> anyhow:: Result < ( ) > {
1014+ match self {
1015+ AppInstallationDiff :: Add ( app) => {
1016+ sync. add_repo_to_app_installation ( app. installation_id , repo. repo_id ) ?;
1017+ }
1018+ AppInstallationDiff :: Remove ( _) => {
1019+ todo ! ( )
1020+ }
1021+ }
1022+ Ok ( ( ) )
1023+ }
1024+ }
1025+
1026+ impl std:: fmt:: Display for AppInstallationDiff {
1027+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
1028+ match self {
1029+ AppInstallationDiff :: Add ( app) => {
1030+ writeln ! ( f, " Install app {}" , app. app)
1031+ }
1032+ AppInstallationDiff :: Remove ( app) => {
1033+ writeln ! ( f, " Remove app {}" , app. app)
1034+ }
1035+ }
1036+ }
1037+ }
1038+
9331039#[ derive( Debug ) ]
9341040enum TeamDiff {
9351041 Create ( CreateTeamDiff ) ,
0 commit comments