diff --git a/docs/toml-schema.md b/docs/toml-schema.md index b801acd5e..8da8422c0 100644 --- a/docs/toml-schema.md +++ b/docs/toml-schema.md @@ -306,4 +306,9 @@ dismiss-stale-review = false # This option is only relevant if bors is not used. # (optional - default `1`) required-approvals = 1 +# Which GitHub teams have access to push/merge to this branch. +# If unspecified, all teams/contributors with write or higher access +# can push/merge to the branch. +# (optional) +allowed-merge-teams = ["awesome-team"] ``` diff --git a/rust_team_data/src/v1.rs b/rust_team_data/src/v1.rs index 2ca2c063d..d02b73431 100644 --- a/rust_team_data/src/v1.rs +++ b/rust_team_data/src/v1.rs @@ -196,6 +196,7 @@ pub struct BranchProtection { pub ci_checks: Vec, pub dismiss_stale_review: bool, pub required_approvals: u32, + pub allowed_merge_teams: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] diff --git a/src/schema.rs b/src/schema.rs index d6bd70b22..2c9283236 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -782,4 +782,6 @@ pub(crate) struct BranchProtection { pub dismiss_stale_review: bool, #[serde(default)] pub required_approvals: Option, + #[serde(default)] + pub allowed_merge_teams: Vec, } diff --git a/src/static_api.rs b/src/static_api.rs index 4a49f0cc6..58334d78b 100644 --- a/src/static_api.rs +++ b/src/static_api.rs @@ -45,6 +45,7 @@ impl<'a> Generator<'a> { ci_checks: b.ci_checks.clone(), dismiss_stale_review: b.dismiss_stale_review, required_approvals: b.required_approvals.unwrap_or(1), + allowed_merge_teams: b.allowed_merge_teams.clone(), }) .collect(); let repo = v1::Repo { diff --git a/src/validate.rs b/src/validate.rs index 5357e4026..b63b19d22 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -778,16 +778,41 @@ fn validate_repos(data: &Data, errors: &mut Vec) { /// Validate that branch protections make sense in combination with used bots. fn validate_branch_protections(data: &Data, errors: &mut Vec) { + let github_teams = data.github_teams(); + wrapper(data.repos(), errors, |repo, _| { let bors_used = repo.bots.iter().any(|b| matches!(b, Bot::Bors)); for protection in &repo.branch_protections { - if bors_used && protection.required_approvals.is_some() { - bail!( - r#"repo '{}' uses bors and its branch protection for {} uses the `required-approvals` attribute; + for team in &protection.allowed_merge_teams { + let key = (repo.org.clone(), team.clone()); + if !github_teams.contains(&key) { + bail!( + r#"repo '{}' uses a branch protection for {} that mentions the '{}' github team; +but that team does not seem to exist"#, + repo.name, + protection.pattern, + team + ); + } + } + + if bors_used { + if protection.required_approvals.is_some() { + bail!( + r#"repo '{}' uses bors and its branch protection for {} uses the `required-approvals` attribute; please remove the attribute when using bors"#, - repo.name, - protection.pattern, - ); + repo.name, + protection.pattern, + ); + } + if !protection.allowed_merge_teams.is_empty() { + bail!( + r#"repo '{}' uses bors and its branch protection for {} uses the `allowed-merge-teams` attribute; +please remove the attribute when using bors"#, + repo.name, + protection.pattern, + ); + } } } Ok(()) diff --git a/tests/static-api/_expected/v1/repos.json b/tests/static-api/_expected/v1/repos.json index f8f9e1a01..abc1ddbd4 100644 --- a/tests/static-api/_expected/v1/repos.json +++ b/tests/static-api/_expected/v1/repos.json @@ -19,7 +19,10 @@ "CI" ], "dismiss_stale_review": false, - "required_approvals": 1 + "required_approvals": 1, + "allowed_merge_teams": [ + "foo" + ] } ] } diff --git a/tests/static-api/_expected/v1/repos/some_repo.json b/tests/static-api/_expected/v1/repos/some_repo.json index fa8560285..500e01509 100644 --- a/tests/static-api/_expected/v1/repos/some_repo.json +++ b/tests/static-api/_expected/v1/repos/some_repo.json @@ -17,7 +17,10 @@ "CI" ], "dismiss_stale_review": false, - "required_approvals": 1 + "required_approvals": 1, + "allowed_merge_teams": [ + "foo" + ] } ] } \ No newline at end of file diff --git a/tests/static-api/repos/test-org/some_repo.toml b/tests/static-api/repos/test-org/some_repo.toml index 76ca6c169..d74cab8f7 100644 --- a/tests/static-api/repos/test-org/some_repo.toml +++ b/tests/static-api/repos/test-org/some_repo.toml @@ -8,4 +8,5 @@ foo = "admin" [[branch-protections]] pattern = "master" -ci-checks = ["CI"] \ No newline at end of file +ci-checks = ["CI"] +allowed-merge-teams = ["foo"]