From 8055d7c2597e14a7f8554b31c0f935f282e722df Mon Sep 17 00:00:00 2001 From: Mattia Verga Date: Jul 15 2023 09:11:23 +0000 Subject: Post comment about affected packages, users and groups Signed-off-by: Mattia Verga --- diff --git a/find_inactive_packagers.py b/find_inactive_packagers.py index 4311161..48c1b63 100644 --- a/find_inactive_packagers.py +++ b/find_inactive_packagers.py @@ -41,6 +41,8 @@ Optionally, it can open tickets against inactive packagers in a Pagure repositor The check-impact subcommand will iterate on the list of tickets opened and provide information about how many packages will possibly be orphaned based on the packagers detected as inactive. +It can be used with the `--post-comment` to post a comment on each inactive_packager ticket +with a list of packages / users / groups affected by user removal. The step-two subcommand can either check a list of inactive users from the csv file generated in step-one, or check a list of users based on open Pagure tickets. In this last case, the tickets @@ -123,6 +125,14 @@ Have a wonderful day and see you (maybe?) at the next run! ''' +CHECK_IMPACT_COMMENT = '''The following is a list of packages that may become orphan if the user +is removed from the `packager` group. Affected co-maintainers users and groups are provided +as well: + +{package_list} + +''' + logging.basicConfig(level=logging.INFO, format='%(message)s') log = logging.getLogger('find-inactive-packagers') @@ -181,6 +191,22 @@ def _fetch_tickets(): return user_ticket_waiting_reply, user_ticket_pending_removal +def _comment_pagure_ticket(ticket_id, comment): + """Post a comment on pagure ticket.""" + headers = {'Authorization': f'token {PAGURE_API_KEY}'} + data = {'comment': comment} + try: + log.debug(f'Posting comment on ticket {ticket_id}') + resp = session.post(f'{PAGURE_API_BASE_URL}/issue/{ticket_id}/comment', data=data, headers=headers) + if resp.status_code == 401: + log.error('ERROR: Invalid or expired Pagure token, queue processing will stop immediately.') + sys.exit('Invalid or expired Pagure token, queue processing will stop immediately.') + if resp.status_code != 200: + log.error(f'ERROR: Error posting comment on ticket {ticket_id}') + except Exception: + log.error(f'ERROR: Error posting comment on ticket {ticket_id}') + + def _close_pagure_ticket(ticket_id, close_status, comment=''): """Close pagure ticket with comment.""" headers = {'Authorization': f'token {PAGURE_API_KEY}'} @@ -443,6 +469,46 @@ def mask_email(email, preserve_chars=3, mask_char='*', privacy=True): return f'{local_part}@{valid_email[1]}' +def _get_affected_packages_users(session, username, mark=''): + """Get the list of packages orphaned when a user is removed from packager group. + + Also get a list of affected co-maintainers impacted by the orphaning process. + + Args: + username: the username which will be removed from packagers. + mark: used in 'check-impact' + Returns: + A list of tuples containing: + (package name, username, mark, set(co-maintainers, set(affected groups)) + """ + page = 1 + list_affected = [] + while True: + try: + data = session.get(f'https://src.fedoraproject.org/api/0/user/{username}' + f'?per_page=100&repopage={page}').json() + repos = data.get('repos', []) + total_pages = data.get('repos_pagination', dict()).get('pages', 1) + for repo in repos: + if username in repo.get('access_users', dict()).get('owner'): + co_maintainers = set(repo.get('access_users').get('admin', []) + + repo.get('access_users').get('collaborator', []) + + repo.get('access_users').get('commit', [])) + groups = set(repo.get('access_groups').get('admin', []) + + repo.get('access_groups').get('collaborator', []) + + repo.get('access_groups').get('commit', [])) + list_affected.append((repo.get('name'), username, mark, co_maintainers, groups)) + log.info(f"Done {page} pages out of {total_pages}.") + if page >= total_pages: + break + page += 1 + except Exception as e: + log.warning('Error while retrieving user info.') + print(e) + break + return list_affected + + @click.group() @click.option('--privacy', is_flag=True, default=False, help='Hide users email in log.') @click.option('-D', '--debug', is_flag=True, default=False, help='Enable logging of debug messages.') @@ -744,8 +810,9 @@ def step_two(ctx, with_bz_check, close_tickets, from_file): @cli.command() @click.option('--on-the-fly', 'otf', is_flag=True, default=False, help='Check user activity on the fly.') +@click.option('--post-comment', 'comment', is_flag=True, default=False, help='Post comments on pagure tickets.') @click.pass_context -def check_impact(ctx, otf): +def check_impact(ctx, comment, otf): """Report packages expected to be orphaned. This report will show how many packages are going to be orphaned based on the @@ -782,48 +849,31 @@ def check_impact(ctx, otf): affected_packages = [] log.info(f'### Fetching affected users info ###') for u, t in waiting.items(): - page = 1 log.info(f'Getting user {u} information') if otf: if _check_user_activity(u, privacy=privacy, fasclient=fasclient, bzclient=bzclient): log.info(f"Activity detected for user {u}.") continue - while True: - try: - data = session.get(f'https://src.fedoraproject.org/api/0/user/{u}' - f'?per_page=100&repopage={page}').json() - repos = data.get('repos', []) - total_pages = data.get('repos_pagination', dict()).get('pages', 1) - for repo in repos: - if u in repo.get('access_users', dict()).get('owner'): - affected_packages.append((repo.get('name'), u, '')) - log.info(f"Done {page} pages out of {total_pages}.") - if page >= total_pages: - break - page += 1 - except Exception: - log.warning('Error while retrieving user info.') + affected_packages.extend(_get_affected_packages_users(session, u)) for u, t in pending.items(): - page = 1 log.info(f'Getting user {u} information') - while True: - try: - data = session.get(f'https://src.fedoraproject.org/api/0/user/{u}' - f'?per_page=100&repopage={page}').json() - repos = data.get('repos', []) - total_pages = data.get('repos_pagination', dict()).get('pages', 1) - for repo in repos: - if u in repo.get('access_users', dict()).get('owner'): - affected_packages.append((repo.get('name'), u, '*')) - log.info(f"Done {page} pages out of {total_pages}.") - if page >= total_pages: - break - page += 1 - except Exception: - log.warning('Error while retrieving ticket list.') - package_list = "\n".join([f'- {mark}{package} (owned by {user})' for package, user, mark in sorted(affected_packages)]) + affected_packages.extend(_get_affected_packages_users(session, u, '*')) + package_list = "\n".join([f'- {mark}{package} (owned by {user})' for package, user, mark, co_maintainers, groups in sorted(affected_packages)]) log.info(CHECK_IMPACT_OUTPUT.format(total_packages=len(affected_packages), package_list=package_list)) + if comment: + tickets = waiting | pending + for u, t in tickets.items(): + user_package_map = [] + for package, user, mark, co_maintainers, groups in sorted(affected_packages): + if user == u: + affected_users = ", ".join([f'@{affected_user}' for affected_user in co_maintainers]) + affected_groups = ", ".join([f'@{affected_group}' for affected_group in groups]) + user_package_map.append((package, affected_users, affected_groups)) + package_list_comment = "\n".join([f'- {package}{" (affected users: "+affected_users+")" if affected_users else ""}{" (affected groups: "+affected_groups+")" if affected_groups else ""}' for package, affected_users, affected_groups in user_package_map]) + if package_list_comment: + _comment_pagure_ticket(t, CHECK_IMPACT_COMMENT.format(package_list=package_list_comment)) + @cli.command() @click.argument('username')