Skip to content

Commit 8a1a2e9

Browse files
authored
Merge pull request #816 from rylev/check-zulip-sync
Check that Zulip user groups are properly synced
2 parents 9c5b289 + 1f49f7d commit 8a1a2e9

File tree

3 files changed

+99
-3
lines changed

3 files changed

+99
-3
lines changed

src/check_synced.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,83 @@
22
33
use crate::data::Data;
44
use crate::github::{self, GitHubApi};
5-
use crate::schema;
5+
use crate::schema::{self, ZulipGroupMember};
6+
use crate::zulip::ZulipApi;
67
use log::{error, warn};
78
use rayon::prelude::*;
8-
use std::collections::HashMap;
9+
use std::collections::{HashMap, HashSet};
910

1011
pub(crate) fn check(data: &Data) -> Result<(), failure::Error> {
12+
check_github(data)?;
13+
check_zulip(data)?;
14+
Ok(())
15+
}
16+
17+
fn check_zulip(data: &Data) -> Result<(), failure::Error> {
18+
let zulip = ZulipApi::new();
19+
zulip.require_auth()?;
20+
let mut remote_groups = zulip
21+
.get_user_groups()?
22+
.into_iter()
23+
.filter(|g| !g.is_system_group)
24+
.map(|g| (g.name.clone(), g))
25+
.collect::<HashMap<_, _>>();
26+
let users = zulip
27+
.get_users()?
28+
.into_iter()
29+
.map(|u| (u.email, u.user_id))
30+
.collect::<HashMap<_, _>>();
31+
for (_, local_group) in &data.zulip_groups()? {
32+
match remote_groups.remove(local_group.name()) {
33+
Some(rg) => {
34+
let mut remote_members = rg.members.iter().collect::<HashSet<_>>();
35+
for local_member in local_group.members() {
36+
let i = match local_member {
37+
ZulipGroupMember::Id(i) => *i,
38+
ZulipGroupMember::Email(e) => match users.get(e) {
39+
Some(i) => *i,
40+
None => {
41+
error!("User email '{e}' is not on Zulip");
42+
continue;
43+
}
44+
},
45+
ZulipGroupMember::Missing => {
46+
error!("Member of Zulip user group '{}' does not have an email or Zulip id", local_group.name());
47+
continue;
48+
}
49+
};
50+
if !remote_members.remove(&i) {
51+
error!(
52+
"Zulip user '{:?}' is not in the remote Zulip user group",
53+
local_member
54+
)
55+
}
56+
}
57+
for remote_memember in remote_members {
58+
error!(
59+
"Zulip user '{:?}' is in the remote Zulip user group '{}' but not in the team repo",
60+
remote_memember,
61+
local_group.name()
62+
)
63+
}
64+
}
65+
None => error!(
66+
"User group '{}' is in the team repo but not on Zulip",
67+
local_group.name()
68+
),
69+
}
70+
}
71+
72+
for (_, remote_group) in remote_groups {
73+
error!(
74+
"Zulip group '{}' is on Zulip but not in team repo",
75+
remote_group.name
76+
)
77+
}
78+
Ok(())
79+
}
80+
81+
pub(crate) fn check_github(data: &Data) -> Result<(), failure::Error> {
1182
const BOT_TEAMS: &[&str] = &["bors", "bots", "rfcbot", "highfive"];
1283
let github = GitHubApi::new();
1384
let pending_invites = github.pending_org_invites()?;

src/schema.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ impl ZulipGroup {
619619
}
620620
}
621621

622-
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
622+
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
623623
pub(crate) enum ZulipGroupMember {
624624
Id(usize),
625625
Email(String),

src/zulip.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,17 @@ impl ZulipApi {
5656
Ok(response)
5757
}
5858

59+
/// Get all user groups of the Rust Zulip instance
60+
pub(crate) fn get_user_groups(&self) -> Result<Vec<ZulipUserGroup>, Error> {
61+
let response = self
62+
.req(Method::GET, "/user_groups", None)?
63+
.error_for_status()?
64+
.json::<ZulipUserGroups>()?
65+
.user_groups;
66+
67+
Ok(response)
68+
}
69+
5970
/// Perform a request against the Zulip API
6071
fn req(
6172
&self,
@@ -91,3 +102,17 @@ pub(crate) struct ZulipUser {
91102
pub(crate) email: String,
92103
pub(crate) user_id: usize,
93104
}
105+
106+
/// A collection of Zulip user groups
107+
#[derive(Deserialize)]
108+
struct ZulipUserGroups {
109+
user_groups: Vec<ZulipUserGroup>,
110+
}
111+
112+
/// A single Zulip user group
113+
#[derive(Deserialize)]
114+
pub(crate) struct ZulipUserGroup {
115+
pub(crate) name: String,
116+
pub(crate) members: Vec<usize>,
117+
pub(crate) is_system_group: bool,
118+
}

0 commit comments

Comments
 (0)