From 5be1f4f2e27dbcc085d95ad9a3d19c5c80a15035 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Pi=C3=A9dallu?= Date: Thu, 15 Feb 2024 23:13:16 +0100 Subject: [PATCH] Use a state enum to handle already existing update prs --- .../autoupdate_app_sources.py | 84 ++++++++++++------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/autoupdate_app_sources/autoupdate_app_sources.py b/autoupdate_app_sources/autoupdate_app_sources.py index 03c962d..1dc4e43 100755 --- a/autoupdate_app_sources/autoupdate_app_sources.py +++ b/autoupdate_app_sources/autoupdate_app_sources.py @@ -4,6 +4,7 @@ import argparse import hashlib import multiprocessing import logging +from enum import Enum from typing import Any, Optional, Union import re import sys @@ -163,6 +164,13 @@ class LocalOrRemoteRepo: return next(pull.html_url for pull in self.repo.get_pulls(head=branch)) +class State(Enum): + up_to_date = 0 + already = 1 + created = 2 + failure = 3 + + class AppAutoUpdater: def __init__(self, app_id: Union[str, Path]) -> None: self.repo = LocalOrRemoteRepo(app_id) @@ -178,11 +186,10 @@ class AppAutoUpdater: self.main_upstream = self.manifest.get("upstream", {}).get("code") - def run(self, edit: bool = False, commit: bool = False, pr: bool = False - ) -> Union[bool, tuple[Optional[str], Optional[str], Optional[str]]]: - has_updates = False - main_version = None - pr_url = None + def run(self, edit: bool = False, commit: bool = False, pr: bool = False) -> tuple[State, str, str, str]: + state = State.up_to_date + main_version = "" + pr_url = "" # Default message pr_title = commit_msg = "Upgrade sources" @@ -192,7 +199,8 @@ class AppAutoUpdater: update = self.get_source_update(source, infos) if update is None: continue - has_updates = True + # We assume we'll create a PR + state = State.created version, assets, msg = update if source == "main": @@ -206,8 +214,8 @@ class AppAutoUpdater: self.repo.manifest_raw, version, assets, infos, is_main=source == "main", ) - if not has_updates: - return False + if state == State.up_to_date: + return (State.up_to_date, "", "", "") if edit: self.repo.edit_manifest(self.repo.manifest_raw) @@ -218,13 +226,14 @@ class AppAutoUpdater: if commit: self.repo.commit(commit_msg) if pr: - pr_url = self.repo.create_pr(branch_name, pr_title, commit_msg) + pr_url = self.repo.create_pr(branch_name, pr_title, commit_msg) or "" except github.GithubException as e: if e.status == 422 or e.status == 409: - pr_url = f"already existing pr: {self.repo.get_pr(branch_name)}" + state = State.already + pr_url = self.repo.get_pr(branch_name) else: raise - return self.current_version, main_version, pr_url + return (state, self.current_version, main_version, pr_url) @staticmethod def filter_and_get_latest_tag(tags: list[str], app_id: str) -> tuple[str, str]: @@ -276,7 +285,8 @@ class AppAutoUpdater: except Exception as e: raise RuntimeError(f"Failed to compute sha256 for {url} : {e}") from e - def get_source_update(self, name: str, infos: dict[str, Any]) -> Optional[tuple[str, Union[str, dict[str, str]], str]]: + def get_source_update(self, name: str, infos: dict[str, Any] + ) -> Optional[tuple[str, Union[str, dict[str, str]], str]]: if "autoupdate" not in infos: return None @@ -485,19 +495,17 @@ class StdoutSwitch: sys.stdout = self.save_stdout -def run_autoupdate_for_multiprocessing(data) -> Optional[tuple[bool, str, Any]]: +def run_autoupdate_for_multiprocessing(data) -> tuple[str, tuple[State, str, str, str]]: app, edit, commit, pr = data stdoutswitch = StdoutSwitch() try: result = AppAutoUpdater(app).run(edit=edit, commit=commit, pr=pr) - if result is False: - return None - return True, app, result + return (app, result) except Exception: log_str = stdoutswitch.reset() import traceback t = traceback.format_exc() - return False, app, f"{log_str}\n{t}" + return (app, (State.failure, log_str, str(t), "")) def main() -> None: @@ -525,23 +533,43 @@ def main() -> None: # Handle apps or no apps apps = list(args.apps) if args.apps else apps_to_run_auto_update_for() - apps_failed = {} + apps_already = {} # for which a PR already exists apps_updated = {} + apps_failed = {} with multiprocessing.Pool(processes=args.processes) as pool: tasks = pool.imap(run_autoupdate_for_multiprocessing, ((app, args.edit, args.commit, args.pr) for app in apps)) - for result in tqdm.tqdm(tasks, total=len(apps), ascii=" ·#"): - if result is None: + for app, result in tqdm.tqdm(tasks, total=len(apps), ascii=" ·#"): + state, current_version, main_version, pr_url = result + if state == State.up_to_date: continue - is_ok, app, info = result - if is_ok: - apps_updated[app] = info - else: - apps_failed[app] = info - pass + if state == State.already: + apps_already[app] = (current_version, main_version, pr_url) + if state == State.created: + apps_updated[app] = (current_version, main_version, pr_url) + if state == State.failure: + apps_failed[app] = current_version, main_version # actually stores logs result_message = "" + if apps_already: + result_message += f"\n{'=' * 80}\nApps already with an update PR:" + for app, info in apps_already.items(): + result_message += f"\n- {app}" + if isinstance(info, tuple): + result_message += f" ({info[0]} -> {info[1]})" + if info[2] is not None: + result_message += f" see {info[2]}" + + if apps_updated: + result_message += f"\n{'=' * 80}\nApps updated:" + for app, info in apps_updated.items(): + result_message += f"\n- {app}" + if isinstance(info, tuple): + result_message += f" ({info[0]} -> {info[1]})" + if info[2] is not None: + result_message += f" see {info[2]}" + if apps_updated: result_message += f"\n{'=' * 80}\nApps updated:" for app, info in apps_updated.items(): @@ -553,8 +581,8 @@ def main() -> None: if apps_failed: result_message += f"\n{'=' * 80}\nApps failed:" - for app, info in apps_failed.items(): - result_message += f"\n{'='*40}\n{app}\n{'-'*40}\n{info}\n\n" + for app, logs in apps_failed.items(): + result_message += f"\n{'='*40}\n{app}\n{'-'*40}\n{logs[0]}\n{logs[1]}\n\n" if apps_failed and args.paste: paste_url = paste_on_haste(result_message)