mirror of
https://github.com/YunoHost/apps.git
synced 2024-09-03 20:06:07 +02:00
Merge remote-tracking branch 'origin/master' into app-store
This commit is contained in:
commit
5d0f04d08c
9 changed files with 270 additions and 51 deletions
6
.github/workflows/main.yml
vendored
6
.github/workflows/main.yml
vendored
|
@ -1,6 +1,8 @@
|
||||||
name: Catalog consistency checks
|
name: Catalog consistency checks
|
||||||
|
|
||||||
on: pull_request
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
@ -15,7 +17,7 @@ jobs:
|
||||||
python-version: 3.11
|
python-version: 3.11
|
||||||
- name: Install toml python lib
|
- name: Install toml python lib
|
||||||
run: |
|
run: |
|
||||||
pip3 install toml
|
pip3 install toml jsonschema
|
||||||
- name: Check TOML validity for apps.toml
|
- name: Check TOML validity for apps.toml
|
||||||
run: |
|
run: |
|
||||||
python3 -c "import toml; toml.load(open('apps.toml'))"
|
python3 -c "import toml; toml.load(open('apps.toml'))"
|
||||||
|
|
26
apps.toml
26
apps.toml
|
@ -864,7 +864,7 @@ url = "https://github.com/YunoHost-Apps/element_ynh"
|
||||||
|
|
||||||
[eleventy]
|
[eleventy]
|
||||||
category = "publishing"
|
category = "publishing"
|
||||||
level = 7
|
level = 1
|
||||||
potential_alternative_to = [ "Blogger", "Blogspot", "Wix" ]
|
potential_alternative_to = [ "Blogger", "Blogspot", "Wix" ]
|
||||||
state = "working"
|
state = "working"
|
||||||
subtags = [ "websites", "blog" ]
|
subtags = [ "websites", "blog" ]
|
||||||
|
@ -928,6 +928,7 @@ url = "https://github.com/YunoHost-Apps/ethercalc_ynh"
|
||||||
|
|
||||||
[etherpad]
|
[etherpad]
|
||||||
category = "office"
|
category = "office"
|
||||||
|
level = 2
|
||||||
potential_alternative_to = [ "Google Docs", "G Suite", "Microsoft Word", "Microsoft Office", "Office 365" ]
|
potential_alternative_to = [ "Google Docs", "G Suite", "Microsoft Word", "Microsoft Office", "Office 365" ]
|
||||||
state = "working"
|
state = "working"
|
||||||
subtags = [ "text" ]
|
subtags = [ "text" ]
|
||||||
|
@ -1657,6 +1658,7 @@ url = "https://github.com/YunoHost-Apps/jellyfin_ynh"
|
||||||
|
|
||||||
[jellyfin-vue]
|
[jellyfin-vue]
|
||||||
category = "multimedia"
|
category = "multimedia"
|
||||||
|
level = 2
|
||||||
potential_alternative_to = [ "Plex", "Netflix" ]
|
potential_alternative_to = [ "Plex", "Netflix" ]
|
||||||
state = "working"
|
state = "working"
|
||||||
subtags = [ "music", "mediacenter" ]
|
subtags = [ "music", "mediacenter" ]
|
||||||
|
@ -2046,7 +2048,7 @@ url = "https://github.com/YunoHost-Apps/matomo_ynh"
|
||||||
|
|
||||||
[matrix-appservice-irc]
|
[matrix-appservice-irc]
|
||||||
category = "communication"
|
category = "communication"
|
||||||
state = "inprogress"
|
state = "working"
|
||||||
subtags = [ "chat" ]
|
subtags = [ "chat" ]
|
||||||
url = "https://github.com/YunoHost-Apps/matrix-appservice-irc_ynh"
|
url = "https://github.com/YunoHost-Apps/matrix-appservice-irc_ynh"
|
||||||
|
|
||||||
|
@ -2067,7 +2069,7 @@ url = "https://github.com/YunoHost-Apps/matterbridge_ynh"
|
||||||
|
|
||||||
[mattermost]
|
[mattermost]
|
||||||
category = "communication"
|
category = "communication"
|
||||||
level = 8
|
level = 6
|
||||||
potential_alternative_to = [ "Slack" ]
|
potential_alternative_to = [ "Slack" ]
|
||||||
state = "working"
|
state = "working"
|
||||||
subtags = [ "chat" ]
|
subtags = [ "chat" ]
|
||||||
|
@ -2356,7 +2358,7 @@ url = "https://github.com/YunoHost-Apps/my_webapp_ynh"
|
||||||
|
|
||||||
[mybb]
|
[mybb]
|
||||||
category = "communication"
|
category = "communication"
|
||||||
level = 6
|
level = 8
|
||||||
state = "working"
|
state = "working"
|
||||||
subtags = [ "forum" ]
|
subtags = [ "forum" ]
|
||||||
url = "https://github.com/YunoHost-Apps/mybb_ynh"
|
url = "https://github.com/YunoHost-Apps/mybb_ynh"
|
||||||
|
@ -2946,7 +2948,7 @@ url = "https://github.com/YunoHost-Apps/prometheus_ynh"
|
||||||
|
|
||||||
[prosody]
|
[prosody]
|
||||||
category = "communication"
|
category = "communication"
|
||||||
level = 8
|
level = 6
|
||||||
state = "working"
|
state = "working"
|
||||||
url = "https://github.com/YunoHost-Apps/prosody_ynh"
|
url = "https://github.com/YunoHost-Apps/prosody_ynh"
|
||||||
|
|
||||||
|
@ -2964,10 +2966,11 @@ state = "notworking"
|
||||||
url = "https://github.com/YunoHost-Apps/proxitok_ynh"
|
url = "https://github.com/YunoHost-Apps/proxitok_ynh"
|
||||||
|
|
||||||
[psitransfer]
|
[psitransfer]
|
||||||
category = "small_utilities"
|
category = "synchronization"
|
||||||
level = 8
|
level = 8
|
||||||
potential_alternative_to = [ "WeTransfer" ]
|
potential_alternative_to = [ "WeTransfer" ]
|
||||||
state = "working"
|
state = "working"
|
||||||
|
subtags = [ "files" ]
|
||||||
url = "https://github.com/YunoHost-Apps/psitransfer_ynh"
|
url = "https://github.com/YunoHost-Apps/psitransfer_ynh"
|
||||||
|
|
||||||
[pterodactyl]
|
[pterodactyl]
|
||||||
|
@ -3119,7 +3122,7 @@ url = "https://github.com/YunoHost-Apps/roadiz_ynh"
|
||||||
[rocketchat]
|
[rocketchat]
|
||||||
antifeatures = [ "not-totally-free" ]
|
antifeatures = [ "not-totally-free" ]
|
||||||
category = "communication"
|
category = "communication"
|
||||||
level = 6
|
level = 8
|
||||||
potential_alternative_to = [ "Slack" ]
|
potential_alternative_to = [ "Slack" ]
|
||||||
state = "working"
|
state = "working"
|
||||||
subtags = [ "chat" ]
|
subtags = [ "chat" ]
|
||||||
|
@ -3134,9 +3137,10 @@ subtags = [ "email" ]
|
||||||
url = "https://github.com/YunoHost-Apps/roundcube_ynh"
|
url = "https://github.com/YunoHost-Apps/roundcube_ynh"
|
||||||
|
|
||||||
[rportd]
|
[rportd]
|
||||||
|
antifeatures = [ "deprecated-software" ]
|
||||||
category = "system_tools"
|
category = "system_tools"
|
||||||
level = 8
|
level = 0
|
||||||
state = "working"
|
state = "notworking"
|
||||||
subtags = [ "monitoring" ]
|
subtags = [ "monitoring" ]
|
||||||
url = "https://github.com/YunoHost-Apps/rportd_ynh"
|
url = "https://github.com/YunoHost-Apps/rportd_ynh"
|
||||||
|
|
||||||
|
@ -3226,7 +3230,7 @@ url = "https://github.com/YunoHost-Apps/seafile_ynh"
|
||||||
[searx]
|
[searx]
|
||||||
antifeatures = [ "deprecated-software" ]
|
antifeatures = [ "deprecated-software" ]
|
||||||
category = "small_utilities"
|
category = "small_utilities"
|
||||||
level = 8
|
level = 7
|
||||||
state = "working"
|
state = "working"
|
||||||
url = "https://github.com/YunoHost-Apps/searx_ynh"
|
url = "https://github.com/YunoHost-Apps/searx_ynh"
|
||||||
|
|
||||||
|
@ -3544,7 +3548,7 @@ url = "https://github.com/drfred1981/subsonic_ynh"
|
||||||
|
|
||||||
[sutom]
|
[sutom]
|
||||||
category = "games"
|
category = "games"
|
||||||
level = 6
|
level = 8
|
||||||
state = "working"
|
state = "working"
|
||||||
url = "https://github.com/YunoHost-Apps/sutom_ynh"
|
url = "https://github.com/YunoHost-Apps/sutom_ynh"
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 8.2 KiB |
64
schemas/apps.toml.schema.json
Normal file
64
schemas/apps.toml.schema.json
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
|
"$id": "https://github.com/YunoHost/apps/blob/master/schemas/apps.toml.schema.json",
|
||||||
|
"title": "Yunohost's apps.toml schema",
|
||||||
|
"version": "0",
|
||||||
|
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]*$": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["url", "state"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"category": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"subtags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"additionalItems": false
|
||||||
|
},
|
||||||
|
"level": {
|
||||||
|
"type": "integer",
|
||||||
|
"minimum": 0,
|
||||||
|
"maximum": 8
|
||||||
|
},
|
||||||
|
"state": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["working", "notworking", "inprogress"]
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "url"
|
||||||
|
},
|
||||||
|
"antifeatures": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"additionalItems": false
|
||||||
|
},
|
||||||
|
"potential_alternative_to": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"additionalItems": false
|
||||||
|
},
|
||||||
|
"revision": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[a-z0-9]{32,64}$"
|
||||||
|
},
|
||||||
|
"branch": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
52
schemas/categories.toml.schema.json
Normal file
52
schemas/categories.toml.schema.json
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema",
|
||||||
|
"$id": "https://github.com/YunoHost/apps/blob/master/schemas/categories.toml.schema.json",
|
||||||
|
"title": "Yunohost's categories.toml schema",
|
||||||
|
"version": "0",
|
||||||
|
|
||||||
|
"$defs": {
|
||||||
|
"translated_string": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["en"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z]{2}$": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]*$": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["icon", "title", "description"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"icon": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": { "$ref": "#/$defs/translated_string" },
|
||||||
|
"description": { "$ref": "#/$defs/translated_string" },
|
||||||
|
"subtags": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z_]*$": {
|
||||||
|
"type": "object",
|
||||||
|
"required": ["title"],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"title": { "$ref": "#/$defs/translated_string" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -7,14 +7,21 @@
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"translated_string": {
|
"translated_string": {
|
||||||
"type": "object",
|
"anyOf": [
|
||||||
"required": ["en"],
|
{
|
||||||
"additionalProperties": false,
|
"type": "object",
|
||||||
"patternProperties": {
|
"required": ["en"],
|
||||||
"^[a-z]{2}$": {
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z]{2}$": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
]
|
||||||
},
|
},
|
||||||
"byte_size": {
|
"byte_size": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -26,11 +33,11 @@
|
||||||
},
|
},
|
||||||
"path_absolute": {
|
"path_absolute": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^/.*$"
|
"pattern": "^(__[A-Z_]*__)?/.*$"
|
||||||
},
|
},
|
||||||
"name_and_permission": {
|
"name_and_permission": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^([a-z_][a-z0-9_-]{0,30})(:[rwx-]{3})?$"
|
"pattern": "^(([a-z_][a-z0-9_-]{0,30})|([_A-Z]*))(:[rwx-]{1,3})?$"
|
||||||
},
|
},
|
||||||
"sha256sum": {
|
"sha256sum": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -142,6 +149,14 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"antifeatures": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9_-]*$": {"$ref": "#/$defs/translated_string"}
|
||||||
|
}
|
||||||
|
},
|
||||||
"install": {
|
"install": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [],
|
"required": [],
|
||||||
|
@ -184,6 +199,20 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"pattern": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [],
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"regexp": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "regex"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -286,9 +315,10 @@
|
||||||
},
|
},
|
||||||
"additional_urls": {
|
"additional_urls": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {"$ref": "#/$defs/path_absolute"}
|
||||||
"type": "string"
|
},
|
||||||
}
|
"label": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
"^[a-z][a-z0-9_]*$": {
|
"^[a-z][a-z0-9_]*$": {
|
||||||
"anyOf": [
|
"anyOf": [
|
||||||
{"type": "string"},
|
{"type": "string"},
|
||||||
{"type": "number"}
|
{"type": "number"},
|
||||||
|
{"type": "boolean"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,7 +385,7 @@ class AppAutoUpdater:
|
||||||
if is_main:
|
if is_main:
|
||||||
|
|
||||||
def repl(m):
|
def repl(m):
|
||||||
return m.group(1) + new_version + m.group(3)
|
return m.group(1) + new_version + "~ynh1"
|
||||||
|
|
||||||
content = re.sub(
|
content = re.sub(
|
||||||
r"(\s*version\s*=\s*[\"\'])([\d\.]+)(\~ynh\d+[\"\'])", repl, content
|
r"(\s*version\s*=\s*[\"\'])([\d\.]+)(\~ynh\d+[\"\'])", repl, content
|
||||||
|
|
116
tools/catalog_linter.py
Normal file → Executable file
116
tools/catalog_linter.py
Normal file → Executable file
|
@ -1,37 +1,103 @@
|
||||||
import toml
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
from functools import cache
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, Generator, List, Tuple
|
||||||
|
|
||||||
errors = []
|
import jsonschema
|
||||||
|
import toml
|
||||||
|
|
||||||
catalog = toml.load(open('apps.toml'))
|
APPS_ROOT = Path(__file__).parent.parent
|
||||||
|
|
||||||
for app, infos in catalog.items():
|
|
||||||
|
@cache
|
||||||
|
def get_catalog() -> Dict[str, Dict[str, Any]]:
|
||||||
|
catalog_path = APPS_ROOT / "apps.toml"
|
||||||
|
return toml.load(catalog_path)
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def get_categories() -> Dict[str, Any]:
|
||||||
|
categories_path = APPS_ROOT / "categories.toml"
|
||||||
|
return toml.load(categories_path)
|
||||||
|
|
||||||
|
|
||||||
|
@cache
|
||||||
|
def get_antifeatures() -> Dict[str, Any]:
|
||||||
|
antifeatures_path = APPS_ROOT / "antifeatures.toml"
|
||||||
|
return toml.load(antifeatures_path)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_schema() -> Generator[str, None, None]:
|
||||||
|
with open(APPS_ROOT / "schemas" / "apps.toml.schema.json", encoding="utf-8") as file:
|
||||||
|
apps_catalog_schema = json.load(file)
|
||||||
|
validator = jsonschema.Draft202012Validator(apps_catalog_schema)
|
||||||
|
for error in validator.iter_errors(get_catalog()):
|
||||||
|
yield f"at .{'.'.join(error.path)}: {error.message}"
|
||||||
|
|
||||||
|
|
||||||
|
def check_app(app: str, infos: Dict[str, Any]) -> Generator[Tuple[str, bool], None, None]:
|
||||||
if "state" not in infos:
|
if "state" not in infos:
|
||||||
errors.append(f"{app}: missing state info")
|
yield "state is missing", True
|
||||||
|
return
|
||||||
|
|
||||||
catalog = {app: infos for app, infos in catalog.items() if infos.get('state') == "working"}
|
if infos["state"] != "working":
|
||||||
categories = toml.load(open('categories.toml')).keys()
|
return
|
||||||
|
|
||||||
|
repo_name = infos.get("url", "").split("/")[-1]
|
||||||
|
if repo_name != f"{app}_ynh":
|
||||||
|
yield f"repo name should be {app}_ynh, not in {repo_name}", True
|
||||||
|
|
||||||
|
antifeatures = infos.get("antifeatures", [])
|
||||||
|
for antifeature in antifeatures:
|
||||||
|
if antifeature not in get_antifeatures():
|
||||||
|
yield f"unknown antifeature {antifeature}", True
|
||||||
|
|
||||||
|
category = infos.get("category")
|
||||||
|
if not category:
|
||||||
|
yield "category is missing", True
|
||||||
|
else:
|
||||||
|
if category not in get_categories():
|
||||||
|
yield f"unknown category {category}", True
|
||||||
|
|
||||||
|
subtags = infos.get("subtags", [])
|
||||||
|
for subtag in subtags:
|
||||||
|
if subtag not in get_categories().get(category, {}).get("subtags", []):
|
||||||
|
yield f"unknown subtag {category} / {subtag}", False
|
||||||
|
|
||||||
|
|
||||||
def check_apps():
|
def check_all_apps() -> Generator[Tuple[str, List[Tuple[str, bool]]], None, None]:
|
||||||
|
for app, info in get_catalog().items():
|
||||||
for app, infos in catalog.items():
|
errors = list(check_app(app, info))
|
||||||
|
if errors:
|
||||||
repo_name = infos.get("url", "").split("/")[-1]
|
yield app, errors
|
||||||
if repo_name != app + "_ynh":
|
|
||||||
yield f"{app}: repo name should be {app}_ynh, not in {repo_name}"
|
|
||||||
|
|
||||||
category = infos.get("category")
|
|
||||||
if not category:
|
|
||||||
yield f"{app}: missing category"
|
|
||||||
if category not in categories:
|
|
||||||
yield f"{app}: category {category} is not defined in categories.toml"
|
|
||||||
|
|
||||||
|
|
||||||
errors = errors + list(check_apps())
|
def main() -> None:
|
||||||
|
has_errors = False
|
||||||
|
|
||||||
for error in errors:
|
schema_errors = list(validate_schema())
|
||||||
print(error)
|
if schema_errors:
|
||||||
|
has_errors = True
|
||||||
|
print("Error while validating catalog against schema:")
|
||||||
|
for error in schema_errors:
|
||||||
|
print(f" - {error}")
|
||||||
|
if schema_errors:
|
||||||
|
print()
|
||||||
|
|
||||||
if errors:
|
for app, errors in check_all_apps():
|
||||||
sys.exit(1)
|
print(f"{app}:")
|
||||||
|
for error, is_fatal in errors:
|
||||||
|
if is_fatal:
|
||||||
|
has_errors = True
|
||||||
|
level = "error" if is_fatal else "warning"
|
||||||
|
print(f" - {level}: {error}")
|
||||||
|
|
||||||
|
if has_errors:
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
|
Loading…
Add table
Reference in a new issue