@@ -4,8 +4,9 @@ mod tests;
44
55use self :: api:: { BranchProtectionOp , TeamPrivacy , TeamRole } ;
66use crate :: github:: api:: { GithubRead , Login , PushAllowanceActor , RepoPermission , RepoSettings } ;
7+ use anyhow:: anyhow;
78use log:: debug;
8- use rust_team_data:: v1:: { Bot , BranchProtectionMode , MergeBot } ;
9+ use rust_team_data:: v1:: { Bot , BranchProtectionMode , GitHubTeam , MergeBot } ;
910use std:: collections:: { HashMap , HashSet } ;
1011use std:: fmt:: { Display , Formatter , Write } ;
1112
@@ -73,6 +74,7 @@ struct SyncGitHub {
7374 repos : Vec < rust_team_data:: v1:: Repo > ,
7475 usernames_cache : HashMap < u64 , String > ,
7576 org_owners : HashMap < OrgName , HashSet < u64 > > ,
77+ org_members : HashMap < OrgName , HashSet < u64 > > ,
7678 org_apps : HashMap < OrgName , Vec < OrgAppInstallation > > ,
7779}
7880
@@ -103,10 +105,12 @@ impl SyncGitHub {
103105 . collect :: < HashSet < _ > > ( ) ;
104106
105107 let mut org_owners = HashMap :: new ( ) ;
108+ let mut org_members = HashMap :: new ( ) ;
106109 let mut org_apps = HashMap :: new ( ) ;
107110
108111 for org in & orgs {
109112 org_owners. insert ( ( * org) . to_string ( ) , github. org_owners ( org) ?) ;
113+ org_members. insert ( ( * org) . to_string ( ) , github. org_members ( org) ?) ;
110114
111115 let mut installations: Vec < OrgAppInstallation > = vec ! [ ] ;
112116
@@ -134,17 +138,21 @@ impl SyncGitHub {
134138 repos,
135139 usernames_cache,
136140 org_owners,
141+ org_members,
137142 org_apps,
138143 } )
139144 }
140145
141146 pub ( crate ) fn diff_all ( & self ) -> anyhow:: Result < Diff > {
142147 let team_diffs = self . diff_teams ( ) ?;
143148 let repo_diffs = self . diff_repos ( ) ?;
149+ let org_team_members = self . map_teams_to_org ( ) ?;
150+ let toml_github_diffs = self . diff_teams_gh_org ( org_team_members) ?;
144151
145152 Ok ( Diff {
146153 team_diffs,
147154 repo_diffs,
155+ toml_github_diffs,
148156 } )
149157 }
150158
@@ -195,6 +203,66 @@ impl SyncGitHub {
195203 Ok ( diffs)
196204 }
197205
206+ // collect all org and respective teams members in a HashMap
207+ fn map_teams_to_org ( & self ) -> anyhow:: Result < HashMap < String , HashSet < u64 > > > {
208+ let mut org_team_members: HashMap < String , HashSet < u64 > > = HashMap :: new ( ) ;
209+
210+ for team in & self . teams {
211+ let team_org;
212+ // get the team github org through the corresponding github team
213+ if let Some ( gh) = & team. github {
214+ let github_teams = & gh. teams ;
215+ let github_team: & GitHubTeam = github_teams
216+ . iter ( )
217+ . find ( |& gt| gt. name == team. name )
218+ . expect ( "Team Not Found" ) ;
219+ team_org = github_team. org . clone ( ) ;
220+ } else {
221+ return Err ( anyhow ! (
222+ "TeamGitHub object not found, got {:?}" ,
223+ & team. github
224+ ) ) ;
225+ }
226+
227+ let team_members_github_id: HashSet < u64 > =
228+ team. members . iter ( ) . map ( |member| member. github_id ) . collect ( ) ;
229+
230+ org_team_members
231+ . entry ( team_org)
232+ . or_default ( )
233+ . extend ( team_members_github_id) ;
234+ }
235+ Ok ( org_team_members)
236+ }
237+
238+ // create diff against github org members against toml team members
239+ fn diff_teams_gh_org (
240+ & self ,
241+ org_team_members : HashMap < String , HashSet < u64 > > ,
242+ ) -> anyhow:: Result < OrgMembershipDiff > {
243+ let mut org_with_members_to_be_removed: HashMap < String , HashSet < String > > = HashMap :: new ( ) ;
244+
245+ for ( gh_org, toml_members_across_teams) in org_team_members. into_iter ( ) {
246+ let gh_org_members = self . org_members . get ( & gh_org) . unwrap ( ) ;
247+
248+ let mut members_to_be_removed = HashSet :: new ( ) ;
249+
250+ for toml_member in toml_members_across_teams {
251+ if !gh_org_members. contains ( & toml_member. clone ( ) ) {
252+ members_to_be_removed. insert ( self . usernames_cache [ & toml_member] . clone ( ) ) ;
253+ }
254+ }
255+ org_with_members_to_be_removed
256+ . entry ( gh_org)
257+ . or_default ( )
258+ . extend ( members_to_be_removed) ;
259+ }
260+
261+ Ok ( OrgMembershipDiff :: Delete ( DeleteOrgMembershipDiff {
262+ org_with_members : org_with_members_to_be_removed,
263+ } ) )
264+ }
265+
198266 fn diff_team ( & self , github_team : & rust_team_data:: v1:: GitHubTeam ) -> anyhow:: Result < TeamDiff > {
199267 // Ensure the team exists and is consistent
200268 let team = match self . github . team ( & github_team. org , & github_team. name ) ? {
@@ -667,6 +735,7 @@ const BOTS_TEAMS: &[&str] = &["bors", "highfive", "rfcbot", "bots"];
667735pub ( crate ) struct Diff {
668736 team_diffs : Vec < TeamDiff > ,
669737 repo_diffs : Vec < RepoDiff > ,
738+ toml_github_diffs : OrgMembershipDiff ,
670739}
671740
672741impl Diff {
@@ -679,6 +748,8 @@ impl Diff {
679748 repo_diff. apply ( sync) ?;
680749 }
681750
751+ self . toml_github_diffs . apply ( sync) ?;
752+
682753 Ok ( ( ) )
683754 }
684755}
@@ -720,6 +791,55 @@ impl std::fmt::Display for RepoDiff {
720791 }
721792}
722793
794+ #[ derive( Debug ) ]
795+
796+ enum OrgMembershipDiff {
797+ Delete ( DeleteOrgMembershipDiff ) ,
798+ }
799+
800+ impl OrgMembershipDiff {
801+ fn apply ( self , sync : & GitHubWrite ) -> anyhow:: Result < ( ) > {
802+ match self {
803+ OrgMembershipDiff :: Delete ( d) => d. apply ( sync) ?,
804+ }
805+
806+ Ok ( ( ) )
807+ }
808+ }
809+
810+ impl std:: fmt:: Display for OrgMembershipDiff {
811+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
812+ match self {
813+ OrgMembershipDiff :: Delete ( d) => write ! ( f, "{d}" ) ,
814+ }
815+ }
816+ }
817+
818+ #[ derive( Debug ) ]
819+
820+ struct DeleteOrgMembershipDiff {
821+ org_with_members : HashMap < String , HashSet < String > > ,
822+ }
823+
824+ impl DeleteOrgMembershipDiff {
825+ fn apply ( self , sync : & GitHubWrite ) -> anyhow:: Result < ( ) > {
826+ for ( gh_org, members) in self . org_with_members . iter ( ) {
827+ for member in members {
828+ sync. remove_gh_member_from_org ( gh_org, member) ?;
829+ }
830+ }
831+
832+ Ok ( ( ) )
833+ }
834+ }
835+
836+ impl std:: fmt:: Display for DeleteOrgMembershipDiff {
837+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
838+ writeln ! ( f, "❌ Deleting members '{:?}'" , self . org_with_members) ?;
839+ Ok ( ( ) )
840+ }
841+ }
842+
723843struct CreateRepoDiff {
724844 org : String ,
725845 name : String ,
0 commit comments