Page MenuHomeSoftware Heritage

No OneTemporary

diff --git a/gitlab/manage_users_groups.py b/gitlab/manage_users_groups.py
new file mode 100755
index 0000000..a6f5263
--- /dev/null
+++ b/gitlab/manage_users_groups.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python3
+
+import logging
+
+import click
+import yaml
+
+import gitlab
+
+logger = logging.getLogger(__name__)
+
+
+@click.command()
+@click.option(
+ "--gitlab",
+ "-g",
+ "gitlab_instance",
+ help="Which GitLab instance to use, as configured in the python-gitlab config",
+)
+@click.option(
+ "--do-it",
+ "do_it",
+ is_flag=True,
+ help="Actually perform the operations",
+)
+@click.argument("config_file")
+def cli(gitlab_instance, do_it, config_file):
+ """Ensure that GitLab users and group memberships match the structure defined in the
+ configuration file. This uses the python-gitlab configuration parsing."""
+ gl = gitlab.Gitlab.from_config(gitlab_instance)
+ gl.auth()
+
+ with open(config_file, "r") as f:
+ config = yaml.safe_load(f)
+
+ if not do_it:
+ logger.info(
+ "Will not perform any actions, please use --do-it once you're satisfied"
+ " with the expected actions."
+ )
+
+ for group_path, group_conf in config["groups"].items():
+ group = gl.groups.get(group_path, with_projects=False)
+ expected_members = {
+ username: gitlab.const.AccessLevel[access_level.upper()]
+ for username, access_level in group_conf["users"].items()
+ }
+ recorded_members = set()
+
+ expected_group_shares = {
+ other_path: gitlab.const.AccessLevel[access_level.upper()]
+ for other_path, access_level in group_conf.get(
+ "share_with_groups", {}
+ ).items()
+ }
+ recorded_members = set()
+
+ remove_extra_memberships = group_conf.get("remove_extra_memberships", False)
+ for member in group.members.list():
+ username = member.username
+ expected_access_level = expected_members.get(member.username)
+ if expected_access_level and member.access_level != expected_access_level:
+ logger.info(
+ "Adjusting membership for %s in %s to %s (was %s)",
+ username,
+ group_path,
+ expected_access_level.name,
+ member.access_level,
+ )
+ if do_it:
+ member.access_level = expected_access_level
+ member.save()
+
+ if remove_extra_memberships and not expected_access_level:
+ logger.info("Removing member %s from %s", username, group_path)
+ if do_it:
+ member.delete()
+
+ recorded_members.add(username)
+
+ for username, access_level in expected_members.items():
+ if username in recorded_members:
+ continue
+
+ users = gl.users.list(username=username)
+ if not users:
+ logger.warning(
+ "User %s not found, cannot add them to %s!", username, group_path
+ )
+ continue
+
+ user_id = users[0].id
+
+ logger.info("Adding member %s in %s at level %s", username, access_level)
+ if do_it:
+ group.members.create({"user_id": user_id, "access_level": access_level})
+
+ recorded_group_shares = set()
+
+ for group_share in group.shared_with_groups:
+ other_path = group_share["group_full_path"]
+ other_id = group_share["group_id"]
+ other_access_level = group_share["group_access_level"]
+ expected_access_level = expected_group_shares.get(other_path)
+ if expected_access_level and other_access_level != expected_access_level:
+ logger.info(
+ "Adjusting group_share for %s in %s to %s (was %s)",
+ other_path,
+ group_path,
+ expected_access_level,
+ other_access_level,
+ )
+ if do_it:
+ group.share(other_id, expected_access_level)
+
+ if remove_extra_memberships and not expected_access_level:
+ logger.info("Removing group %s from %s", other_path, group_path)
+ if do_it:
+ group.unshare(other_id)
+
+ recorded_group_shares.add(other_path)
+
+ for other_path, access_level in expected_group_shares.items():
+ if other_path in recorded_group_shares:
+ continue
+
+ other_group = gl.groups.get(other_path)
+ if not other_group:
+ logger.warning(
+ "Group %s not found, cannot add them to %s!", other_path, group_path
+ )
+ continue
+
+ logger.info(
+ "Adding group %s in %s at level %s",
+ other_path,
+ group_path,
+ access_level,
+ )
+ if do_it:
+ group.share(other_group.id, access_level)
+
+
+if __name__ == "__main__":
+ logging.basicConfig(
+ level=logging.INFO, format="%(asctime)s %(name)s:%(levelname)s %(message)s"
+ )
+ cli()
diff --git a/gitlab/users_groups.yml b/gitlab/users_groups.yml
new file mode 100644
index 0000000..a7a32ce
--- /dev/null
+++ b/gitlab/users_groups.yml
@@ -0,0 +1,80 @@
+# Owner
+# Maintainer
+# Developer
+# Reporter
+# Guest
+
+groups:
+ teams/staff:
+ users:
+ phabricator-migration: Owner
+ anlambert: Maintainer
+ ardumont: Maintainer
+ bchauvet: Owner
+ douardda: Maintainer
+ jayeshv: Maintainer
+ lunar: Maintainer
+ marla.dasilva: Maintainer
+ moranegg: Maintainer
+ olasd: Maintainer
+ rdicosmo: Owner
+ sgranger: Maintainer
+ vlorentz: Maintainer
+ vsellier: Maintainer
+ zack: Owner
+ remove_extra_memberships: true
+ teams/developers:
+ users:
+ phabricator-migration: Owner
+ anlambert: Maintainer
+ ardumont: Maintainer
+ bchauvet: Maintainer
+ douardda: Maintainer
+ jayeshv: Maintainer
+ lunar: Maintainer
+ moranegg: Maintainer
+ olasd: Maintainer
+ rdicosmo: Maintainer
+ vlorentz: Maintainer
+ vsellier: Maintainer
+ zack: Maintainer
+ remove_extra_memberships: true
+ teams/sysadmin:
+ users:
+ phabricator-migration: Owner
+ ardumont: Owner
+ olasd: Owner
+ vsellier: Owner
+ remove_extra_memberships: true
+ teams/interns:
+ users:
+ phabricator-migration: Owner
+ remove_extra_memberships: true
+ teams/management:
+ users:
+ phabricator-migration: Owner
+ bchauvet: Owner
+ douardda: Maintainer
+ rdicosmo: Owner
+ vsellier: Maintainer
+ zack: Owner
+ remove_extra_memberships: true
+ infra:
+ users:
+ phabricator-migration: Owner
+ remove_extra_memberships: true
+ share_with_groups:
+ teams/sysadmin: Owner
+ modules:
+ users:
+ phabricator-migration: Owner
+ share_with_groups:
+ teams/developers: Maintainer
+ remove_extra_memberships: true
+ migrated:
+ users:
+ phabricator-migration: Owner
+ share_with_groups:
+ teams/sysadmin: Owner
+ teams/developers: Maintainer
+ remove_extra_memberships: true

File Metadata

Mime Type
text/x-diff
Expires
Fri, Jul 4, 11:27 AM (3 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3300904

Event Timeline