erplibre/script/git/github_api.py
Mathieu Benoit 770de6b0ca [FIX] script: différer les annotations, la 12 tourne en Python 3.7
La migration lance ses outils avec le venv de la version Odoo courante.
Au premier palier c'est celui de la 12, en 3.7, où « dict | None » (3.10)
et « tuple[str, str] » (3.9) sont ÉVALUÉS au chargement du module. La
restauration du zip mourait donc sur un TypeError avant d'avoir rien
fait, dans execute.py — importé par db_restore.py.

`from __future__ import annotations` existe depuis 3.7 et le dépôt s'en
sert déjà dans treize fichiers. Un test le vérifie maintenant sur toute
la fermeture d'imports des outils que le pilote lance, points d'entrée
lus dans le pilote pour que le script ajouté demain soit couvert.

--- EN ---

The migration runs its tools with the venv of the current Odoo version.
At the first step that is 12's, on 3.7, where « dict | None » (3.10) and
« tuple[str, str] » (3.9) are EVALUATED when the module loads. Restoring
the zip therefore died on a TypeError before doing anything at all, in
execute.py — imported by db_restore.py.

`from __future__ import annotations` has existed since 3.7 and the repo
already uses it in thirteen files. A test now checks the whole import
closure of the tools the driver launches, with the entry points read
from the driver so tomorrow's script is covered too.

Assisted-by: Claude Opus 5
2026-08-23 03:13:58 -04:00

146 lines
4.7 KiB
Python

#!/usr/bin/env python3
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
# Annotations différées : la migration charge ce module sous le Python
# d'Odoo 12 — 3.7 — où « dict | None » et « tuple[str, str] » n'existent
# pas encore. Sans ceci, l'annotation est ÉVALUÉE au chargement et la
# migration meurt sur un TypeError avant d'avoir rien fait.
from __future__ import annotations
import git
from agithub.GitHub import GitHub
from colorama import Fore, Style
from git import Repo
from giturlparse import parse
from retrying import retry
def get_pull_request_repo(
upstream_url: str,
github_token: str,
organization_name: str = "",
) -> list | bool:
"""
Get pull requests for a repo.
:param upstream_url: URL of the upstream repo
:param github_token: GitHub API token
:param organization_name: optional organization name
:return: List of PRs if success, else False
"""
gh = GitHub(token=github_token)
parsed_url = parse(upstream_url)
status, user = gh.user.get()
user_name = user["login"] if not organization_name else organization_name
status, lst_pull = gh.repos[user_name][parsed_url.repo].pulls.get()
if type(lst_pull) is dict:
print(f"For url {upstream_url}," f" got {lst_pull.get('message')}")
return False
else:
for pull in lst_pull:
print(pull.get("html_url"))
return lst_pull
def fork_repo(
upstream_url: str,
github_token: str,
organization_name: str = "",
) -> None:
gh = GitHub(token=github_token)
parsed_url = parse(upstream_url)
status, user = gh.user.get()
user_name = user["login"] if not organization_name else organization_name
status, forked_repo = gh.repos[user_name][parsed_url.repo].get()
if status == 404:
status, upstream_repo = gh.repos[parsed_url.owner][
parsed_url.repo
].get()
if status == 404:
print("Unable to find repo %s" % upstream_url)
exit(1)
args = {}
if organization_name:
args["organization"] = organization_name
status, forked_repo = gh.repos[parsed_url.owner][
parsed_url.repo
].forks.post(**args)
if status == 404:
print(
f"{Fore.RED}Error{Style.RESET_ALL} when forking"
f" repo {forked_repo}"
)
exit(1)
else:
try:
print(
"Forked %s to %s" % (upstream_url, forked_repo["html_url"])
)
except Exception as e:
print(e)
print(forked_repo)
print(upstream_url)
elif status == 202:
print("Forked repo %s already exists" % forked_repo["full_name"])
elif status != 200:
print("Status not supported: %s - %s" % (status, forked_repo))
exit(1)
def add_and_fetch_remote(
repo_info, root_repo: Repo = None, branch_name: str = ""
) -> None:
"""
Deprecated function, not use anymore git submodule
"""
try:
working_repo = Repo(repo_info.relative_path)
if repo_info.organization in [a.name for a in working_repo.remotes]:
print(
f'Remote "{repo_info.organization}" already exist'
f" in {repo_info.relative_path}"
)
return
except git.NoSuchPathError:
print(f"New repo {repo_info.relative_path}")
if not root_repo:
print(
"Missing git repository to root for repo" f" {repo_info.path}"
)
return
if branch_name:
submodule_repo = retry(
wait_exponential_multiplier=1000,
stop_max_delay=15000,
)(root_repo.create_submodule)(
repo_info.path,
repo_info.path,
url=repo_info.url_https,
branch=branch_name,
)
else:
submodule_repo = retry(
wait_exponential_multiplier=1000,
stop_max_delay=15000,
)(root_repo.create_submodule)(
repo_info.path,
repo_info.path,
url=repo_info.url_https,
)
return
# Add remote
upstream_remote = retry(
wait_exponential_multiplier=1000, stop_max_delay=15000
)(working_repo.create_remote)(repo_info.organization, repo_info.url_https)
print(
'Remote "%s" created for %s'
% (repo_info.organization, repo_info.url_https)
)
# Fetch the remote
retry(wait_exponential_multiplier=1000, stop_max_delay=15000)(
upstream_remote.fetch
)()
print('Remote "%s" fetched' % repo_info.organization)