From 913b02e4fcc32622a3a3763680333980c4c04028 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 24 Apr 2021 21:34:57 +0200 Subject: [PATCH 1/2] Add diagnosis section to check that app are in catalog with good quality + check for deprecated practices --- data/hooks/diagnosis/80-apps.py | 82 +++++++++++++++++++++++++++++++++ locales/en.json | 8 ++++ src/yunohost/app.py | 10 ++-- 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 data/hooks/diagnosis/80-apps.py diff --git a/data/hooks/diagnosis/80-apps.py b/data/hooks/diagnosis/80-apps.py new file mode 100644 index 000000000..ce54faef1 --- /dev/null +++ b/data/hooks/diagnosis/80-apps.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +import os +import re + +from yunohost.app import app_info, app_list +from moulinette.utils.filesystem import read_file + +from yunohost.settings import settings_get +from yunohost.diagnosis import Diagnoser +from yunohost.regenconf import _get_regenconf_infos, _calculate_hash +from moulinette.utils.filesystem import read_file + + +class AppDiagnoser(Diagnoser): + + id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] + cache_duration = 300 + dependencies = [] + + def run(self): + + apps = app_list(full=True)["apps"] + for app in apps: + app["issues"] = list(self.issues(app)) + + if not any(app["issues"] for app in apps): + yield dict( + meta={"test": "apps"}, + status="SUCCESS", + summary="diagnosis_apps_allgood", + ) + else: + for app in apps: + + if not app["issues"]: + continue + + level = "ERROR" if any(issue[0] == "error" for issue in app["issues"]) else "WARNING" + + yield dict( + meta={"test": "apps", "app": app["name"]}, + status=level, + summary="diagnosis_apps_issue", + details=[issue[1] for issue in app["issues"]] + ) + + def issues(self, app): + + # Check quality level in catalog + + if not app.get("from_catalog") or app["from_catalog"].get("state") != "working": + yield ("error", "diagnosis_apps_not_in_app_catalog") + elif not isinstance(app["from_catalog"].get("level"), int) or app["from_catalog"]["level"] == 0: + yield ("error", "diagnosis_apps_broken") + elif app["from_catalog"]["level"] <= 4: + yield ("warning", "diagnosis_apps_bad_quality") + + # Check for super old, deprecated practices + + yunohost_version_req = app["manifest"].get("requirements", {}).get("yunohost", "").strip(">= ") + if yunohost_version_req.startswith("2."): + yield ("error", "diagnosis_apps_outdated_ynh_requirement") + + deprecated_helpers = [ + "yunohost app setting", + "yunohost app checkurl", + "yunohost app checkport", + "yunohost app initdb", + "yunohost tools port-available", + ] + for deprecated_helper in deprecated_helpers: + if os.system(f"grep -nr -q '{deprecated_helper}' {app['setting_path']}/scripts/") == 0: + yield ("error", "diagnosis_apps_deprecated_practices") + + old_arg_regex = r'^domain=\${?[0-9]' + if os.system(f"grep -q '{old_arg_regex}' {app['setting_path']}/scripts/install") == 0: + yield ("error", "diagnosis_apps_deprecated_practices") + + +def main(args, env, loggers): + return AppDiagnoser(args, env, loggers).diagnose() diff --git a/locales/en.json b/locales/en.json index 938a38e20..8852f5587 100644 --- a/locales/en.json +++ b/locales/en.json @@ -248,6 +248,14 @@ "diagnosis_description_web": "Web", "diagnosis_description_mail": "Email", "diagnosis_description_regenconf": "System configurations", + "diagnosis_description_apps": "Applications", + "diagnosis_apps_allgood": "All installed apps respect basic packaging practices", + "diagnosis_apps_issue": "An issue was found for app {app}", + "diagnosis_apps_not_in_app_catalog": "This application is not in YunoHost's application catalog. If it was in the past and got removed, you should consider uninstalling this app as it won't receive upgrade, and may compromise the integrity and security of your system.", + "diagnosis_apps_broken": "This application is currently flagged as broken on YunoHost's application catalog. This may be a temporary issue while the maintainers attempt to fix the issue. In the meantime, upgrading this app is disabled.", + "diagnosis_apps_bad_quality": "This application is currently flagged as broken on YunoHost's application catalog. This may be a temporary issue while the maintainers attempt to fix the issue. In the meantime, upgrading this app is disabled.", + "diagnosis_apps_outdated_ynh_requirement": "This app's installed version only requires yunohost >= 2.x, which tends to indicate that it's not up to date with recommended packaging practices and helpers. You should really consider upgrading it.", + "diagnosis_apps_deprecated_practices": "This app's installed version still uses some super-old deprecated packaging practices. You should really consider upgrading it.", "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside in IPv{ipversion}.", "diagnosis_ports_could_not_diagnose_details": "Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index c048ca5ea..646535fab 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -193,7 +193,8 @@ def app_info(app, full=False): "app_not_installed", app=app, all_apps=_get_all_installed_apps_id() ) - local_manifest = _get_manifest_of_app(os.path.join(APPS_SETTING_PATH, app)) + setting_path = os.path.join(APPS_SETTING_PATH, app) + local_manifest = _get_manifest_of_app(setting_path) permissions = user_permission_list(full=True, absolute_urls=True, apps=[app])[ "permissions" ] @@ -212,6 +213,7 @@ def app_info(app, full=False): if not full: return ret + ret["setting_path"] = setting_path ret["manifest"] = local_manifest ret["manifest"]["arguments"] = _set_default_ask_questions( ret["manifest"].get("arguments", {}) @@ -222,11 +224,11 @@ def app_info(app, full=False): ret["from_catalog"] = _load_apps_catalog()["apps"].get(absolute_app_name, {}) ret["upgradable"] = _app_upgradable(ret) ret["supports_change_url"] = os.path.exists( - os.path.join(APPS_SETTING_PATH, app, "scripts", "change_url") + os.path.join(setting_path, "scripts", "change_url") ) ret["supports_backup_restore"] = os.path.exists( - os.path.join(APPS_SETTING_PATH, app, "scripts", "backup") - ) and os.path.exists(os.path.join(APPS_SETTING_PATH, app, "scripts", "restore")) + os.path.join(setting_path, "scripts", "backup") + ) and os.path.exists(os.path.join(setting_path, "scripts", "restore")) ret["supports_multi_instance"] = is_true( local_manifest.get("multi_instance", False) ) From 569dc24267a499d9c231154ed64feab3a3c38fa9 Mon Sep 17 00:00:00 2001 From: Kay0u Date: Thu, 26 Aug 2021 23:01:14 +0200 Subject: [PATCH 2/2] remove unused imports --- data/hooks/diagnosis/80-apps.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/data/hooks/diagnosis/80-apps.py b/data/hooks/diagnosis/80-apps.py index ce54faef1..4ab5a6c0d 100644 --- a/data/hooks/diagnosis/80-apps.py +++ b/data/hooks/diagnosis/80-apps.py @@ -1,16 +1,10 @@ #!/usr/bin/env python import os -import re -from yunohost.app import app_info, app_list -from moulinette.utils.filesystem import read_file +from yunohost.app import app_list -from yunohost.settings import settings_get from yunohost.diagnosis import Diagnoser -from yunohost.regenconf import _get_regenconf_infos, _calculate_hash -from moulinette.utils.filesystem import read_file - class AppDiagnoser(Diagnoser):