Issue tracker augmentation
If you find the issue tracking software you’re using lacks specific process or functionality, it may be possible to augment it through fairly straightforward use of its API.
#!/usr/bin/env python3
"""
This script scans all issues in GitLab and performs arbitrary
modifications. It's currently set up to ensure that every issue
is assigned to at least one user, defaulting to a random choice
between the project's owner and maintainers.
This is just a demonstration of how you can augment an issue
tracker's process with a simple cronjob. Left as an exercise to
the reader is how one would go about doing this reactively with
webhooks:
https://docs.gitlab.com/ee/user/project/integrations/webhooks.html#issue-events
"""
import gitlab
import logging
import os
import random
ASSIGNEE_ACCESS_LEVELS = set([
gitlab.MAINTAINER_ACCESS,
gitlab.OWNER_ACCESS,
])
def get_active_project_members(gl, project):
# The documentation (https://docs.gitlab.com/ee/api/members.html)
# is unclear whether the state attribute of a membership object
# represents the state of the membership or the state of the user.
# To be safe I'm checking both, but we could investigate if it
# refers to the latter and save a check in the process.
return [
member
for member in project.members.list(as_list=False)
if member.state == "active"
and get_cached_user(gl, member.username).state == "active"
]
def get_default_assignees(gl, project):
return [
member
for member in get_active_project_members(gl, project)
if member.access_level in ASSIGNEE_ACCESS_LEVELS
]
def adjust_project_issues(gl, project):
default_assignees = None
for issue in project.issues.list(as_list=False):
if not any((
user["state"] == "active"
for user in issue.assignees
)):
default_assignees = default_assignees or get_default_assignees(gl, project)
assigned_user = random.choice(default_assignees)
logger.info(f"Assigning {assigned_user.username} to {issue.web_url}")
issue.assignee_id = get_cached_user(gl, assigned_user.username).id
issue.save()
def get_cached_user(gl, username):
if not hasattr(gl, "cached_users"):
gl.cached_users = {}
if username not in gl.cached_users:
gl.cached_users[username] = gl.users.list(username=username)[0]
return gl.cached_users[username]
if __name__ == "__main__":
logging.basicConfig(level=os.environ.get("LOGLEVEL") or "INFO")
logger = logging.getLogger()
logger.debug(f"Connecting to {os.environ['GITLAB_URL']}")
with gitlab.Gitlab(
os.environ["GITLAB_URL"],
private_token=os.environ["GITLAB_TOKEN"],
) as gl:
gl.auth()
for project in gl.projects.list(as_list=False):
logger.debug(f"Considering {project.name}")
adjust_project_issues(gl, project)