Skip to content

Commit bad6bde

Browse files
committed
Add first step for a python ynh-dev
1 parent 0e77bd1 commit bad6bde

File tree

9 files changed

+879
-125
lines changed

9 files changed

+879
-125
lines changed

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Python stuff
2+
__pycache__/
3+
.*_cache/
4+
uv.lock
5+
.python-version
6+
17
# Apps
28
*_ynh
39

@@ -11,6 +17,7 @@ Vagrantfile
1117
moulinette
1218
yunohost
1319
yunohost-admin
20+
yunohost-portal
1421
ssowat
1522

1623
# Folders

custom-catalog/catalog_manager.py

Lines changed: 0 additions & 125 deletions
This file was deleted.

pyproject.toml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
[project]
2+
name = "ynh-dev"
3+
version = "2.0"
4+
description = "Yunohost dev environment manager"
5+
readme = "README.md"
6+
authors = [
7+
{name = "YunoHost", email = "yunohost@yunohost.org"}
8+
]
9+
requires-python = ">=3.11"
10+
11+
dependencies = [
12+
"pyinotify",
13+
]
14+
15+
16+
[project.scripts]
17+
ynh-dev = "ynh-dev:main"
18+
19+
[dependency-groups]
20+
lint = [
21+
"ruff",
22+
"mypy>=1.18",
23+
]
24+
25+
[tool.uv]
26+
package = false
27+
28+
[tool.ruff]
29+
line-length = 120
30+
31+
[tool.ruff.lint]
32+
select = [
33+
"YTT", # flake8-2020
34+
"ANN", # flake8-annotations
35+
"BLE", # flake8-blind-except
36+
"B", # flake8-bugbear
37+
"A", # flake8-builtins
38+
"C4", # flake8-comprehensions
39+
"DTZ", # flake8-datetimez
40+
"ISC", # flake8-implicit-str-concat
41+
"ICN", # flake8-import-conventions
42+
# "LOG", # flake8-logging
43+
# "G", # flake8-logging-format
44+
"PIE", # flake8-pie
45+
"Q", # flake8-quotes
46+
"RET", # flake8-return
47+
"SIM", # flake8-simplify
48+
"SLOT", # flake8-slots
49+
"PTH", # flake8-use-pathlib
50+
"FLY", # flynt
51+
"I", # isort
52+
"N", # pep8-naming
53+
"PERF", # Perflint
54+
"E", # pycodestyle
55+
"W", # pycodestyle
56+
"F", # Pyflakes
57+
"PL", # Pylint
58+
"UP", # pyupgrade
59+
"FURB", # refurb
60+
"RUF", # Ruff-specific rules
61+
"TRY", # tryceratops
62+
# "D",
63+
]
64+
ignore = [
65+
"ANN401", # any-type
66+
"COM812", # missing-trailing-comma
67+
"D203", # incorrect-blank-line-before-class
68+
"PLR0911", # too-many-return-statements
69+
"PLR0912", # too-many-branches
70+
"PLR0913", # too-many-arguments
71+
"PLR0915", # too-many-statements
72+
"PLR2004", # magic-value-comparison
73+
"UP009", # utf8-encoding-declaration
74+
"TRY003", # raise-vanilla-args
75+
"D100", # undocumented-public-module
76+
"D104", # undocumented-public-package
77+
"D105", # undocumented-magic-method
78+
"D200", # unnecessary-multiline-docstring
79+
"D212", # multi-line-summary-first-line
80+
"D401", # non-imperative-mood
81+
]

ynh-dev.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
5+
6+
def main() -> None:
7+
if "container" in os.environ:
8+
from ynh_dev.ynh_dev_guest import main_container # noqa: PLC0415
9+
main_container()
10+
else:
11+
from ynh_dev.ynh_dev_host import main_host # noqa: PLC0415
12+
main_host()
13+
14+
15+
if __name__ == "__main__":
16+
main()

