mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
Merge d596130f1d
into 8c9d6219ab
This commit is contained in:
commit
d3aa4153ba
5 changed files with 204 additions and 3 deletions
30
.github/workflows/deprecated.yml
vendored
Normal file
30
.github/workflows/deprecated.yml
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
name: Find deprecated softwares
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 20 * * 1'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
black:
|
||||||
|
name: Find deprecated softwares
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Set up Python 3.11
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: 3.11
|
||||||
|
- name: Install toml python lib
|
||||||
|
run: |
|
||||||
|
pip3 install toml tomlkit gitpython
|
||||||
|
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@v6
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
title: "Flag deprecated apps in the catalog"
|
||||||
|
commit-message: ":coffin: Flag deprecated apps in the catalog"
|
||||||
|
body: |
|
||||||
|
This was done with tools/find_deprecated.py
|
||||||
|
base: ${{ github.head_ref }} # Creates pull request onto pull request or commit branch
|
||||||
|
branch: actions/deprecated
|
|
@ -243,6 +243,7 @@ url = "https://github.com/YunoHost-Apps/biboumi_ynh"
|
||||||
|
|
||||||
[bicbucstriim]
|
[bicbucstriim]
|
||||||
added_date = 1674232499 # 2023/01/20
|
added_date = 1674232499 # 2023/01/20
|
||||||
|
antifeatures = [ "deprecated-software" ]
|
||||||
category = "reading"
|
category = "reading"
|
||||||
level = 7
|
level = 7
|
||||||
state = "working"
|
state = "working"
|
||||||
|
@ -1637,6 +1638,7 @@ url = "https://github.com/labriqueinternet/hotspot_ynh"
|
||||||
|
|
||||||
[httpsh]
|
[httpsh]
|
||||||
added_date = 1695759380 # 2023/09/26
|
added_date = 1695759380 # 2023/09/26
|
||||||
|
antifeatures = [ "deprecated-software" ]
|
||||||
category = "system_tools"
|
category = "system_tools"
|
||||||
level = 8
|
level = 8
|
||||||
state = "working"
|
state = "working"
|
||||||
|
@ -1758,7 +1760,6 @@ url = "https://github.com/YunoHost-Apps/invidious_ynh"
|
||||||
|
|
||||||
[invoiceninja]
|
[invoiceninja]
|
||||||
added_date = 1674232499 # 2023/01/20
|
added_date = 1674232499 # 2023/01/20
|
||||||
antifeatures = [ "deprecated-software" ]
|
|
||||||
category = "productivity_and_management"
|
category = "productivity_and_management"
|
||||||
deprecated_date = 1691394954 # 2023/08/07
|
deprecated_date = 1691394954 # 2023/08/07
|
||||||
level = 6
|
level = 6
|
||||||
|
@ -3718,8 +3719,7 @@ subtags = [ "chat" ]
|
||||||
url = "https://github.com/YunoHost-Apps/simplex_ynh"
|
url = "https://github.com/YunoHost-Apps/simplex_ynh"
|
||||||
|
|
||||||
[simplytranslate]
|
[simplytranslate]
|
||||||
added_date = 1685875056 # 2023/06/04
|
antifeatures = [ "non-free-network" ]
|
||||||
antifeatures = [ "deprecated-software", "non-free-network" ]
|
|
||||||
category = "small_utilities"
|
category = "small_utilities"
|
||||||
deprecated_date = 1717071136 # 2024/05/30
|
deprecated_date = 1717071136 # 2024/05/30
|
||||||
level = 7
|
level = 7
|
||||||
|
|
|
@ -42,6 +42,10 @@ class GithubAPI:
|
||||||
"""Get a list of releases for project."""
|
"""Get a list of releases for project."""
|
||||||
return self.internal_api(f"repos/{self.upstream_repo}/releases?per_page=100")
|
return self.internal_api(f"repos/{self.upstream_repo}/releases?per_page=100")
|
||||||
|
|
||||||
|
def archived(self) -> bool:
|
||||||
|
"""Return the archival status for the repository"""
|
||||||
|
return self.internal_api(f"repos/{self.upstream_repo}")["archived"]
|
||||||
|
|
||||||
def url_for_ref(self, ref: str, ref_type: RefType) -> str:
|
def url_for_ref(self, ref: str, ref_type: RefType) -> str:
|
||||||
"""Get a URL for a ref."""
|
"""Get a URL for a ref."""
|
||||||
if ref_type == RefType.tags or ref_type == RefType.releases:
|
if ref_type == RefType.tags or ref_type == RefType.releases:
|
||||||
|
@ -62,6 +66,7 @@ class GithubAPI:
|
||||||
class GitlabAPI:
|
class GitlabAPI:
|
||||||
def __init__(self, upstream: str):
|
def __init__(self, upstream: str):
|
||||||
# Find gitlab api root...
|
# Find gitlab api root...
|
||||||
|
upstream = upstream.rstrip("/")
|
||||||
self.forge_root = self.get_forge_root(upstream).rstrip("/")
|
self.forge_root = self.get_forge_root(upstream).rstrip("/")
|
||||||
self.project_path = upstream.replace(self.forge_root, "").lstrip("/")
|
self.project_path = upstream.replace(self.forge_root, "").lstrip("/")
|
||||||
self.project_id = self.find_project_id(self.project_path)
|
self.project_id = self.find_project_id(self.project_path)
|
||||||
|
@ -145,6 +150,10 @@ class GitlabAPI:
|
||||||
|
|
||||||
return retval
|
return retval
|
||||||
|
|
||||||
|
def archived(self) -> bool:
|
||||||
|
"""Return the archival status for the repository"""
|
||||||
|
return self.internal_api(f"projects/{self.project_id}").get("archived", False)
|
||||||
|
|
||||||
def url_for_ref(self, ref: str, _: RefType) -> str:
|
def url_for_ref(self, ref: str, _: RefType) -> str:
|
||||||
name = self.project_path.split("/")[-1]
|
name = self.project_path.split("/")[-1]
|
||||||
clean_ref = ref.replace("/", "-")
|
clean_ref = ref.replace("/", "-")
|
||||||
|
@ -196,6 +205,10 @@ class GiteaForgejoAPI:
|
||||||
"""Get a list of releases for project."""
|
"""Get a list of releases for project."""
|
||||||
return self.internal_api(f"repos/{self.project_path}/releases")
|
return self.internal_api(f"repos/{self.project_path}/releases")
|
||||||
|
|
||||||
|
def archived(self) -> bool:
|
||||||
|
"""Return the archival status for the repository"""
|
||||||
|
return self.internal_api(f"repos/{self.project_path}")["archived"]
|
||||||
|
|
||||||
def url_for_ref(self, ref: str, _: RefType) -> str:
|
def url_for_ref(self, ref: str, _: RefType) -> str:
|
||||||
"""Get a URL for a ref."""
|
"""Get a URL for a ref."""
|
||||||
return f"{self.forge_root}/{self.project_path}/archive/{ref}.tar.gz"
|
return f"{self.forge_root}/{self.project_path}/archive/{ref}.tar.gz"
|
||||||
|
|
152
tools/find_deprecated.py
Executable file
152
tools/find_deprecated.py
Executable file
|
@ -0,0 +1,152 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import traceback
|
||||||
|
import argparse
|
||||||
|
import tomlkit
|
||||||
|
import multiprocessing
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
from functools import cache
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import toml
|
||||||
|
import tqdm
|
||||||
|
import github
|
||||||
|
|
||||||
|
# add apps/tools to sys.path
|
||||||
|
sys.path.insert(0, str(Path(__file__).parent.parent))
|
||||||
|
|
||||||
|
from appslib.utils import REPO_APPS_ROOT, get_catalog # noqa: E402 pylint: disable=import-error,wrong-import-position
|
||||||
|
from app_caches import app_cache_folder # noqa: E402 pylint: disable=import-error,wrong-import-position
|
||||||
|
from autoupdate_app_sources.rest_api import GithubAPI, GitlabAPI, GiteaForgejoAPI, RefType # noqa: E402,E501 pylint: disable=import-error,wrong-import-position
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def get_github() -> tuple[Optional[tuple[str, str]], Optional[github.Github], Optional[github.InputGitAuthor]]:
|
||||||
|
try:
|
||||||
|
github_login = (REPO_APPS_ROOT / ".github_login").open("r", encoding="utf-8").read().strip()
|
||||||
|
github_token = (REPO_APPS_ROOT / ".github_token").open("r", encoding="utf-8").read().strip()
|
||||||
|
github_email = (REPO_APPS_ROOT / ".github_email").open("r", encoding="utf-8").read().strip()
|
||||||
|
|
||||||
|
auth = (github_login, github_token)
|
||||||
|
github_api = github.Github(github_token)
|
||||||
|
author = github.InputGitAuthor(github_login, github_email)
|
||||||
|
return auth, github_api, author
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f"Could not get github: {e}")
|
||||||
|
return None, None, None
|
||||||
|
|
||||||
|
|
||||||
|
def upstream_last_update_ago(app: str) -> tuple[str, int | None]:
|
||||||
|
manifest_toml = app_cache_folder(app) / "manifest.toml"
|
||||||
|
manifest_json = app_cache_folder(app) / "manifest.json"
|
||||||
|
|
||||||
|
if manifest_toml.exists():
|
||||||
|
manifest = toml.load(manifest_toml.open("r", encoding="utf-8"))
|
||||||
|
upstream = manifest.get("upstream", {}).get("code")
|
||||||
|
|
||||||
|
elif manifest_json.exists():
|
||||||
|
manifest = json.load(manifest_json.open("r", encoding="utf-8"))
|
||||||
|
upstream = manifest.get("upstream", {}).get("code")
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"App {app} doesn't have a manifest!")
|
||||||
|
|
||||||
|
if upstream is None:
|
||||||
|
raise RuntimeError(f"App {app} doesn't have an upstream code link!")
|
||||||
|
|
||||||
|
api = None
|
||||||
|
try:
|
||||||
|
if upstream.startswith("https://github.com/"):
|
||||||
|
try:
|
||||||
|
api = GithubAPI(upstream, auth=get_github()[0])
|
||||||
|
except AssertionError as e:
|
||||||
|
logging.error(f"Exception while handling {app}: {e}")
|
||||||
|
return app, None
|
||||||
|
|
||||||
|
if upstream.startswith("https://gitlab.") or upstream.startswith("https://framagit.org"):
|
||||||
|
api = GitlabAPI(upstream)
|
||||||
|
|
||||||
|
if upstream.startswith("https://codeberg.org"):
|
||||||
|
api = GiteaForgejoAPI(upstream)
|
||||||
|
|
||||||
|
if not api:
|
||||||
|
autoupdate = manifest.get("resources", {}).get("sources", {}).get("main", {}).get("autoupdate")
|
||||||
|
if autoupdate:
|
||||||
|
strat = autoupdate["strategy"]
|
||||||
|
if "gitea" in strat or "forgejo" in strat:
|
||||||
|
api = GiteaForgejoAPI(upstream)
|
||||||
|
|
||||||
|
if api:
|
||||||
|
if api.archived():
|
||||||
|
# A stupid value that we know to be higher than the trigger value
|
||||||
|
return app, 1000
|
||||||
|
|
||||||
|
last_commit = api.commits()[0]
|
||||||
|
date = last_commit["commit"]["author"]["date"]
|
||||||
|
date = datetime.datetime.fromisoformat(date)
|
||||||
|
ago: datetime.timedelta = datetime.datetime.now() - date.replace(tzinfo=None)
|
||||||
|
return app, ago.days
|
||||||
|
except Exception:
|
||||||
|
logging.error(f"Exception while handling {app}", traceback.format_exc())
|
||||||
|
raise
|
||||||
|
|
||||||
|
raise RuntimeError(f"App {app} not handled (not github, gitlab or gitea with autoupdate). Upstream is {upstream}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("apps", nargs="*", type=str,
|
||||||
|
help="If not passed, the script will run on the catalog. Github keys required.")
|
||||||
|
parser.add_argument("-j", "--processes", type=int, default=multiprocessing.cpu_count())
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
apps_dict = get_catalog()
|
||||||
|
if args.apps:
|
||||||
|
apps_dict = {app: info for app, info in apps_dict.items() if app in args.apps}
|
||||||
|
|
||||||
|
deprecated: list[str] = []
|
||||||
|
not_deprecated: list[str] = []
|
||||||
|
# for app, info in apps_dict.items():
|
||||||
|
with multiprocessing.Pool(processes=args.processes) as pool:
|
||||||
|
tasks = pool.imap_unordered(upstream_last_update_ago, apps_dict.keys())
|
||||||
|
|
||||||
|
for _ in tqdm.tqdm(range(len(apps_dict)), ascii=" ·#"):
|
||||||
|
try:
|
||||||
|
app, result = next(tasks)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Exception found: {e}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if result > 365:
|
||||||
|
deprecated.append(app)
|
||||||
|
else:
|
||||||
|
not_deprecated.append(app)
|
||||||
|
|
||||||
|
catalog = tomlkit.load(open("apps.toml"))
|
||||||
|
for app, info in catalog.items():
|
||||||
|
antifeatures = info.get("antifeatures", [])
|
||||||
|
if app in deprecated:
|
||||||
|
if "deprecated-software" not in antifeatures:
|
||||||
|
antifeatures.append("deprecated-software")
|
||||||
|
elif app in not_deprecated:
|
||||||
|
if "deprecated-software" in antifeatures:
|
||||||
|
antifeatures.remove("deprecated-software")
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
# unique the keys
|
||||||
|
if antifeatures:
|
||||||
|
info["antifeatures"] = antifeatures
|
||||||
|
else:
|
||||||
|
if "antifeatures" in info.keys():
|
||||||
|
info.pop("antifeatures")
|
||||||
|
tomlkit.dump(catalog, open("apps.toml", "w"))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
|
@ -2224,6 +2224,12 @@ upstream = "https://github.com/dream-num/univer"
|
||||||
website = "https://univer.ai/"
|
website = "https://univer.ai/"
|
||||||
added_date = 1717071136 # 2024/05/30
|
added_date = 1717071136 # 2024/05/30
|
||||||
|
|
||||||
|
[univer]
|
||||||
|
name = "Univer"
|
||||||
|
description = "Replacement of Luckysheet (deprecated, in yunohost packages)"
|
||||||
|
upstream = "https://github.com/dream-num/univer"
|
||||||
|
website = "https://univer.ai/"
|
||||||
|
|
||||||
[upmpdcli]
|
[upmpdcli]
|
||||||
name = "upmpdcli"
|
name = "upmpdcli"
|
||||||
description = "UPnP renderer front-end for MPD"
|
description = "UPnP renderer front-end for MPD"
|
||||||
|
|
Loading…
Add table
Reference in a new issue