Use a state enum to handle already existing update prs
This commit is contained in:
parent
83ae0689b0
commit
5be1f4f2e2
1 changed files with 56 additions and 28 deletions
|
@ -4,6 +4,7 @@ import argparse
|
||||||
import hashlib
|
import hashlib
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import logging
|
import logging
|
||||||
|
from enum import Enum
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Optional, Union
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
@ -163,6 +164,13 @@ class LocalOrRemoteRepo:
|
||||||
return next(pull.html_url for pull in self.repo.get_pulls(head=branch))
|
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:
|
class AppAutoUpdater:
|
||||||
def __init__(self, app_id: Union[str, Path]) -> None:
|
def __init__(self, app_id: Union[str, Path]) -> None:
|
||||||
self.repo = LocalOrRemoteRepo(app_id)
|
self.repo = LocalOrRemoteRepo(app_id)
|
||||||
|
@ -178,11 +186,10 @@ class AppAutoUpdater:
|
||||||
|
|
||||||
self.main_upstream = self.manifest.get("upstream", {}).get("code")
|
self.main_upstream = self.manifest.get("upstream", {}).get("code")
|
||||||
|
|
||||||
def run(self, edit: bool = False, commit: bool = False, pr: bool = False
|
def run(self, edit: bool = False, commit: bool = False, pr: bool = False) -> tuple[State, str, str, str]:
|
||||||
) -> Union[bool, tuple[Optional[str], Optional[str], Optional[str]]]:
|
state = State.up_to_date
|
||||||
has_updates = False
|
main_version = ""
|
||||||
main_version = None
|
pr_url = ""
|
||||||
pr_url = None
|
|
||||||
|
|
||||||
# Default message
|
# Default message
|
||||||
pr_title = commit_msg = "Upgrade sources"
|
pr_title = commit_msg = "Upgrade sources"
|
||||||
|
@ -192,7 +199,8 @@ class AppAutoUpdater:
|
||||||
update = self.get_source_update(source, infos)
|
update = self.get_source_update(source, infos)
|
||||||
if update is None:
|
if update is None:
|
||||||
continue
|
continue
|
||||||
has_updates = True
|
# We assume we'll create a PR
|
||||||
|
state = State.created
|
||||||
version, assets, msg = update
|
version, assets, msg = update
|
||||||
|
|
||||||
if source == "main":
|
if source == "main":
|
||||||
|
@ -206,8 +214,8 @@ class AppAutoUpdater:
|
||||||
self.repo.manifest_raw, version, assets, infos, is_main=source == "main",
|
self.repo.manifest_raw, version, assets, infos, is_main=source == "main",
|
||||||
)
|
)
|
||||||
|
|
||||||
if not has_updates:
|
if state == State.up_to_date:
|
||||||
return False
|
return (State.up_to_date, "", "", "")
|
||||||
|
|
||||||
if edit:
|
if edit:
|
||||||
self.repo.edit_manifest(self.repo.manifest_raw)
|
self.repo.edit_manifest(self.repo.manifest_raw)
|
||||||
|
@ -218,13 +226,14 @@ class AppAutoUpdater:
|
||||||
if commit:
|
if commit:
|
||||||
self.repo.commit(commit_msg)
|
self.repo.commit(commit_msg)
|
||||||
if pr:
|
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:
|
except github.GithubException as e:
|
||||||
if e.status == 422 or e.status == 409:
|
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:
|
else:
|
||||||
raise
|
raise
|
||||||
return self.current_version, main_version, pr_url
|
return (state, self.current_version, main_version, pr_url)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def filter_and_get_latest_tag(tags: list[str], app_id: str) -> tuple[str, str]:
|
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:
|
except Exception as e:
|
||||||
raise RuntimeError(f"Failed to compute sha256 for {url} : {e}") from 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:
|
if "autoupdate" not in infos:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -485,19 +495,17 @@ class StdoutSwitch:
|
||||||
sys.stdout = self.save_stdout
|
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
|
app, edit, commit, pr = data
|
||||||
stdoutswitch = StdoutSwitch()
|
stdoutswitch = StdoutSwitch()
|
||||||
try:
|
try:
|
||||||
result = AppAutoUpdater(app).run(edit=edit, commit=commit, pr=pr)
|
result = AppAutoUpdater(app).run(edit=edit, commit=commit, pr=pr)
|
||||||
if result is False:
|
return (app, result)
|
||||||
return None
|
|
||||||
return True, app, result
|
|
||||||
except Exception:
|
except Exception:
|
||||||
log_str = stdoutswitch.reset()
|
log_str = stdoutswitch.reset()
|
||||||
import traceback
|
import traceback
|
||||||
t = traceback.format_exc()
|
t = traceback.format_exc()
|
||||||
return False, app, f"{log_str}\n{t}"
|
return (app, (State.failure, log_str, str(t), ""))
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
@ -525,23 +533,43 @@ def main() -> None:
|
||||||
|
|
||||||
# Handle apps or no apps
|
# Handle apps or no apps
|
||||||
apps = list(args.apps) if args.apps else apps_to_run_auto_update_for()
|
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_updated = {}
|
||||||
|
apps_failed = {}
|
||||||
|
|
||||||
with multiprocessing.Pool(processes=args.processes) as pool:
|
with multiprocessing.Pool(processes=args.processes) as pool:
|
||||||
tasks = pool.imap(run_autoupdate_for_multiprocessing,
|
tasks = pool.imap(run_autoupdate_for_multiprocessing,
|
||||||
((app, args.edit, args.commit, args.pr) for app in apps))
|
((app, args.edit, args.commit, args.pr) for app in apps))
|
||||||
for result in tqdm.tqdm(tasks, total=len(apps), ascii=" ·#"):
|
for app, result in tqdm.tqdm(tasks, total=len(apps), ascii=" ·#"):
|
||||||
if result is None:
|
state, current_version, main_version, pr_url = result
|
||||||
|
if state == State.up_to_date:
|
||||||
continue
|
continue
|
||||||
is_ok, app, info = result
|
if state == State.already:
|
||||||
if is_ok:
|
apps_already[app] = (current_version, main_version, pr_url)
|
||||||
apps_updated[app] = info
|
if state == State.created:
|
||||||
else:
|
apps_updated[app] = (current_version, main_version, pr_url)
|
||||||
apps_failed[app] = info
|
if state == State.failure:
|
||||||
pass
|
apps_failed[app] = current_version, main_version # actually stores logs
|
||||||
|
|
||||||
result_message = ""
|
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:
|
if apps_updated:
|
||||||
result_message += f"\n{'=' * 80}\nApps updated:"
|
result_message += f"\n{'=' * 80}\nApps updated:"
|
||||||
for app, info in apps_updated.items():
|
for app, info in apps_updated.items():
|
||||||
|
@ -553,8 +581,8 @@ def main() -> None:
|
||||||
|
|
||||||
if apps_failed:
|
if apps_failed:
|
||||||
result_message += f"\n{'=' * 80}\nApps failed:"
|
result_message += f"\n{'=' * 80}\nApps failed:"
|
||||||
for app, info in apps_failed.items():
|
for app, logs in apps_failed.items():
|
||||||
result_message += f"\n{'='*40}\n{app}\n{'-'*40}\n{info}\n\n"
|
result_message += f"\n{'='*40}\n{app}\n{'-'*40}\n{logs[0]}\n{logs[1]}\n\n"
|
||||||
|
|
||||||
if apps_failed and args.paste:
|
if apps_failed and args.paste:
|
||||||
paste_url = paste_on_haste(result_message)
|
paste_url = paste_on_haste(result_message)
|
||||||
|
|
Loading…
Reference in a new issue