mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
Merge db7cd6f29e
into 8c9d6219ab
This commit is contained in:
commit
28b3954de1
9 changed files with 196 additions and 116 deletions
60
tools/appslib/get_apps_repo.py
Normal file
60
tools/appslib/get_apps_repo.py
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import tempfile
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
from git import Repo
|
||||||
|
|
||||||
|
|
||||||
|
DEFAULT_GIT_REPO = "git@github.com:YunoHost/apps"
|
||||||
|
|
||||||
|
|
||||||
|
class TemporaryPath(Path):
|
||||||
|
""" Just a helper to return agnostically a Path or a TemporaryDirectory """
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
self.temporary_directory = tempfile.TemporaryDirectory(*args, **kwargs)
|
||||||
|
Path.__init__(self, self.temporary_directory.name)
|
||||||
|
|
||||||
|
def with_segments(self, *pathsegments):
|
||||||
|
""" We need to overload this method because it calls type(self)
|
||||||
|
but we don't want to create multiple TemporaryPaths.
|
||||||
|
"""
|
||||||
|
return Path(*pathsegments)
|
||||||
|
|
||||||
|
|
||||||
|
APPS_REPO_DIR: Optional[TemporaryPath] = None
|
||||||
|
|
||||||
|
def add_args(parser: argparse.ArgumentParser, required: bool = False, allow_temp: bool = True) -> None:
|
||||||
|
env_apps_dir_str = os.environ.get("YNH_APPS_DIR")
|
||||||
|
env_apps_dir = Path(env_apps_dir_str) if env_apps_dir_str is not None else None
|
||||||
|
|
||||||
|
repo_group = parser.add_mutually_exclusive_group(required=required)
|
||||||
|
if allow_temp:
|
||||||
|
repo_group.add_argument("-c", "--apps-repo", type=str, default=DEFAULT_GIT_REPO,
|
||||||
|
help="Git url to clone the 'apps' repository")
|
||||||
|
repo_group.add_argument("-d", "--apps-dir", type=Path, help="Path to an existing 'apps' repository", default=env_apps_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def from_args(args: Optional[argparse.Namespace]) -> Path:
|
||||||
|
global APPS_REPO_DIR
|
||||||
|
if APPS_REPO_DIR is not None:
|
||||||
|
return APPS_REPO_DIR
|
||||||
|
|
||||||
|
assert args is not None
|
||||||
|
if args.apps_dir is not None:
|
||||||
|
APPS_REPO_DIR = args.apps_dir
|
||||||
|
assert APPS_REPO_DIR is not None
|
||||||
|
return APPS_REPO_DIR
|
||||||
|
|
||||||
|
if args.apps_repo is not None:
|
||||||
|
tmpdir = TemporaryPath(prefix="yunohost_apps_")
|
||||||
|
logging.info("Cloning the 'apps' repository...")
|
||||||
|
repo = Repo.clone_from(args.apps_repo, to_path=tmpdir)
|
||||||
|
assert repo.working_tree_dir is not None
|
||||||
|
APPS_REPO_DIR = tmpdir
|
||||||
|
return APPS_REPO_DIR
|
||||||
|
|
||||||
|
raise RuntimeError("You need to pass either --apps-repo or --apps-dir!")
|
|
@ -13,11 +13,6 @@ import toml
|
||||||
REPO_APPS_ROOT = Path(Repo(__file__, search_parent_directories=True).working_dir)
|
REPO_APPS_ROOT = Path(Repo(__file__, search_parent_directories=True).working_dir)
|
||||||
|
|
||||||
|
|
||||||
@cache
|
|
||||||
def apps_repo_root() -> Path:
|
|
||||||
return Path(__file__).parent.parent.parent
|
|
||||||
|
|
||||||
|
|
||||||
def git(cmd: list[str], cwd: Optional[Path] = None) -> str:
|
def git(cmd: list[str], cwd: Optional[Path] = None) -> str:
|
||||||
full_cmd = ["git"]
|
full_cmd = ["git"]
|
||||||
if cwd:
|
if cwd:
|
||||||
|
@ -41,9 +36,11 @@ def git_repo_age(path: Path) -> Union[bool, int]:
|
||||||
|
|
||||||
|
|
||||||
@cache
|
@cache
|
||||||
def get_catalog(working_only: bool = False) -> dict[str, dict[str, Any]]:
|
def get_catalog(apps_repo: Path | None = None, working_only: bool = False) -> dict[str, dict[str, Any]]:
|
||||||
"""Load the app catalog and filter out the non-working ones"""
|
"""Load the app catalog and filter out the non-working ones"""
|
||||||
catalog = toml.load((REPO_APPS_ROOT / "apps.toml").open("r", encoding="utf-8"))
|
apps_repo = apps_repo or REPO_APPS_ROOT
|
||||||
|
|
||||||
|
catalog = toml.load((apps_repo / "apps.toml").open("r", encoding="utf-8"))
|
||||||
if working_only:
|
if working_only:
|
||||||
catalog = {
|
catalog = {
|
||||||
app: infos
|
app: infos
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
@ -14,19 +15,20 @@ import toml
|
||||||
sys.path.insert(0, str(Path(__file__).parent.parent))
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
from appslib.utils import ( # noqa: E402 pylint: disable=import-error,wrong-import-position
|
from appslib.utils import ( # noqa: E402 pylint: disable=import-error,wrong-import-position
|
||||||
REPO_APPS_ROOT,
|
|
||||||
get_catalog,
|
get_catalog,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
TOOLS_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
my_env = os.environ.copy()
|
my_env = os.environ.copy()
|
||||||
my_env["GIT_TERMINAL_PROMPT"] = "0"
|
my_env["GIT_TERMINAL_PROMPT"] = "0"
|
||||||
os.makedirs(".apps_cache", exist_ok=True)
|
os.makedirs(".apps_cache", exist_ok=True)
|
||||||
|
|
||||||
login = (
|
login = (
|
||||||
(REPO_APPS_ROOT / "tools/.github_login").open("r", encoding="utf-8").read().strip()
|
(TOOLS_DIR / ".github_login").open("r", encoding="utf-8").read().strip()
|
||||||
)
|
)
|
||||||
token = (
|
token = (
|
||||||
(REPO_APPS_ROOT / "tools/.github_token").open("r", encoding="utf-8").read().strip()
|
(TOOLS_DIR / ".github_token").open("r", encoding="utf-8").read().strip()
|
||||||
)
|
)
|
||||||
github_api = "https://api.github.com"
|
github_api = "https://api.github.com"
|
||||||
|
|
||||||
|
@ -193,36 +195,31 @@ def create_pull_request(repo, patch, base_branch, s):
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
action = sys.argv[1]
|
parser = argparse.ArgumentParser()
|
||||||
if action == "--help":
|
parser.add_argument("the_patch", type=str, nargs="?", help="The name of the patch to apply")
|
||||||
print(
|
parser.add_argument("--cache", "-b", action="store_true", help="Init local git clone for all apps")
|
||||||
"""
|
parser.add_argument("--apply", "-a", action="store_true", help="Apply patch on all local clones")
|
||||||
Example usage:
|
parser.add_argument("--diff", "-d", action="store_true", help="Inspect diff for all apps")
|
||||||
|
parser.add_argument("--push", "-p", action="store_true", help="Push and create pull requests on all apps with non-empty diff")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Init local git clone for all apps
|
if not (args.cache or args.apply or args.diff or args.push):
|
||||||
./autopatch.py --build-cache
|
parser.error("We required --cache, --apply, --diff or --push.")
|
||||||
|
|
||||||
# Apply patch in all local clones
|
if args.cache:
|
||||||
./autopatch.py --apply explicit-php-version-in-deps
|
|
||||||
|
|
||||||
# Inspect diff for all apps
|
|
||||||
./autopatch.py --diff
|
|
||||||
|
|
||||||
# Push and create pull requests on all apps with non-empty diff
|
|
||||||
./autopatch.py --push explicit-php-version-in-deps
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
elif action == "--build-cache":
|
|
||||||
build_cache()
|
build_cache()
|
||||||
elif action == "--apply":
|
|
||||||
apply(sys.argv[2])
|
|
||||||
elif action == "--diff":
|
|
||||||
diff()
|
|
||||||
elif action == "--push":
|
|
||||||
push(sys.argv[2])
|
|
||||||
else:
|
|
||||||
print("Unknown action %s" % action)
|
|
||||||
|
|
||||||
|
if args.apply:
|
||||||
|
if not args.the_patch:
|
||||||
|
parser.error("--apply requires the patch name to be passed")
|
||||||
|
apply(args.the_patch)
|
||||||
|
|
||||||
|
if args.diff:
|
||||||
|
diff()
|
||||||
|
|
||||||
|
if args.push:
|
||||||
|
if not args.the_patch:
|
||||||
|
parser.error("--push requires the patch name to be passed")
|
||||||
|
push(args.the_patch)
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -27,15 +27,16 @@ from rest_api import (
|
||||||
DownloadPageAPI,
|
DownloadPageAPI,
|
||||||
RefType,
|
RefType,
|
||||||
) # noqa: E402,E501 pylint: disable=import-error,wrong-import-position
|
) # noqa: E402,E501 pylint: disable=import-error,wrong-import-position
|
||||||
|
import appslib.get_apps_repo as get_apps_repo
|
||||||
import appslib.logging_sender # noqa: E402 pylint: disable=import-error,wrong-import-position
|
import appslib.logging_sender # noqa: E402 pylint: disable=import-error,wrong-import-position
|
||||||
from appslib.utils import (
|
from appslib.utils import (
|
||||||
REPO_APPS_ROOT,
|
|
||||||
get_catalog,
|
get_catalog,
|
||||||
) # noqa: E402 pylint: disable=import-error,wrong-import-position
|
) # noqa: E402 pylint: disable=import-error,wrong-import-position
|
||||||
from app_caches import (
|
from app_caches import (
|
||||||
app_cache_folder,
|
app_cache_folder,
|
||||||
) # noqa: E402 pylint: disable=import-error,wrong-import-position
|
) # noqa: E402 pylint: disable=import-error,wrong-import-position
|
||||||
|
|
||||||
|
TOOLS_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
||||||
STRATEGIES = [
|
STRATEGIES = [
|
||||||
"latest_github_release",
|
"latest_github_release",
|
||||||
|
@ -62,19 +63,19 @@ def get_github() -> tuple[
|
||||||
]:
|
]:
|
||||||
try:
|
try:
|
||||||
github_login = (
|
github_login = (
|
||||||
(REPO_APPS_ROOT / "tools" / ".github_login")
|
(TOOLS_DIR / ".github_login")
|
||||||
.open("r", encoding="utf-8")
|
.open("r", encoding="utf-8")
|
||||||
.read()
|
.read()
|
||||||
.strip()
|
.strip()
|
||||||
)
|
)
|
||||||
github_token = (
|
github_token = (
|
||||||
(REPO_APPS_ROOT / "tools" / ".github_token")
|
(TOOLS_DIR / ".github_token")
|
||||||
.open("r", encoding="utf-8")
|
.open("r", encoding="utf-8")
|
||||||
.read()
|
.read()
|
||||||
.strip()
|
.strip()
|
||||||
)
|
)
|
||||||
github_email = (
|
github_email = (
|
||||||
(REPO_APPS_ROOT / "tools" / ".github_email")
|
(TOOLS_DIR / ".github_email")
|
||||||
.open("r", encoding="utf-8")
|
.open("r", encoding="utf-8")
|
||||||
.read()
|
.read()
|
||||||
.strip()
|
.strip()
|
||||||
|
@ -89,10 +90,10 @@ def get_github() -> tuple[
|
||||||
return None, None, None
|
return None, None, None
|
||||||
|
|
||||||
|
|
||||||
def apps_to_run_auto_update_for() -> list[str]:
|
def apps_to_run_auto_update_for(apps_repo: Path) -> list[str]:
|
||||||
apps_flagged_as_working_and_on_yunohost_apps_org = [
|
apps_flagged_as_working_and_on_yunohost_apps_org = [
|
||||||
app
|
app
|
||||||
for app, infos in get_catalog().items()
|
for app, infos in get_catalog(apps_repo).items()
|
||||||
if infos["state"] == "working"
|
if infos["state"] == "working"
|
||||||
and "/github.com/yunohost-apps" in infos["url"].lower()
|
and "/github.com/yunohost-apps" in infos["url"].lower()
|
||||||
]
|
]
|
||||||
|
@ -746,6 +747,7 @@ def main() -> None:
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-j", "--processes", type=int, default=multiprocessing.cpu_count()
|
"-j", "--processes", type=int, default=multiprocessing.cpu_count()
|
||||||
)
|
)
|
||||||
|
get_apps_repo.add_args(parser)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
appslib.logging_sender.enable()
|
appslib.logging_sender.enable()
|
||||||
|
@ -758,10 +760,12 @@ def main() -> None:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# 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(get_apps_repo.from_args(args))
|
||||||
apps_already = {} # for which a PR already exists
|
apps_already = {} # for which a PR already exists
|
||||||
apps_updated = {}
|
apps_updated = {}
|
||||||
apps_failed = {}
|
apps_failed = {}
|
||||||
|
print(apps)
|
||||||
|
exit()
|
||||||
|
|
||||||
with multiprocessing.Pool(processes=args.processes) as pool:
|
with multiprocessing.Pool(processes=args.processes) as pool:
|
||||||
tasks = pool.imap(
|
tasks = pool.imap(
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
import json
|
import json
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
@ -7,6 +8,7 @@ from difflib import SequenceMatcher
|
||||||
from typing import Any, Dict, Generator, List, Tuple
|
from typing import Any, Dict, Generator, List, Tuple
|
||||||
|
|
||||||
import jsonschema
|
import jsonschema
|
||||||
|
import appslib.get_apps_repo as get_apps_repo
|
||||||
from appslib.utils import (
|
from appslib.utils import (
|
||||||
REPO_APPS_ROOT, # pylint: disable=import-error
|
REPO_APPS_ROOT, # pylint: disable=import-error
|
||||||
get_antifeatures,
|
get_antifeatures,
|
||||||
|
@ -98,6 +100,10 @@ def check_all_apps() -> Generator[Tuple[str, List[Tuple[str, bool]]], None, None
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
get_apps_repo.add_args(parser)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
has_errors = False
|
has_errors = False
|
||||||
|
|
||||||
has_errors |= validate_schema_pretty(get_antifeatures(), "antifeatures.toml")
|
has_errors |= validate_schema_pretty(get_antifeatures(), "antifeatures.toml")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#! /usr/bin/env python3
|
#! /usr/bin/env python3
|
||||||
|
|
||||||
|
import sys
|
||||||
import os
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
@ -14,8 +15,12 @@ from babel.support import Translations
|
||||||
from babel.messages.pofile import PoFileParser
|
from babel.messages.pofile import PoFileParser
|
||||||
from langcodes import Language
|
from langcodes import Language
|
||||||
|
|
||||||
|
# add apps/tools to sys.path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
|
from appslib import get_apps_repo
|
||||||
|
|
||||||
README_GEN_DIR = Path(__file__).resolve().parent
|
README_GEN_DIR = Path(__file__).resolve().parent
|
||||||
APPS_REPO_ROOT = README_GEN_DIR.parent.parent
|
|
||||||
|
|
||||||
TRANSLATIONS_DIR = README_GEN_DIR / "translations"
|
TRANSLATIONS_DIR = README_GEN_DIR / "translations"
|
||||||
|
|
||||||
|
@ -31,7 +36,7 @@ def value_for_lang(values: Dict, lang: str):
|
||||||
return list(values.values())[0]
|
return list(values.values())[0]
|
||||||
|
|
||||||
|
|
||||||
def generate_READMEs(app_path: Path):
|
def generate_READMEs(app_path: Path, apps_repo_path: Path):
|
||||||
if not app_path.exists():
|
if not app_path.exists():
|
||||||
raise Exception("App path provided doesn't exists ?!")
|
raise Exception("App path provided doesn't exists ?!")
|
||||||
|
|
||||||
|
@ -42,11 +47,11 @@ def generate_READMEs(app_path: Path):
|
||||||
|
|
||||||
upstream = manifest.get("upstream", {})
|
upstream = manifest.get("upstream", {})
|
||||||
|
|
||||||
catalog = toml.load((APPS_REPO_ROOT / "apps.toml").open(encoding="utf-8"))
|
catalog = toml.load((apps_repo_path / "apps.toml").open(encoding="utf-8"))
|
||||||
from_catalog = catalog.get(manifest["id"], {})
|
from_catalog = catalog.get(manifest["id"], {})
|
||||||
|
|
||||||
antifeatures_list = toml.load(
|
antifeatures_list = toml.load(
|
||||||
(APPS_REPO_ROOT / "antifeatures.toml").open(encoding="utf-8")
|
(apps_repo_path / "antifeatures.toml").open(encoding="utf-8")
|
||||||
)
|
)
|
||||||
|
|
||||||
if not upstream and not (app_path / "doc" / "DISCLAIMER.md").exists():
|
if not upstream and not (app_path / "doc" / "DISCLAIMER.md").exists():
|
||||||
|
@ -188,13 +193,16 @@ def generate_READMEs(app_path: Path):
|
||||||
(app_path / "ALL_README.md").write_text(out)
|
(app_path / "ALL_README.md").write_text(out)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(description="Automatically (re)generate README for apps")
|
||||||
description="Automatically (re)generate README for apps"
|
parser.add_argument("app_path", type=Path, help="Path to the app to generate/update READMEs for")
|
||||||
)
|
get_apps_repo.add_args(parser)
|
||||||
parser.add_argument(
|
|
||||||
"app_path", type=Path, help="Path to the app to generate/update READMEs for"
|
|
||||||
)
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
generate_READMEs(Path(args.app_path))
|
|
||||||
|
apps_path = get_apps_repo.from_args(args)
|
||||||
|
|
||||||
|
generate_READMEs(args.app_path, apps_path)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
|
@ -26,9 +26,11 @@ def test_running_make_readme():
|
||||||
)
|
)
|
||||||
|
|
||||||
# Now run test...
|
# Now run test...
|
||||||
subprocess.check_call(
|
subprocess.check_call([
|
||||||
[TEST_DIRECTORY.parent / "make_readme.py", temporary_tested_app_directory]
|
TEST_DIRECTORY.parent / "make_readme.py",
|
||||||
)
|
"--apps-dir", TEST_DIRECTORY.parent.parent,
|
||||||
|
temporary_tested_app_directory
|
||||||
|
])
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
open(TEST_DIRECTORY / "README.md").read()
|
open(TEST_DIRECTORY / "README.md").read()
|
||||||
|
|
|
@ -1,22 +1,17 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
import tomlkit
|
import tomlkit
|
||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from git import Repo, Commit
|
from git import Repo, Commit
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import logging
|
import logging
|
||||||
from typing import TYPE_CHECKING, Callable
|
from typing import Callable
|
||||||
|
import appslib.get_apps_repo as get_apps_repo
|
||||||
if TYPE_CHECKING:
|
|
||||||
REPO_APPS_ROOT = Path()
|
|
||||||
else:
|
|
||||||
from appslib.utils import REPO_APPS_ROOT
|
|
||||||
|
|
||||||
|
|
||||||
def git_bisect(repo_path: Path, is_newer: Callable[[Commit], bool]) -> Commit | None:
|
def git_bisect(repo: Repo, is_newer: Callable[[Commit], bool]) -> Commit | None:
|
||||||
repo = Repo(repo_path)
|
|
||||||
|
|
||||||
# Start with whole repo
|
# Start with whole repo
|
||||||
first_commit = repo.git.rev_list("HEAD", reverse=True, max_parents=0)
|
first_commit = repo.git.rev_list("HEAD", reverse=True, max_parents=0)
|
||||||
repo.git.bisect("reset")
|
repo.git.bisect("reset")
|
||||||
|
@ -69,19 +64,19 @@ def app_is_deprecated(commit: Commit, name: str) -> bool:
|
||||||
return "deprecated-software" in antifeatures
|
return "deprecated-software" in antifeatures
|
||||||
|
|
||||||
|
|
||||||
def date_added(name: str) -> int | None:
|
def date_added(repo: Repo, name: str) -> int | None:
|
||||||
result = git_bisect(REPO_APPS_ROOT, lambda x: app_is_present(x, name))
|
result = git_bisect(repo, lambda x: app_is_present(x, name))
|
||||||
print(result)
|
print(result)
|
||||||
return None if result is None else result.committed_date
|
return None if result is None else result.committed_date
|
||||||
|
|
||||||
|
|
||||||
def date_deprecated(name: str) -> int | None:
|
def date_deprecated(repo: Repo, name: str) -> int | None:
|
||||||
result = git_bisect(REPO_APPS_ROOT, lambda x: app_is_deprecated(x, name))
|
result = git_bisect(repo, lambda x: app_is_deprecated(x, name))
|
||||||
print(result)
|
print(result)
|
||||||
return None if result is None else result.committed_date
|
return None if result is None else result.committed_date
|
||||||
|
|
||||||
|
|
||||||
def add_deprecation_dates(file: Path) -> None:
|
def add_deprecation_dates(repo: Repo, file: Path) -> None:
|
||||||
key = "deprecated_date"
|
key = "deprecated_date"
|
||||||
document = tomlkit.load(file.open("r", encoding="utf-8"))
|
document = tomlkit.load(file.open("r", encoding="utf-8"))
|
||||||
for app, info in document.items():
|
for app, info in document.items():
|
||||||
|
@ -89,7 +84,7 @@ def add_deprecation_dates(file: Path) -> None:
|
||||||
continue
|
continue
|
||||||
if "deprecated-software" not in info.get("antifeatures", []):
|
if "deprecated-software" not in info.get("antifeatures", []):
|
||||||
continue
|
continue
|
||||||
date = date_deprecated(app)
|
date = date_deprecated(repo, app)
|
||||||
if date is None:
|
if date is None:
|
||||||
continue
|
continue
|
||||||
info[key] = date
|
info[key] = date
|
||||||
|
@ -98,10 +93,9 @@ def add_deprecation_dates(file: Path) -> None:
|
||||||
tomlkit.dump(document, file.open("w"))
|
tomlkit.dump(document, file.open("w"))
|
||||||
|
|
||||||
|
|
||||||
def date_added_to(match: str, file: Path) -> int | None:
|
def date_added_to(repo: Repo, match: str, file: Path) -> int | None:
|
||||||
commits = (
|
commits = (
|
||||||
Repo(REPO_APPS_ROOT)
|
repo.git.log(
|
||||||
.git.log(
|
|
||||||
"-S",
|
"-S",
|
||||||
match,
|
match,
|
||||||
"--first-parent",
|
"--first-parent",
|
||||||
|
@ -120,12 +114,12 @@ def date_added_to(match: str, file: Path) -> int | None:
|
||||||
return int(first_commit)
|
return int(first_commit)
|
||||||
|
|
||||||
|
|
||||||
def add_apparition_dates(file: Path, key: str) -> None:
|
def add_apparition_dates(repo: Repo, file: Path, key: str) -> None:
|
||||||
document = tomlkit.load(file.open("r", encoding="utf-8"))
|
document = tomlkit.load(file.open("r", encoding="utf-8"))
|
||||||
for app, info in document.items():
|
for app, info in document.items():
|
||||||
if key in info.keys():
|
if key in info.keys():
|
||||||
continue
|
continue
|
||||||
date = date_added_to(f"[{app}]", file)
|
date = date_added_to(repo, f"[{app}]", file)
|
||||||
assert date is not None
|
assert date is not None
|
||||||
info[key] = date
|
info[key] = date
|
||||||
info[key].comment(datetime.fromtimestamp(info[key]).strftime("%Y/%m/%d"))
|
info[key].comment(datetime.fromtimestamp(info[key]).strftime("%Y/%m/%d"))
|
||||||
|
@ -134,14 +128,21 @@ def add_apparition_dates(file: Path, key: str) -> None:
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
get_apps_repo.add_args(parser, allow_temp=False)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
|
|
||||||
add_apparition_dates(REPO_APPS_ROOT / "apps.toml", key="added_date")
|
apps_repo_dir = get_apps_repo.from_args(args)
|
||||||
add_apparition_dates(REPO_APPS_ROOT / "wishlist.toml", key="added_date")
|
apps_repo = Repo(apps_repo_dir)
|
||||||
add_apparition_dates(REPO_APPS_ROOT / "graveyard.toml", key="killed_date")
|
|
||||||
|
|
||||||
add_deprecation_dates(REPO_APPS_ROOT / "apps.toml")
|
add_apparition_dates(apps_repo, apps_repo_dir / "apps.toml", key="added_date")
|
||||||
add_deprecation_dates(REPO_APPS_ROOT / "graveyard.toml")
|
add_apparition_dates(apps_repo, apps_repo_dir / "wishlist.toml", key="added_date")
|
||||||
|
add_apparition_dates(apps_repo, apps_repo_dir / "graveyard.toml", key="killed_date")
|
||||||
|
|
||||||
|
add_deprecation_dates(apps_repo, apps_repo_dir / "apps.toml")
|
||||||
|
add_deprecation_dates(apps_repo, apps_repo_dir / "graveyard.toml")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
Update app catalog: commit, and create a merge request
|
Update app catalog: commit, and create a merge request
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
|
@ -17,12 +18,16 @@ import tomlkit
|
||||||
import tomlkit.items
|
import tomlkit.items
|
||||||
from git import Repo
|
from git import Repo
|
||||||
|
|
||||||
|
# add apps/tools to sys.path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
|
from appslib import get_apps_repo
|
||||||
|
|
||||||
APPS_REPO = "YunoHost/apps"
|
APPS_REPO = "YunoHost/apps"
|
||||||
|
|
||||||
CI_RESULTS_URL = "https://ci-apps.yunohost.org/ci/api/results"
|
CI_RESULTS_URL = "https://ci-apps.yunohost.org/ci/api/results"
|
||||||
|
|
||||||
REPO_APPS_ROOT = Path(Repo(__file__, search_parent_directories=True).working_dir)
|
TOOLS_DIR = Path(__file__).resolve().parent.parent
|
||||||
TOOLS_DIR = REPO_APPS_ROOT / "tools"
|
|
||||||
|
|
||||||
|
|
||||||
def github_token() -> Optional[str]:
|
def github_token() -> Optional[str]:
|
||||||
|
@ -206,49 +211,49 @@ def main():
|
||||||
parser.add_argument("--commit", action=argparse.BooleanOptionalAction, default=True)
|
parser.add_argument("--commit", action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("--pr", action=argparse.BooleanOptionalAction, default=True)
|
parser.add_argument("--pr", action=argparse.BooleanOptionalAction, default=True)
|
||||||
parser.add_argument("-v", "--verbose", action=argparse.BooleanOptionalAction)
|
parser.add_argument("-v", "--verbose", action=argparse.BooleanOptionalAction)
|
||||||
|
get_apps_repo.add_args(parser)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
logging.getLogger().setLevel(logging.INFO)
|
||||||
if args.verbose:
|
if args.verbose:
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
|
||||||
with tempfile.TemporaryDirectory(prefix="update_app_levels_") as tmpdir:
|
repo_path = get_apps_repo.from_args(args)
|
||||||
logging.info("Cloning the repository...")
|
|
||||||
apps_repo = Repo.clone_from(f"git@github.com:{APPS_REPO}", to_path=tmpdir)
|
|
||||||
assert apps_repo.working_tree_dir is not None
|
|
||||||
apps_toml_path = Path(apps_repo.working_tree_dir) / "apps.toml"
|
|
||||||
|
|
||||||
# Load the app catalog and filter out the non-working ones
|
apps_repo = Repo(repo_path)
|
||||||
catalog = tomlkit.load(apps_toml_path.open("r", encoding="utf-8"))
|
apps_toml_path = repo_path / "apps.toml"
|
||||||
|
|
||||||
new_branch = apps_repo.create_head("update_app_levels", apps_repo.refs.master)
|
# Load the app catalog and filter out the non-working ones
|
||||||
apps_repo.head.reference = new_branch
|
catalog = tomlkit.load(apps_toml_path.open("r", encoding="utf-8"))
|
||||||
|
|
||||||
logging.info("Retrieving the CI results...")
|
new_branch = apps_repo.create_head("update_app_levels", apps_repo.refs.master)
|
||||||
ci_results = get_ci_results()
|
apps_repo.head.reference = new_branch
|
||||||
|
|
||||||
# Now compute changes, then update the catalog
|
logging.info("Retrieving the CI results...")
|
||||||
changes = list_changes(catalog, ci_results)
|
ci_results = get_ci_results()
|
||||||
pr_body = pretty_changes(changes)
|
|
||||||
catalog = update_catalog(catalog, ci_results)
|
|
||||||
|
|
||||||
# Save the new catalog
|
# Now compute changes, then update the catalog
|
||||||
updated_catalog = tomlkit.dumps(catalog)
|
changes = list_changes(catalog, ci_results)
|
||||||
updated_catalog = updated_catalog.replace(",]", " ]")
|
pr_body = pretty_changes(changes)
|
||||||
apps_toml_path.open("w", encoding="utf-8").write(updated_catalog)
|
catalog = update_catalog(catalog, ci_results)
|
||||||
|
|
||||||
if args.commit:
|
# Save the new catalog
|
||||||
logging.info("Committing and pushing the new catalog...")
|
updated_catalog = tomlkit.dumps(catalog)
|
||||||
apps_repo.index.add("apps.toml")
|
updated_catalog = updated_catalog.replace(",]", " ]")
|
||||||
apps_repo.index.commit("Update app levels according to CI results")
|
apps_toml_path.open("w", encoding="utf-8").write(updated_catalog)
|
||||||
apps_repo.git.push("--set-upstream", "origin", new_branch)
|
|
||||||
|
|
||||||
if args.verbose:
|
if args.commit:
|
||||||
print(pr_body)
|
logging.info("Committing and pushing the new catalog...")
|
||||||
|
apps_repo.index.add("apps.toml")
|
||||||
|
apps_repo.index.commit("Update app levels according to CI results")
|
||||||
|
apps_repo.git.push("--set-upstream", "origin", new_branch)
|
||||||
|
|
||||||
if args.pr:
|
if args.verbose:
|
||||||
logging.info("Opening a pull request...")
|
print(pr_body)
|
||||||
make_pull_request(pr_body)
|
|
||||||
|
if args.pr:
|
||||||
|
logging.info("Opening a pull request...")
|
||||||
|
make_pull_request(pr_body)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Add table
Reference in a new issue