diff --git a/Cargo.lock b/Cargo.lock index 7e5b336..952a39f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -909,7 +909,7 @@ dependencies = [ [[package]] name = "rust_team_data" version = "1.0.0" -source = "git+https://github.com/rust-lang/team#3c2148cab16d60b9a0336076ac085bed7ddeb46b" +source = "git+https://github.com/rust-lang/team#1a4ef20fe2773e6539b7b60a1f6e0f1641497960" dependencies = [ "chacha20poly1305", "getrandom", diff --git a/src/github/api/mod.rs b/src/github/api/mod.rs index 3def026..fc22085 100644 --- a/src/github/api/mod.rs +++ b/src/github/api/mod.rs @@ -268,8 +268,8 @@ where /// An object with a `login` field #[derive(Deserialize, Debug, Clone, PartialEq, Eq)] -struct Login { - login: String, +pub(crate) struct Login { + pub(crate) login: String, } #[derive(serde::Serialize, serde::Deserialize, Debug, Eq, PartialEq, Copy, Clone)] @@ -365,8 +365,8 @@ pub(crate) struct UserPushAllowanceActor { /// Team that can be allowed to push to a branch in a repo #[derive(Clone, Deserialize, Debug, PartialEq, Eq)] pub(crate) struct TeamPushAllowanceActor { - organization: Login, - name: String, + pub(crate) organization: Login, + pub(crate) name: String, } pub(crate) enum BranchProtectionOp { diff --git a/src/github/api/write.rs b/src/github/api/write.rs index e727370..1c86e7c 100644 --- a/src/github/api/write.rs +++ b/src/github/api/write.rs @@ -1,9 +1,6 @@ -use anyhow::Context; use log::debug; use reqwest::Method; -use std::rc::Rc; -use crate::github::api::read::GithubRead; use crate::github::api::{ allow_not_found, BranchProtection, BranchProtectionOp, HttpClient, Login, PushAllowanceActor, Repo, RepoPermission, Team, TeamPrivacy, TeamPushAllowanceActor, TeamRole, @@ -14,19 +11,13 @@ use crate::utils::ResponseExt; pub(crate) struct GitHubWrite { client: HttpClient, dry_run: bool, - read: Rc, } impl GitHubWrite { - pub(crate) fn new( - client: HttpClient, - read: Rc, - dry_run: bool, - ) -> anyhow::Result { + pub(crate) fn new(client: HttpClient, dry_run: bool) -> anyhow::Result { Ok(Self { client: client.clone(), dry_run, - read, }) } @@ -55,6 +46,38 @@ impl GitHubWrite { Ok(data.user.id) } + fn team_id(&self, org: &str, name: &str) -> anyhow::Result { + #[derive(serde::Serialize)] + struct Params<'a> { + org: &'a str, + team: &'a str, + } + let query = " + query($org: String!, $team: String!) { + organization(login: $org) { + team(slug: $team) { + id + } + } + } + "; + #[derive(serde::Deserialize)] + struct Data { + organization: Organization, + } + #[derive(serde::Deserialize)] + struct Organization { + team: Team, + } + #[derive(serde::Deserialize)] + struct Team { + id: String, + } + + let data: Data = self.client.graphql(query, Params { org, team: name })?; + Ok(data.organization.team.id) + } + /// Create a team in a org pub(crate) fn create_team( &self, @@ -377,12 +400,7 @@ impl GitHubWrite { PushAllowanceActor::Team(TeamPushAllowanceActor { organization: Login { login: org }, name, - }) => push_actor_ids.push( - self.read - .team(org, name)? - .with_context(|| format!("could not find team: {org}/{name}"))? - .name, - ), + }) => push_actor_ids.push(self.team_id(org, name)?), } } diff --git a/src/github/mod.rs b/src/github/mod.rs index 9130b3c..ad5d493 100644 --- a/src/github/mod.rs +++ b/src/github/mod.rs @@ -3,12 +3,11 @@ mod api; mod tests; use self::api::{BranchProtectionOp, TeamPrivacy, TeamRole}; -use crate::github::api::{GithubRead, RepoPermission}; +use crate::github::api::{GithubRead, Login, PushAllowanceActor, RepoPermission}; use log::debug; use rust_team_data::v1::Bot; use std::collections::{HashMap, HashSet}; use std::fmt::Write; -use std::rc::Rc; pub(crate) use self::api::{GitHubApiRead, GitHubWrite, HttpClient}; @@ -16,7 +15,7 @@ static DEFAULT_DESCRIPTION: &str = "Managed by the rust-lang/team repository."; static DEFAULT_PRIVACY: TeamPrivacy = TeamPrivacy::Closed; pub(crate) fn create_diff( - github: Rc, + github: Box, teams: Vec, repos: Vec, ) -> anyhow::Result { @@ -25,7 +24,7 @@ pub(crate) fn create_diff( } struct SyncGitHub { - github: Rc, + github: Box, teams: Vec, repos: Vec, usernames_cache: HashMap, @@ -34,7 +33,7 @@ struct SyncGitHub { impl SyncGitHub { pub(crate) fn new( - github: Rc, + github: Box, teams: Vec, repos: Vec, ) -> anyhow::Result { @@ -454,7 +453,8 @@ fn construct_branch_protection( expected_repo: &rust_team_data::v1::Repo, branch_protection: &rust_team_data::v1::BranchProtection, ) -> api::BranchProtection { - let required_approving_review_count: u8 = if expected_repo.bots.contains(&Bot::Bors) { + let uses_bors = expected_repo.bots.contains(&Bot::Bors); + let required_approving_review_count: u8 = if uses_bors { 0 } else { branch_protection @@ -462,15 +462,24 @@ fn construct_branch_protection( .try_into() .expect("Too large required approval count") }; - let push_allowances = expected_repo - .bots - .contains(&Bot::Bors) - .then(|| { - vec![api::PushAllowanceActor::User(api::UserPushAllowanceActor { - login: "bors".to_owned(), - })] + let mut push_allowances: Vec = branch_protection + .allowed_merge_teams + .iter() + .map(|team| { + api::PushAllowanceActor::Team(api::TeamPushAllowanceActor { + organization: Login { + login: expected_repo.org.clone(), + }, + name: team.to_string(), + }) }) - .unwrap_or_default(); + .collect(); + + if uses_bors { + push_allowances.push(PushAllowanceActor::User(api::UserPushAllowanceActor { + login: "bors".to_owned(), + })); + } api::BranchProtection { pattern: branch_protection.pattern.clone(), is_admin_enforced: true, diff --git a/src/github/tests/test_utils.rs b/src/github/tests/test_utils.rs index 0b668a4..4a83d71 100644 --- a/src/github/tests/test_utils.rs +++ b/src/github/tests/test_utils.rs @@ -1,5 +1,4 @@ use std::collections::{HashMap, HashSet}; -use std::rc::Rc; use derive_builder::Builder; use rust_team_data::v1::{GitHubTeam, Person, TeamGitHub, TeamKind}; @@ -66,7 +65,7 @@ impl DataModel { let teams = self.teams.iter().map(|r| r.to_data()).collect(); let repos = vec![]; - let read = Rc::new(github); + let read = Box::new(github); let sync = SyncGitHub::new(read, teams, repos).expect("Cannot create SyncGitHub"); sync.diff_teams().expect("Cannot diff teams") } @@ -101,6 +100,7 @@ impl TeamData { alumni: vec![], github: (!gh_teams.is_empty()).then(|| TeamGitHub { teams: gh_teams }), website_data: None, + roles: vec![], discord: vec![], } } diff --git a/src/main.rs b/src/main.rs index 64cfc4a..757e078 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,6 @@ use crate::team_api::TeamApi; use crate::zulip::SyncZulip; use anyhow::Context; use log::{error, info, warn}; -use std::rc::Rc; const AVAILABLE_SERVICES: &[&str] = &["github", "mailgun", "zulip"]; const USER_AGENT: &str = "rust-lang teams sync (https://github.com/rust-lang/sync-team)"; @@ -85,13 +84,13 @@ fn app() -> anyhow::Result<()> { let token = get_env("GITHUB_TOKEN")?; let client = HttpClient::from_url_and_token("https://api.github.com/".to_string(), token)?; - let gh_read = Rc::new(GitHubApiRead::from_client(client.clone())?); + let gh_read = Box::new(GitHubApiRead::from_client(client.clone())?); let teams = team_api.get_teams()?; let repos = team_api.get_repos()?; - let diff = create_diff(gh_read.clone(), teams, repos)?; + let diff = create_diff(gh_read, teams, repos)?; info!("{}", diff); if !only_print_plan { - let gh_write = GitHubWrite::new(client, gh_read, dry_run)?; + let gh_write = GitHubWrite::new(client, dry_run)?; diff.apply(&gh_write)?; } }