From 9c657f238d9a1354536dcb0a2e89adf5dd6cef4b Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 17 Jul 2019 12:06:29 +0200 Subject: add github teams in the schema and the api This will allow synchronization tools for GitHub teams to be built. No teams are currently configured to be synchronized. --- config.toml | 5 +++++ docs/toml-schema.md | 6 ++++++ rust_team_data/src/v1.rs | 12 ++++++++++++ src/schema.rs | 26 ++++++++++++++++++++++++++ src/static_api.rs | 13 +++++++++++++ src/validate.rs | 29 +++++++++++++++++++++++++++++ 6 files changed, 91 insertions(+) diff --git a/config.toml b/config.toml index e62c24b..5159efb 100644 --- a/config.toml +++ b/config.toml @@ -3,3 +3,8 @@ allowed-mailing-lists-domains = [ "crates.io", "docs.rs", ] + +allowed-github-orgs = [ + "rust-lang", + "rust-lang-nursery", +] diff --git a/docs/toml-schema.md b/docs/toml-schema.md index 208ceed..0147d6d 100644 --- a/docs/toml-schema.md +++ b/docs/toml-schema.md @@ -45,6 +45,12 @@ members = [ [permissions] # Optional, see the permissions documentation +# Configure the GitHub integration +# This is optional, and if missing the team won't be synchronized with GitHub +[github] +name = "overlords-team" # The name of the GitHub team (optional) +orgs = ["rust-lang"] # Organizations to create the team in (required) + # Define the mailing lists used by the team # It's optional, and there can be more than one [[lists]] diff --git a/rust_team_data/src/v1.rs b/rust_team_data/src/v1.rs index 839c66d..b582f4f 100644 --- a/rust_team_data/src/v1.rs +++ b/rust_team_data/src/v1.rs @@ -16,6 +16,7 @@ pub struct Team { pub kind: TeamKind, pub subteam_of: Option, pub members: Vec, + pub github: Option, pub website_data: Option, } @@ -27,6 +28,17 @@ pub struct TeamMember { pub is_lead: bool, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TeamGitHub { + pub teams: Vec, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GitHubTeam { + pub org: String, + pub name: String, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct TeamWebsite { pub name: String, diff --git a/src/schema.rs b/src/schema.rs index 570b212..779e86c 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -7,12 +7,17 @@ use std::collections::HashSet; #[serde(deny_unknown_fields, rename_all = "kebab-case")] pub(crate) struct Config { allowed_mailing_lists_domains: HashSet, + allowed_github_orgs: HashSet, } impl Config { pub(crate) fn allowed_mailing_lists_domains(&self) -> &HashSet { &self.allowed_mailing_lists_domains } + + pub(crate) fn allowed_github_orgs(&self) -> &HashSet { + &self.allowed_github_orgs + } } // This is an enum to allow two kinds of values for the email field: @@ -108,6 +113,7 @@ pub(crate) struct Team { people: TeamPeople, #[serde(default)] permissions: Permissions, + github: Option, rfcbot: Option, website: Option, #[serde(default)] @@ -213,6 +219,19 @@ impl Team { pub(crate) fn permissions(&self) -> &Permissions { &self.permissions } + + pub(crate) fn github_teams(&self) -> Vec<(&str, &str)> { + if let Some(github) = &self.github { + let name = github + .name + .as_ref() + .map(|n| n.as_str()) + .unwrap_or(&self.name); + github.orgs.iter().map(|org| (org.as_str(), name)).collect() + } else { + Vec::new() + } + } } #[derive(serde_derive::Deserialize, Debug)] @@ -228,6 +247,13 @@ struct TeamPeople { include_all_team_members: bool, } +#[derive(serde::Deserialize, Debug)] +#[serde(rename_all = "kebab-case", deny_unknown_fields)] +struct GitHubData { + name: Option, + orgs: Vec, +} + #[derive(serde_derive::Deserialize, Debug)] #[serde(rename_all = "kebab-case", deny_unknown_fields)] pub(crate) struct RfcbotData { diff --git a/src/static_api.rs b/src/static_api.rs index 15e1dcd..3760b4e 100644 --- a/src/static_api.rs +++ b/src/static_api.rs @@ -48,6 +48,9 @@ impl<'a> Generator<'a> { members.sort_by_key(|member| member.github.to_lowercase()); members.sort_by_key(|member| !member.is_lead); + let mut github_teams = team.github_teams(); + github_teams.sort(); + let team_data = v1::Team { name: team.name().into(), kind: if team.is_wg() { @@ -57,6 +60,16 @@ impl<'a> Generator<'a> { }, subteam_of: team.subteam_of().map(|st| st.into()), members, + github: Some(v1::TeamGitHub { + teams: github_teams + .iter() + .map(|(org, name)| v1::GitHubTeam { + org: org.to_string(), + name: name.to_string(), + }) + .collect::>(), + }) + .filter(|gh| !gh.teams.is_empty()), website_data: team.website_data().map(|ws| v1::TeamWebsite { name: ws.name().into(), description: ws.description().into(), diff --git a/src/validate.rs b/src/validate.rs index 4c4ebfb..e33f391 100644 --- a/src/validate.rs +++ b/src/validate.rs @@ -23,6 +23,7 @@ static CHECKS: &[fn(&Data, &mut Vec)] = &[ validate_rfcbot_labels, validate_rfcbot_exclude_members, validate_team_names, + validate_github_teams, ]; static GITHUB_CHECKS: &[fn(&Data, &GitHubApi, &mut Vec)] = &[validate_github_usernames]; @@ -381,6 +382,34 @@ fn validate_team_names(data: &Data, errors: &mut Vec) { }); } +/// Ensure GitHub teams are unique and in the allowed orgs +fn validate_github_teams(data: &Data, errors: &mut Vec) { + let mut found = HashMap::new(); + let allowed = data.config().allowed_github_orgs(); + wrapper(data.teams(), errors, |team, errors| { + wrapper(team.github_teams().into_iter(), errors, |(org, name), _| { + if !allowed.contains(&*org) { + bail!( + "GitHub organization `{}` isn't allowed (in team `{}`)", + org, + team.name() + ); + } + if let Some(other) = found.insert((org, name), team.name()) { + bail!( + "GitHub team `{}/{}` is defined for both the `{}` and `{}` teams", + org, + name, + team.name(), + other + ); + } + Ok(()) + }); + Ok(()) + }); +} + /// Ensure there are no misspelled GitHub account names fn validate_github_usernames(data: &Data, github: &GitHubApi, errors: &mut Vec) { let people = data -- cgit v1.2.3