Add diagnosis section to check that app are in catalog with good quality + check for deprecated practices

This commit is contained in:
Alexandre Aubin 2021-04-24 21:34:57 +02:00
parent 381f789fec
commit 913b02e4fc
3 changed files with 96 additions and 4 deletions

View file

@ -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()

View file

@ -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.",

View file

@ -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)
)