ynh_dev/catalog_manager.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
#!/usr/bin/python3
2+
3+
import json
4+
import os
5+
import sys
6+
import time
7+
from collections import OrderedDict
8+
from pathlib import Path
9+
from typing import Any
10+
11+
import toml
12+
import yaml
13+
14+
my_env = os.environ.copy().update({"GIT_TERMINAL_PROMPT": "0"})
15+
16+
17+
class Catalog:
18+
CATALOG_LIST_PATH = Path("/etc/yunohost/apps_catalog.yml")
19+
DEFAULT_APPS_FOLDER = Path("/ynh-dev/custom-catalog/")
20+
DEFAULT_APP_BRANCH = "master"
21+
22+
def __init__(self) -> None:
23+
assert self.CATALOG_LIST_PATH.exists(), f"Catalog list yaml file '{self.CATALOG_LIST_PATH} does not exists"
24+
25+
def build(self, folder: Path = DEFAULT_APPS_FOLDER) -> None:
26+
assert folder.exists(), f"'{folder}' doesn't exist."
27+
apps_list_path = folder / "apps.json"
28+
assert apps_list_path.exists(), "no 'apps.json' app list found."
29+
apps_list = json.load(apps_list_path.open())
30+
31+
apps = {}
32+
fail = False
33+
34+
for app, infos in apps_list.items():
35+
app = app.lower()
36+
try:
37+
app_dict = self.build_app_dict(app, infos, folder)
38+
except Exception as e:
39+
print(f"[\033[1m\033[31mFAIL\033[00m] Processing {app} failed: {e!s}")
40+
fail = True
41+
continue
42+
43+
apps[app_dict["id"]] = app_dict
44+
45+
# We also remove the app install question and resources parts which aint needed anymore by webadmin etc (or at least we think ;P)
46+
for app in apps.values():
47+
if "manifest" in app and "install" in app["manifest"]:
48+
del app["manifest"]["install"]
49+
if "manifest" in app and "resources" in app["manifest"]:
50+
del app["manifest"]["resources"]
51+
52+
data = {
53+
"apps": apps,
54+
"from_api_version": 3,
55+
}
56+
57+
output_file = folder / "catalog.json"
58+
json.dump(data, output_file.open("w"), sort_keys=True, indent=2)
59+
60+
if fail:
61+
sys.exit(1)
62+
63+
def build_app_dict(self, app: str, infos: dict[str, Any], folder: Path) -> dict[str, Any]:
64+
app_folder = folder / f"{app}_ynh"
65+
66+
# Build the dict with all the infos
67+
manifest_toml = app_folder / "manifest.toml"
68+
manifest_json = app_folder / "manifest.json"
69+
if manifest_toml.exists():
70+
manifest = toml.load(manifest_toml.open(), _dict=OrderedDict)
71+
else:
72+
manifest = json.load(manifest_json.open(), _dict=OrderedDict)
73+
74+
return {
75+
"id": app,
76+
"git": {
77+
"branch": infos.get("branch", self.DEFAULT_APP_BRANCH),
78+
"revision": infos.get("revision", "HEAD"),
79+
"url": f"file://{app_folder}",
80+
},
81+
"lastUpdate": time.time(),
82+
"manifest": manifest,
83+
"state": infos.get("state", "notworking"),
84+
"level": infos.get("level", -1),
85+
"maintained": infos.get("maintained", True),
86+
# "high_quality": infos.get("high_quality", False),
87+
# "featured": infos.get("featured", False),
88+
"category": infos.get("category"),
89+
"subtags": infos.get("subtags", []),
90+
"potential_alternative_to": infos.get("potential_alternative_to", []),
91+
"antifeatures": list(set(list(manifest.get("antifeatures", {}).keys()) + infos.get("antifeatures", []))),
92+
}
93+
94+
def reset(self) -> None:
95+
catalog_list = [{"id": "default", "url": "https://app.yunohost.org/default/"}]
96+
yaml.safe_dump(catalog_list, self.CATALOG_LIST_PATH.open("w"), default_flow_style=False)
97+
98+
def add(self) -> None:
99+
catalog_list = yaml.safe_load(self.CATALOG_LIST_PATH.open())
100+
ids = [catalog["id"] for catalog in catalog_list]
101+
if "custom" not in ids:
102+
catalog_list.append({"id": "custom", "url": None})
103+
yaml.safe_dump(catalog_list, self.CATALOG_LIST_PATH.open("w"), default_flow_style=False)
104+
105+
def override(self) -> None:
106+
catalog_list = [{"id": "custom", "url": None}]
107+
yaml.safe_dump(catalog_list, self.CATALOG_LIST_PATH.open("w"), default_flow_style=False)

0 commit comments

Comments
 (0)