mirror of
https://github.com/YunoHost/yunohost.git
synced 2024-09-03 20:06:10 +02:00
commit
9f60913bf8
97 changed files with 364 additions and 590 deletions
|
@ -37,7 +37,7 @@ workflow:
|
|||
- when: always
|
||||
|
||||
variables:
|
||||
YNH_BUILD_DIR: "ynh-build"
|
||||
YNH_BUILD_DIR: "/ynh-build"
|
||||
|
||||
include:
|
||||
- template: Code-Quality.gitlab-ci.yml
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
- DEBIAN_FRONTEND=noninteractive apt update
|
||||
artifacts:
|
||||
paths:
|
||||
- $YNH_BUILD_DIR/*.deb
|
||||
- ./*.deb
|
||||
|
||||
.build_script: &build_script
|
||||
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" install devscripts --no-install-recommends
|
||||
|
@ -17,6 +17,8 @@
|
|||
- VERSION_NIGHTLY="${VERSION}+$(date +%Y%m%d%H%M)"
|
||||
- dch --package "${PACKAGE}" --force-bad-version -v "${VERSION_NIGHTLY}" -D "unstable" --force-distribution "Daily build."
|
||||
- debuild --no-lintian -us -uc
|
||||
- cp $YNH_BUILD_DIR/*.deb ${CI_PROJECT_DIR}/
|
||||
- cd ${CI_PROJECT_DIR}
|
||||
|
||||
########################################
|
||||
# BUILD DEB
|
||||
|
@ -31,7 +33,7 @@ build-yunohost:
|
|||
- mkdir -p $YNH_BUILD_DIR/$PACKAGE
|
||||
- cat archive.tar.gz | tar -xz -C $YNH_BUILD_DIR/$PACKAGE
|
||||
- rm archive.tar.gz
|
||||
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE
|
||||
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE
|
||||
- *build_script
|
||||
|
||||
|
||||
|
@ -42,7 +44,7 @@ build-ssowat:
|
|||
script:
|
||||
- DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "ssowat \([>,=,<]+ .*\)" | grep -Po "[0-9\.]+")
|
||||
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1
|
||||
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE
|
||||
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE
|
||||
- *build_script
|
||||
|
||||
build-moulinette:
|
||||
|
@ -52,5 +54,5 @@ build-moulinette:
|
|||
script:
|
||||
- DEBIAN_DEPENDS=$(cat debian/control | tr "," "\n" | grep -Po "moulinette \([>,=,<]+ .*\)" | grep -Po "[0-9\.]+")
|
||||
- git clone $YNH_SOURCE/$PACKAGE -b $CI_COMMIT_REF_NAME $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE -b $DEBIAN_DEPENDS $YNH_BUILD_DIR/$PACKAGE --depth 1 || git clone $YNH_SOURCE/$PACKAGE $YNH_BUILD_DIR/$PACKAGE --depth 1
|
||||
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $(pwd)/$YNH_BUILD_DIR/$PACKAGE
|
||||
- DEBIAN_FRONTEND=noninteractive apt --assume-yes -o Dpkg::Options::="--force-confold" build-dep $YNH_BUILD_DIR/$PACKAGE
|
||||
- *build_script
|
||||
|
|
|
@ -17,7 +17,7 @@ upgrade:
|
|||
image: "after-install"
|
||||
script:
|
||||
- apt-get update -o Acquire::Retries=3
|
||||
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb
|
||||
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ${CI_PROJECT_DIR}/*.deb
|
||||
|
||||
|
||||
install-postinstall:
|
||||
|
@ -25,5 +25,5 @@ install-postinstall:
|
|||
image: "before-install"
|
||||
script:
|
||||
- apt-get update -o Acquire::Retries=3
|
||||
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb
|
||||
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ${CI_PROJECT_DIR}/*.deb
|
||||
- yunohost tools postinstall -d domain.tld -u syssa -F 'Syssa Mine' -p the_password --ignore-dyndns --force-diskspace
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.install_debs: &install_debs
|
||||
- apt-get update -o Acquire::Retries=3
|
||||
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ./$YNH_BUILD_DIR/*.deb
|
||||
- DEBIAN_FRONTEND=noninteractive SUDO_FORCE_REMOVE=yes apt --assume-yes -o Dpkg::Options::="--force-confold" --allow-downgrades install ${CI_PROJECT_DIR}/*.deb
|
||||
- pip3 install -U mock pip pytest pytest-cov pytest-mock pytest-sugar requests-mock tox ansi2html black jinja2 "packaging<22"
|
||||
|
||||
.test-stage:
|
||||
|
|
|
@ -6,7 +6,7 @@ map $http_upgrade $connection_upgrade {
|
|||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name {{ domain }}{% if xmpp_enabled != "True" %} xmpp-upload.{{ domain }} muc.{{ domain }}{% endif %};
|
||||
server_name {{ domain }}{% if xmpp_enabled == "True" %} xmpp-upload.{{ domain }} muc.{{ domain }}{% endif %};
|
||||
|
||||
access_by_lua_file /usr/share/ssowat/access.lua;
|
||||
|
||||
|
|
49
debian/changelog
vendored
49
debian/changelog
vendored
|
@ -1,3 +1,52 @@
|
|||
yunohost (11.1.5) stable; urgency=low
|
||||
|
||||
- Release as stable !
|
||||
|
||||
- diagnosis: we can't yield an ERROR if there's no IPv6, otherwise that blocks all subsequent network-related diagnoser because of the dependency system ... (ade92e43)
|
||||
- domains: fix domain_config.toml typos in conditions (480f7a43)
|
||||
- certs: Don't try restarting metronome if no domain configured for it (452ba8bb)
|
||||
|
||||
Thanks to all contributors <3 ! (Axolotle)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 01 Feb 2023 20:21:56 +0100
|
||||
|
||||
yunohost (11.1.4.1) testing; urgency=low
|
||||
|
||||
- debian: don't dump upgradable apps during postinst's catalog update (82d30f02)
|
||||
- ynh_setup_source: Output checksums when source is 'corrupt' ([#1578](https://github.com/yunohost/yunohost/pull/1578))
|
||||
- metronome: Auto-enable/disable metronome if there's no/at least one domain configured for XMPP (c990cee6)
|
||||
|
||||
Thanks to all contributors <3 ! (tituspijean)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Wed, 01 Feb 2023 17:10:32 +0100
|
||||
|
||||
yunohost (11.1.4) testing; urgency=low
|
||||
|
||||
- settings: Add DNS exposure setting given the IP version ([#1451](https://github.com/yunohost/yunohost/pull/1451))
|
||||
|
||||
Thanks to all contributors <3 ! (Tagada)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 30 Jan 2023 16:28:56 +0100
|
||||
|
||||
yunohost (11.1.3.1) testing; urgency=low
|
||||
|
||||
- nginx: add xmpp-upload. and muc. server_name only if xmpp_enabled is enabled (c444dee4)
|
||||
- [i18n] Translations updated for Arabic, Basque, French, Galician, Spanish, Turkish
|
||||
|
||||
Thanks to all contributors <3 ! (Alperen İsa Nalbant, ButterflyOfFire, cristian amoyao, Éric Gaspar, José M, Kayou, ppr, quiwy, xabirequejo)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Mon, 30 Jan 2023 15:44:30 +0100
|
||||
|
||||
yunohost (11.1.3) testing; urgency=low
|
||||
|
||||
- helpers: Include procedures in MySQL database backup ([#1570](https://github.com/yunohost/yunohost/pull/1570))
|
||||
- users: be able to change the loginShell of a user ([#1538](https://github.com/yunohost/yunohost/pull/1538))
|
||||
- debian: refresh catalog upon package upgrade (be5b1c1b)
|
||||
|
||||
Thanks to all contributors <3 ! (Éric Gaspar, Kay0u, ljf, Metin Bektas)
|
||||
|
||||
-- Alexandre Aubin <alex.aubin@mailoo.org> Thu, 19 Jan 2023 23:08:10 +0100
|
||||
|
||||
yunohost (11.1.2.2) testing; urgency=low
|
||||
|
||||
- Minor technical fixes (b37d4baf, 68342171)
|
||||
|
|
2
debian/control
vendored
2
debian/control
vendored
|
@ -10,7 +10,7 @@ Package: yunohost
|
|||
Essential: yes
|
||||
Architecture: all
|
||||
Depends: ${python3:Depends}, ${misc:Depends}
|
||||
, moulinette (>= 11.0), ssowat (>= 11.0)
|
||||
, moulinette (>= 11.1), ssowat (>= 11.1)
|
||||
, python3-psutil, python3-requests, python3-dnspython, python3-openssl
|
||||
, python3-miniupnpc, python3-dbus, python3-jinja2
|
||||
, python3-toml, python3-packaging, python3-publicsuffix2
|
||||
|
|
3
debian/postinst
vendored
3
debian/postinst
vendored
|
@ -28,6 +28,9 @@ do_configure() {
|
|||
|
||||
echo "Re-diagnosing server health..."
|
||||
yunohost diagnosis run --force
|
||||
|
||||
echo "Refreshing app catalog..."
|
||||
yunohost tools update apps --output-as none || true
|
||||
fi
|
||||
|
||||
# Trick to let yunohost handle the restart of the API,
|
||||
|
|
|
@ -22,19 +22,18 @@ import os
|
|||
import sys
|
||||
import yaml
|
||||
import json
|
||||
import requests
|
||||
|
||||
|
||||
def main():
|
||||
""" """
|
||||
with open("../share/actionsmap.yml") as f:
|
||||
action_map = yaml.safe_load(f)
|
||||
|
||||
try:
|
||||
with open("/etc/yunohost/current_host", "r") as f:
|
||||
domain = f.readline().rstrip()
|
||||
except IOError:
|
||||
domain = requests.get("http://ip.yunohost.org").text
|
||||
# try:
|
||||
# with open("/etc/yunohost/current_host", "r") as f:
|
||||
# domain = f.readline().rstrip()
|
||||
# except IOError:
|
||||
# domain = requests.get("http://ip.yunohost.org").text
|
||||
|
||||
with open("../debian/changelog") as f:
|
||||
top_changelog = f.readline()
|
||||
api_version = top_changelog[top_changelog.find("(") + 1 : top_changelog.find(")")]
|
||||
|
|
|
@ -31,7 +31,6 @@ def get_dict_actions(OPTION_SUBTREE, category):
|
|||
|
||||
|
||||
with open(ACTIONSMAP_FILE, "r") as stream:
|
||||
|
||||
# Getting the dictionary containning what actions are possible per category
|
||||
OPTION_TREE = yaml.safe_load(stream)
|
||||
|
||||
|
@ -65,7 +64,6 @@ with open(ACTIONSMAP_FILE, "r") as stream:
|
|||
os.makedirs(BASH_COMPLETION_FOLDER, exist_ok=True)
|
||||
|
||||
with open(BASH_COMPLETION_FILE, "w") as generated_file:
|
||||
|
||||
# header of the file
|
||||
generated_file.write("#\n")
|
||||
generated_file.write("# completion for yunohost\n")
|
||||
|
|
|
@ -20,7 +20,6 @@ def get_current_commit():
|
|||
|
||||
|
||||
def render(helpers):
|
||||
|
||||
current_commit = get_current_commit()
|
||||
|
||||
data = {
|
||||
|
@ -56,20 +55,17 @@ def render(helpers):
|
|||
|
||||
class Parser:
|
||||
def __init__(self, filename):
|
||||
|
||||
self.filename = filename
|
||||
self.file = open(filename, "r").readlines()
|
||||
self.blocks = None
|
||||
|
||||
def parse_blocks(self):
|
||||
|
||||
self.blocks = []
|
||||
|
||||
current_reading = "void"
|
||||
current_block = {"name": None, "line": -1, "comments": [], "code": []}
|
||||
|
||||
for i, line in enumerate(self.file):
|
||||
|
||||
if line.startswith("#!/bin/bash"):
|
||||
continue
|
||||
|
||||
|
@ -117,7 +113,6 @@ class Parser:
|
|||
current_reading = "code"
|
||||
|
||||
elif current_reading == "code":
|
||||
|
||||
if line == "}":
|
||||
# We're getting out of the function
|
||||
current_reading = "void"
|
||||
|
@ -138,7 +133,6 @@ class Parser:
|
|||
continue
|
||||
|
||||
def parse_block(self, b):
|
||||
|
||||
b["brief"] = ""
|
||||
b["details"] = ""
|
||||
b["usage"] = ""
|
||||
|
@ -164,7 +158,6 @@ class Parser:
|
|||
|
||||
elif subblock.startswith("usage"):
|
||||
for line in subblock.split("\n"):
|
||||
|
||||
if line.startswith("| arg"):
|
||||
linesplit = line.split()
|
||||
argname = linesplit[2]
|
||||
|
@ -216,7 +209,6 @@ def malformed_error(line_number):
|
|||
|
||||
|
||||
def main():
|
||||
|
||||
helper_files = sorted(glob.glob("../helpers/*"))
|
||||
helpers = []
|
||||
|
||||
|
|
|
@ -60,7 +60,6 @@ def main():
|
|||
|
||||
# man pages of "yunohost *"
|
||||
with open(ACTIONSMAP_FILE, "r") as actionsmap:
|
||||
|
||||
# Getting the dictionary containning what actions are possible per domain
|
||||
actionsmap = ordered_yaml_load(actionsmap)
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ from yunohost.utils.resources import AppResourceClassesByType
|
|||
resources = sorted(AppResourceClassesByType.values(), key=lambda r: r.priority)
|
||||
|
||||
for klass in resources:
|
||||
|
||||
doc = klass.__doc__.replace("\n ", "\n")
|
||||
|
||||
print("")
|
||||
|
|
|
@ -167,7 +167,7 @@ ynh_setup_source() {
|
|||
|| ynh_die --message="$out"
|
||||
# Check the control sum
|
||||
echo "${src_sum} ${src_filename}" | ${src_sumprg} --check --status \
|
||||
|| ynh_die --message="Corrupt source"
|
||||
|| ynh_die --message="Corrupt source for ${src_filename}: Expected ${src_sum} but got $(${src_sumprg} ${src_filename} | cut --delimiter=' ' --fields=1) (size: $(du -hs ${src_filename} | cut --delimiter=' ' --fields=1))."
|
||||
fi
|
||||
|
||||
# Keep files to be backup/restored at the end of the helper
|
||||
|
|
|
@ -74,8 +74,22 @@ do_post_regen() {
|
|||
chown -R metronome: /var/lib/metronome/
|
||||
chown -R metronome: /etc/metronome/conf.d/
|
||||
|
||||
[[ -z "$regen_conf_files" ]] \
|
||||
|| systemctl restart metronome
|
||||
if [[ -z "$(ls /etc/metronome/conf.d/*.cfg.lua 2>/dev/null)" ]]
|
||||
then
|
||||
if systemctl is-enabled metronome &>/dev/null
|
||||
then
|
||||
systemctl disable metronome --now 2>/dev/null
|
||||
fi
|
||||
else
|
||||
if ! systemctl is-enabled metronome &>/dev/null
|
||||
then
|
||||
systemctl enable metronome --now 2>/dev/null
|
||||
sleep 3
|
||||
fi
|
||||
|
||||
[[ -z "$regen_conf_files" ]] \
|
||||
|| systemctl restart metronome
|
||||
fi
|
||||
}
|
||||
|
||||
do_$1_regen ${@:2}
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
"app_not_installed": "إنّ التطبيق {app} غير مُنصَّب",
|
||||
"app_not_properly_removed": "لم يتم حذف تطبيق {app} بشكلٍ جيّد",
|
||||
"app_removed": "تمت إزالة تطبيق {app}",
|
||||
"app_requirements_checking": "جار فحص الحزم اللازمة لـ {app}…",
|
||||
"app_sources_fetch_failed": "تعذرت عملية جلب مصادر الملفات",
|
||||
"app_requirements_checking": "جار فحص متطلبات تطبيق {app}…",
|
||||
"app_sources_fetch_failed": "تعذر جلب ملفات المصدر ، هل عنوان URL صحيح؟",
|
||||
"app_unknown": "برنامج مجهول",
|
||||
"app_upgrade_app_name": "جارٍ تحديث {app}…",
|
||||
"app_upgrade_failed": "تعذرت عملية ترقية {app}",
|
||||
|
@ -39,7 +39,7 @@
|
|||
"done": "تم",
|
||||
"downloading": "عملية التنزيل جارية …",
|
||||
"dyndns_ip_updated": "لقد تم تحديث عنوان الإيبي الخاص بك على نظام أسماء النطاقات الديناميكي",
|
||||
"dyndns_key_generating": "عملية توليد مفتاح نظام أسماء النطاقات جارية. يمكن للعملية أن تستغرق بعضا من الوقت…",
|
||||
"dyndns_key_generating": "جارٍ إنشاء مفتاح DNS ... قد يستغرق الأمر بعض الوقت.",
|
||||
"dyndns_key_not_found": "لم يتم العثور على مفتاح DNS الخاص باسم النطاق هذا",
|
||||
"extracting": "عملية فك الضغط جارية…",
|
||||
"installation_complete": "إكتملت عملية التنصيب",
|
||||
|
@ -49,15 +49,15 @@
|
|||
"pattern_domain": "يتوجب أن يكون إسم نطاق صالح (مثل my-domain.org)",
|
||||
"pattern_email": "يتوجب أن يكون عنوان بريد إلكتروني صالح (مثل someone@domain.org)",
|
||||
"pattern_password": "يتوجب أن تكون مكونة من 3 حروف على الأقل",
|
||||
"restore_extracting": "جارٍ فك الضغط عن الملفات التي نحتاجها من النسخة الاحتياطية…",
|
||||
"restore_extracting": "جارٍ فك الضغط عن الملفات اللازمة من النسخة الاحتياطية…",
|
||||
"server_shutdown": "سوف ينطفئ الخادوم",
|
||||
"server_shutdown_confirm": "سوف ينطفئ الخادوم حالا. متأكد ؟ [{answers}]",
|
||||
"server_reboot": "سيعاد تشغيل الخادوم",
|
||||
"server_reboot_confirm": "سيعاد تشغيل الخادوم في الحين. هل أنت متأكد ؟ [{answers}]",
|
||||
"service_add_failed": "تعذرت إضافة خدمة '{service}'",
|
||||
"service_already_stopped": "إنّ خدمة '{service}' متوقفة مِن قبلُ",
|
||||
"service_disabled": "لن يتم إطلاق خدمة '{service}' أثناء بداية تشغيل النظام.",
|
||||
"service_enabled": "تم تنشيط خدمة '{service}'",
|
||||
"service_disabled": "لن يتم إطلاق خدمة '{service}' أثناء بداية تشغيل النظام بتاتا.",
|
||||
"service_enabled": "سيتم الآن بدء تشغيل الخدمة '{service}' تلقائيًا أثناء تمهيد النظام.",
|
||||
"service_removed": "تمت إزالة خدمة '{service}'",
|
||||
"service_started": "تم إطلاق تشغيل خدمة '{service}'",
|
||||
"service_stopped": "تمّ إيقاف خدمة '{service}'",
|
||||
|
@ -71,10 +71,10 @@
|
|||
"user_deleted": "تم حذف المستخدم",
|
||||
"user_deletion_failed": "لا يمكن حذف المستخدم",
|
||||
"user_unknown": "المستخدم {user} مجهول",
|
||||
"user_update_failed": "لا يمكن تحديث المستخدم",
|
||||
"user_updated": "تم تحديث المستخدم",
|
||||
"user_update_failed": "لا يمكن تحديث المستخدم {user}: {error}",
|
||||
"user_updated": "تم تحديث معلومات المستخدم",
|
||||
"yunohost_installing": "عملية تنصيب واي يونوهوست جارية …",
|
||||
"yunohost_not_installed": "إنَّ واي يونوهوست ليس مُنَصَّب أو هو مثبت حاليا بشكل خاطئ. قم بتنفيذ الأمر 'yunohost tools postinstall'",
|
||||
"yunohost_not_installed": "إنَّ واي يونوهوست ليس مُنَصَّب بشكل جيد. فضلًا قم بتنفيذ الأمر 'yunohost tools postinstall'",
|
||||
"migrations_list_conflict_pending_done": "لا يمكنك استخدام --previous و --done معًا على نفس سطر الأوامر.",
|
||||
"service_description_metronome": "يُدير حسابات الدردشة الفورية XMPP",
|
||||
"service_description_nginx": "يقوم بتوفير النفاذ و السماح بالوصول إلى كافة مواقع الويب المستضافة على خادومك",
|
||||
|
@ -119,7 +119,7 @@
|
|||
"already_up_to_date": "كل شيء على ما يرام. ليس هناك ما يتطلّب تحديثًا.",
|
||||
"service_description_slapd": "يخزّن المستخدمين والنطاقات والمعلومات المتعلقة بها",
|
||||
"service_reloaded": "تم إعادة تشغيل خدمة '{service}'",
|
||||
"service_restarted": "تم إعادة تشغيل خدمة '{service}'",
|
||||
"service_restarted": "تم إعادة تشغيل خدمة '{service}'",
|
||||
"group_unknown": "الفريق '{group}' مجهول",
|
||||
"group_deletion_failed": "فشلت عملية حذف الفريق '{group}': {error}",
|
||||
"group_deleted": "تم حذف الفريق '{group}'",
|
||||
|
@ -193,5 +193,21 @@
|
|||
"diagnosis_ports_ok": "المنفذ {port} مفتوح ومتاح الوصول إليه مِن الخارج.",
|
||||
"global_settings_setting_smtp_allow_ipv6": "سماح IPv6",
|
||||
"disk_space_not_sufficient_update": "ليس هناك مساحة كافية لتحديث هذا التطبيق",
|
||||
"domain_cert_gen_failed": "لا يمكن إعادة توليد الشهادة"
|
||||
}
|
||||
"domain_cert_gen_failed": "لا يمكن إعادة توليد الشهادة",
|
||||
"diagnosis_apps_issue": "تم العثور على مشكلة في تطبيق {app}",
|
||||
"tools_upgrade": "تحديث حُزم النظام",
|
||||
"service_description_yunomdns": "يسمح لك بالوصول إلى خادمك الخاص باستخدام 'yunohost.local' في شبكتك المحلية",
|
||||
"good_practices_about_user_password": "أنت الآن على وشك تحديد كلمة مرور مستخدم جديدة. يجب أن تتكون كلمة المرور من 8 أحرف على الأقل - على الرغم من أنه من الممارسات الجيدة استخدام كلمة مرور أطول (أي عبارة مرور) و / أو مجموعة متنوعة من الأحرف (الأحرف الكبيرة والصغيرة والأرقام والأحرف الخاصة).",
|
||||
"root_password_changed": "تم تغيير كلمة مرور الجذر",
|
||||
"root_password_desynchronized": "تم تغيير كلمة مرور المدير ، لكن لم يتمكن YunoHost من نشرها على كلمة مرور الجذر!",
|
||||
"user_import_bad_line": "سطر غير صحيح {line}: {details}",
|
||||
"user_import_success": "تم استيراد المستخدمين بنجاح",
|
||||
"visitors": "الزوار",
|
||||
"password_too_simple_3": "يجب أن تتكون كلمة المرور من 8 أحرف على الأقل وأن تحتوي على أرقام وأرقام علوية وسفلية وأحرف خاصة",
|
||||
"password_too_simple_4": "يجب أن تتكون كلمة المرور من 12 حرفًا على الأقل وأن تحتوي على أرقام وأرقام علوية وسفلية وأحرف خاصة",
|
||||
"service_unknown": "الخدمة '{service}' غير معروفة",
|
||||
"unbackup_app": "لن يتم حفظ التطبيق '{app}'",
|
||||
"unrestore_app": "لن يتم استعادة التطبيق '{app}'",
|
||||
"yunohost_already_installed": "إنّ YunoHost مُنصّب مِن قَبل",
|
||||
"hook_name_unknown": "إسم الإجراء '{name}' غير معروف"
|
||||
}
|
||||
|
|
|
@ -565,7 +565,6 @@
|
|||
"diagnosis_apps_issue": "Ein Problem für die App {app} ist aufgetreten",
|
||||
"config_validate_time": "Sollte eine zulässige Zeit wie HH:MM sein",
|
||||
"config_validate_url": "Sollte eine zulässige web URL sein",
|
||||
"config_version_not_supported": "Konfigurationspanel Versionen '{version}' sind nicht unterstützt.",
|
||||
"diagnosis_apps_allgood": "Alle installierten Apps berücksichtigen die grundlegenden Paketierungspraktiken",
|
||||
"diagnosis_apps_broken": "Diese App ist im YunoHost-Applikationskatalog momentan als defekt gekennzeichnet. Es könnte sich dabei um einen vorübergehendes Problem handeln. Während der/die Betreuer:in versucht das Problem zu beheben, ist die Upgrade-Funktion für diese App gesperrt.",
|
||||
"diagnosis_apps_not_in_app_catalog": "Diese Applikation steht nicht im Applikationskatalog von YunoHost. Sie sollten in Betracht ziehen, sie zu deinstallieren, weil sie keine Aktualisierungen mehr erhält und die Integrität und die Sicherheit Ihres Systems kompromittieren könnte.",
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
"app_full_domain_unavailable": "Sorry, this app must be installed on a domain of its own, but other apps are already installed on the domain '{domain}'. You could use a subdomain dedicated to this app instead.",
|
||||
"app_id_invalid": "Invalid app ID",
|
||||
"app_install_failed": "Unable to install {app}: {error}",
|
||||
"app_resource_failed": "Provisioning, deprovisioning, or updating resources for {app} failed: {error}",
|
||||
"app_install_files_invalid": "These files cannot be installed",
|
||||
"app_install_script_failed": "An error occurred inside the app installation script",
|
||||
"app_label_deprecated": "This command is deprecated! Please use the new command 'yunohost user permission update' to manage the app label.",
|
||||
|
@ -50,6 +49,7 @@
|
|||
"app_remove_after_failed_install": "Removing the app after installation failure...",
|
||||
"app_removed": "{app} uninstalled",
|
||||
"app_requirements_checking": "Checking requirements for {app}...",
|
||||
"app_resource_failed": "Provisioning, deprovisioning, or updating resources for {app} failed: {error}",
|
||||
"app_restore_failed": "Could not restore {app}: {error}",
|
||||
"app_restore_script_failed": "An error occured inside the app restore script",
|
||||
"app_sources_fetch_failed": "Could not fetch source files, is the URL correct?",
|
||||
|
@ -245,6 +245,7 @@
|
|||
"diagnosis_ip_no_ipv4": "The server does not have working IPv4.",
|
||||
"diagnosis_ip_no_ipv6": "The server does not have working IPv6.",
|
||||
"diagnosis_ip_no_ipv6_tip": "Having a working IPv6 is not mandatory for your server to work, but it is better for the health of the Internet as a whole. IPv6 should usually be automatically configured by the system or your provider if it's available. Otherwise, you might need to configure a few things manually as explained in the documentation here: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>. If you cannot enable IPv6 or if it seems too technical for you, you can also safely ignore this warning.",
|
||||
"diagnosis_ip_no_ipv6_tip_important": "IPv6 should usually be automatically configured by the system or your provider if it's available. Otherwise, you might need to configure a few things manually as explained in the documentation here: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>.",
|
||||
"diagnosis_ip_not_connected_at_all": "The server does not seem to be connected to the Internet at all!?",
|
||||
"diagnosis_ip_weird_resolvconf": "DNS resolution seems to be working, but it looks like you're using a custom <code>/etc/resolv.conf</code>.",
|
||||
"diagnosis_ip_weird_resolvconf_details": "The file <code>/etc/resolv.conf</code> should be a symlink to <code>/etc/resolvconf/run/resolv.conf</code> itself pointing to <code>127.0.0.1</code> (dnsmasq). If you want to manually configure DNS resolvers, please edit <code>/etc/resolv.dnsmasq.conf</code>.",
|
||||
|
@ -399,6 +400,8 @@
|
|||
"firewall_reloaded": "Firewall reloaded",
|
||||
"firewall_rules_cmd_failed": "Some firewall rule commands have failed. More info in log.",
|
||||
"global_settings_reset_success": "Reset global settings",
|
||||
"global_settings_setting_dns_exposure": "IP versions to consider for DNS configuration and diagnosis",
|
||||
"global_settings_setting_dns_exposure_help": "NB: This only affects the recommended DNS configuration and diagnosis checks. This does not affect system configurations.",
|
||||
"global_settings_setting_admin_strength": "Admin password strength requirements",
|
||||
"global_settings_setting_admin_strength_help": "These requirements are only enforced when initializing or changing the password",
|
||||
"global_settings_setting_backup_compress_tar_archives": "Compress backups",
|
||||
|
@ -408,7 +411,6 @@
|
|||
"global_settings_setting_nginx_redirect_to_https": "Force HTTPS",
|
||||
"global_settings_setting_nginx_redirect_to_https_help": "Redirect HTTP requests to HTTPs by default (DO NOT TURN OFF unless you really know what you're doing!)",
|
||||
"global_settings_setting_passwordless_sudo": "Allow admins to use 'sudo' without re-typing their passwords",
|
||||
"global_settings_setting_passwordless_sudo_help": "FIXME",
|
||||
"global_settings_setting_pop3_enabled": "Enable POP3",
|
||||
"global_settings_setting_pop3_enabled_help": "Enable the POP3 protocol for the mail server",
|
||||
"global_settings_setting_portal_theme": "Portal theme",
|
||||
|
@ -471,6 +473,7 @@
|
|||
"invalid_number_max": "Must be lesser than {max}",
|
||||
"invalid_number_min": "Must be greater than {min}",
|
||||
"invalid_regex": "Invalid regex:'{regex}'",
|
||||
"invalid_shell": "Invalid shell: {shell}",
|
||||
"ip6tables_unavailable": "You cannot play with ip6tables here. You are either in a container or your kernel does not support it",
|
||||
"iptables_unavailable": "You cannot play with iptables here. You are either in a container or your kernel does not support it",
|
||||
"ldap_attribute_already_exists": "LDAP attribute '{attribute}' already exists with value '{value}'",
|
||||
|
|
|
@ -78,8 +78,8 @@
|
|||
"pattern_backup_archive_name": "Debe ser un nombre de archivo válido con un máximo de 30 caracteres, solo se admiten caracteres alfanuméricos y los caracteres -_. (guiones y punto)",
|
||||
"pattern_domain": "El nombre de dominio debe ser válido (por ejemplo mi-dominio.org)",
|
||||
"pattern_email": "Debe ser una dirección de correo electrónico válida, sin el símbolo '+' (ej. alguien@ejemplo.com)",
|
||||
"pattern_firstname": "Debe ser un nombre válido",
|
||||
"pattern_lastname": "Debe ser un apellido válido",
|
||||
"pattern_firstname": "Debe ser un nombre válido (al menos 3 caracteres)",
|
||||
"pattern_lastname": "Debe ser un apellido válido (al menos 3 caracteres)",
|
||||
"pattern_mailbox_quota": "Debe ser un tamaño con el sufijo «b/k/M/G/T» o «0» para no tener una cuota",
|
||||
"pattern_password": "Debe contener al menos 3 caracteres",
|
||||
"pattern_port_or_range": "Debe ser un número de puerto válido (es decir entre 0-65535) o un intervalo de puertos (por ejemplo 100:200)",
|
||||
|
@ -266,7 +266,7 @@
|
|||
"migrations_failed_to_load_migration": "No se pudo cargar la migración {id}: {error}",
|
||||
"migrations_dependencies_not_satisfied": "Ejecutar estas migraciones: «{dependencies_id}» antes de migrar {id}.",
|
||||
"migrations_already_ran": "Esas migraciones ya se han realizado: {ids}",
|
||||
"mail_unavailable": "Esta dirección de correo está reservada y será asignada automáticamente al primer usuario",
|
||||
"mail_unavailable": "Esta dirección de correo electrónico está reservada para el grupo de administradores",
|
||||
"mailbox_disabled": "Correo desactivado para usuario {user}",
|
||||
"log_tools_reboot": "Reiniciar el servidor",
|
||||
"log_tools_shutdown": "Apagar el servidor",
|
||||
|
@ -316,7 +316,7 @@
|
|||
"dyndns_could_not_check_available": "No se pudo comprobar si {domain} está disponible en {provider}.",
|
||||
"domain_dns_conf_is_just_a_recommendation": "Este comando muestra la configuración *recomendada*. No configura las entradas DNS por ti. Es tu responsabilidad configurar la zona DNS en su registrador según esta recomendación.",
|
||||
"dpkg_lock_not_available": "Esta orden no se puede ejecutar en este momento ,parece que programa está usando el bloqueo de dpkg (el gestor de paquetes del sistema)",
|
||||
"dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/APT (los gestores de paquetes del sistema) parecen estar mal configurados... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo apt install --fix-broken` y/o `sudo dpkg --configure -a`.",
|
||||
"dpkg_is_broken": "No puede hacer esto en este momento porque dpkg/APT (los gestores de paquetes del sistema) parecen estar mal configurados... Puede tratar de solucionar este problema conectando a través de SSH y ejecutando `sudo apt install --fix-broken` y/o `sudo dpkg --configure -a` y/o `sudo dpkg --audit`.",
|
||||
"confirm_app_install_thirdparty": "¡PELIGRO! Esta aplicación no forma parte del catálogo de aplicaciones de YunoHost. La instalación de aplicaciones de terceros puede comprometer la integridad y seguridad de tu sistema. Probablemente NO deberías instalarla a menos que sepas lo que estás haciendo. NO se proporcionará NINGÚN SOPORTE si esta aplicación no funciona o rompe su sistema… Si de todos modos quieres correr ese riesgo, escribe '{answers}'",
|
||||
"confirm_app_install_danger": "¡PELIGRO! ¡Esta aplicación sigue siendo experimental (si no es expresamente no funcional)! Probablemente NO deberías instalarla a menos que sepas lo que estás haciendo. NO se proporcionará NINGÚN SOPORTE si esta aplicación no funciona o rompe tu sistema… Si de todos modos quieres correr ese riesgo, escribe '{answers}'",
|
||||
"confirm_app_install_warning": "Aviso: esta aplicación puede funcionar pero no está bien integrada en YunoHost. Algunas herramientas como la autentificación única y respaldo/restauración podrían no estar disponibles. ¿Instalar de todos modos? [{answers}] ",
|
||||
|
@ -454,7 +454,7 @@
|
|||
"diagnosis_ports_forwarding_tip": "Para solucionar este incidente, lo más seguro deberías configurar la redirección de los puertos en el router como se especifica en <a href='https://yunohost.org/isp_box_config'>https://yunohost.org/isp_box_config</a>",
|
||||
"certmanager_warning_subdomain_dns_record": "El subdominio '{subdomain}' no se resuelve en la misma dirección IP que '{domain}'. Algunas funciones no estarán disponibles hasta que solucione esto y regenere el certificado.",
|
||||
"domain_cannot_add_xmpp_upload": "No puede agregar dominios que comiencen con 'xmpp-upload'. Este tipo de nombre está reservado para la función de carga XMPP integrada en YunoHost.",
|
||||
"yunohost_postinstall_end_tip": "¡La post-instalación completada! Para finalizar su configuración, por favor considere:\n - agregar un primer usuario a través de la sección 'Usuarios' del administrador web (o 'yunohost user create <username>' en la línea de comandos);\n - diagnosticar problemas potenciales a través de la sección 'Diagnóstico' del administrador web (o 'yunohost diagnosis run' en la línea de comandos);\n - leyendo las partes 'Finalizando su configuración' y 'Conociendo YunoHost' en la documentación del administrador: https://yunohost.org/admindoc.",
|
||||
"yunohost_postinstall_end_tip": "¡La post-instalación completada! Para finalizar su configuración, por favor considere:\n - diagnosticar problemas potenciales a través de la sección 'Diagnóstico' del administrador web (o 'yunohost diagnosis run' en la línea de comandos);\n - leyendo las partes 'Finalizando su configuración' y 'Conociendo YunoHost' en la documentación del administrador: https://yunohost.org/admindoc.",
|
||||
"diagnosis_dns_point_to_doc": "Por favor, consulta la documentación en <a href='https://yunohost.org/dns_config'>https://yunohost.org/dns_config</a> si necesitas ayuda para configurar los registros DNS.",
|
||||
"diagnosis_ip_global": "IP Global: <code>{global}</code>",
|
||||
"diagnosis_mail_outgoing_port_25_ok": "El servidor de email SMTP puede mandar emails (puerto saliente 25 no está bloqueado).",
|
||||
|
@ -552,7 +552,6 @@
|
|||
"config_validate_email": "Debe ser una dirección de correo correcta",
|
||||
"config_validate_time": "Debe ser una hora valida en formato HH:MM",
|
||||
"config_validate_url": "Debe ser una URL válida",
|
||||
"config_version_not_supported": "Las versiones del panel de configuración '{version}' no están soportadas.",
|
||||
"domain_remove_confirm_apps_removal": "La supresión de este dominio también eliminará las siguientes aplicaciones:\n{apps}\n\n¿Seguro? [{answers}]",
|
||||
"domain_registrar_is_not_configured": "El registrador aún no ha configurado el dominio {domain}.",
|
||||
"diagnosis_apps_not_in_app_catalog": "Esta aplicación se encuentra ausente o ya no figura en el catálogo de aplicaciones de YunoHost. Deberías considerar desinstalarla ya que no recibirá actualizaciones y podría comprometer la integridad y seguridad de tu sistema.",
|
||||
|
@ -599,7 +598,7 @@
|
|||
"postinstall_low_rootfsspace": "El sistema de archivos raíz tiene un espacio total inferior a 10 GB, ¡lo cual es bastante preocupante! ¡Es probable que se quede sin espacio en disco muy rápidamente! Se recomienda tener al menos 16 GB para el sistema de archivos raíz. Si desea instalar YunoHost a pesar de esta advertencia, vuelva a ejecutar la instalación posterior con --force-diskspace",
|
||||
"migration_ldap_rollback_success": "Sistema revertido.",
|
||||
"permission_protected": "Permiso {permission} está protegido. No puede agregar o quitar el grupo de visitantes a/desde este permiso.",
|
||||
"global_settings_setting_ssowat_panel_overlay_enabled": "Habilitar la superposición del panel SSOwat",
|
||||
"global_settings_setting_ssowat_panel_overlay_enabled": "Habilitar el pequeño cuadrado de acceso directo al portal \"YunoHost\" en las aplicaciones",
|
||||
"migration_0021_start": "Iniciando migración a Bullseye",
|
||||
"migration_0021_patching_sources_list": "Parcheando los sources.lists...",
|
||||
"migration_0021_main_upgrade": "Iniciando actualización principal...",
|
||||
|
@ -678,5 +677,76 @@
|
|||
"config_action_failed": "Error al ejecutar la acción '{action}': {error}",
|
||||
"config_forbidden_readonly_type": "El tipo '{type}' no puede establecerse como solo lectura, utilice otro tipo para representar este valor (arg id relevante: '{id}').",
|
||||
"diagnosis_using_stable_codename": "<cmd>apt</cmd> (el gestor de paquetes del sistema) está configurado actualmente para instalar paquetes de nombre en clave 'estable', en lugar del nombre en clave de la versión actual de Debian (bullseye).",
|
||||
"diagnosis_using_stable_codename_details": "Esto suele deberse a una configuración incorrecta de su proveedor de alojamiento. Esto es peligroso, porque tan pronto como la siguiente versión de Debian se convierta en la nueva 'estable', <cmd>apt</cmd> querrá actualizar todos los paquetes del sistema sin pasar por un procedimiento de migración adecuado. Se recomienda arreglar esto editando la fuente de apt para el repositorio base de Debian, y reemplazar la palabra clave <cmd>stable</cmd> por <cmd>bullseye</cmd>. El fichero de configuración correspondiente debería ser <cmd>/etc/apt/sources.list</cmd>, o un fichero en <cmd>/etc/apt/sources.list.d/</cmd>."
|
||||
"diagnosis_using_stable_codename_details": "Esto suele deberse a una configuración incorrecta de su proveedor de alojamiento. Esto es peligroso, porque tan pronto como la siguiente versión de Debian se convierta en la nueva 'estable', <cmd>apt</cmd> querrá actualizar todos los paquetes del sistema sin pasar por un procedimiento de migración adecuado. Se recomienda arreglar esto editando la fuente de apt para el repositorio base de Debian, y reemplazar la palabra clave <cmd>stable</cmd> por <cmd>bullseye</cmd>. El fichero de configuración correspondiente debería ser <cmd>/etc/apt/sources.list</cmd>, o un fichero en <cmd>/etc/apt/sources.list.d/</cmd>.",
|
||||
"domain_config_cert_install": "Instalar el certificado Let's Encrypt",
|
||||
"domain_cannot_add_muc_upload": "No puedes añadir dominios que empiecen por 'muc.'. Este tipo de nombre está reservado para la función de chat XMPP multi-usuarios integrada en YunoHost.",
|
||||
"domain_config_cert_renew_help": "El certificado se renovará automáticamente durante los últimos 15 días de validez. Si lo desea, puede renovarlo manualmente. (No recomendado).",
|
||||
"domain_config_cert_summary_expired": "CRÍTICO: ¡El certificado actual no es válido! ¡HTTPS no funcionará en absoluto!",
|
||||
"domain_config_cert_summary_letsencrypt": "¡Muy bien! Estás utilizando un certificado Let's Encrypt válido.",
|
||||
"global_settings_setting_postfix_compatibility": "Compatibilidad con Postfix",
|
||||
"global_settings_setting_root_password_confirm": "Nueva contraseña de root (confirmar)",
|
||||
"global_settings_setting_webadmin_allowlist_enabled": "Activar la lista de IPs permitidas para Webadmin",
|
||||
"migration_0024_rebuild_python_venv_broken_app": "Omitiendo {app} porque virtualenv no puede ser reconstruido fácilmente para esta app. En su lugar, deberías arreglar la situación forzando la actualización de esta app usando `yunohost app upgrade --force {app}`.",
|
||||
"migration_0024_rebuild_python_venv_in_progress": "Ahora intentando reconstruir el virtualenv de Python para `{app}`",
|
||||
"confirm_app_insufficient_ram": "¡PELIGRO! Esta aplicación requiere {required} de RAM para ser instalada/actualizada, pero solo hay {current} disponible actualmente. Incluso si esta aplicación pudiera ejecutarse, su proceso de instalación/actualización requiere una gran cantidad de RAM, por lo que tu servidor puede congelarse y fallar miserablemente. Si estás dispuesto a asumir ese riesgo de todos modos, escribe '{answers}'",
|
||||
"confirm_notifications_read": "ADVERTENCIA: Deberías revisar las notificaciones de la aplicación antes de continuar, puede haber información importante que debes conocer. [{answers}]",
|
||||
"domain_config_cert_summary_selfsigned": "ADVERTENCIA: El certificado actual es autofirmado. ¡Los navegadores mostrarán una espeluznante advertencia a los nuevos visitantes!.",
|
||||
"global_settings_setting_backup_compress_tar_archives": "Comprimir las copias de seguridad",
|
||||
"global_settings_setting_root_access_explain": "En sistemas Linux, 'root' es el administrador absoluto. En el contexto de YunoHost, el acceso directo 'root' SSH está deshabilitado por defecto - excepto desde la red local del servidor. Los miembros del grupo 'admins' pueden usar el comando sudo para actuar como root desde la linea de comandos. Sin embargo, puede ser útil tener una contraseña de root (robusta) para depurar el sistema si por alguna razón los administradores regulares ya no pueden iniciar sesión.",
|
||||
"migration_0021_not_buster2": "¡La distribución Debian actual no es Buster! Si ya ha ejecutado la migración Buster->Bullseye, entonces este error es sintomático del hecho de que el procedimiento de migración no fue 100% exitoso (de lo contrario YunoHost lo habría marcado como completado). Se recomienda investigar lo sucedido con el equipo de soporte, que necesitará el registro **completo** de la `migración, que se puede encontrar en Herramientas > Registros en el webadmin.",
|
||||
"global_settings_reset_success": "Restablecer la configuración global",
|
||||
"global_settings_setting_nginx_compatibility": "Compatibilidad con NGINX",
|
||||
"global_settings_setting_nginx_redirect_to_https": "Forzar HTTPS",
|
||||
"global_settings_setting_user_strength_help": "Estos requisitos sólo se aplican al inicializar o cambiar la contraseña",
|
||||
"log_resource_snippet": "Aprovisionar/desaprovisionar/actualizar un recurso",
|
||||
"global_settings_setting_pop3_enabled": "Habilitar POP3",
|
||||
"global_settings_setting_smtp_allow_ipv6": "Permitir IPv6",
|
||||
"global_settings_setting_security_experimental_enabled": "Funciones de seguridad experimentales",
|
||||
"migration_0024_rebuild_python_venv_disclaimer_ignored": "Virtualenvs no puede reconstruirse automáticamente para esas aplicaciones. Necesitas forzar una actualización para ellas, lo que puede hacerse desde la línea de comandos con: `yunohost app upgrade --force APP`: {ignored_apps}",
|
||||
"migration_0024_rebuild_python_venv_failed": "Error al reconstruir el virtualenv de Python para {app}. La aplicación puede no funcionar mientras esto no se resuelva. Deberías arreglar la situación forzando la actualización de esta app usando `yunohost app upgrade --force {app}`.",
|
||||
"app_arch_not_supported": "Esta aplicación sólo puede instalarse en arquitecturas {', '.join(required)} pero la arquitectura de su servidor es {current}",
|
||||
"app_resource_failed": "Falló la asignación, desasignación o actualización de recursos para {app}: {error}",
|
||||
"app_not_enough_disk": "Esta aplicación requiere {required} espacio libre.",
|
||||
"app_not_enough_ram": "Esta aplicación requiere {required} de RAM para ser instalada/actualizada, pero solo hay {current} disponible actualmente.",
|
||||
"app_yunohost_version_not_supported": "Esta aplicación requiere YunoHost >= {required} pero la versión actualmente instalada es {current}",
|
||||
"global_settings_setting_ssh_compatibility": "Compatibilidad con SSH",
|
||||
"root_password_changed": "la contraseña de root fue cambiada",
|
||||
"domain_config_acme_eligible_explain": "Este dominio no parece estar preparado para un certificado Let's Encrypt. Compruebe la configuración DNS y la accesibilidad del servidor HTTP. Las secciones \"Registros DNS\" y \"Web\" de <a href='#/diagnosis'>la página de diagnóstico</a> pueden ayudarte a entender qué está mal configurado.",
|
||||
"domain_config_cert_no_checks": "Ignorar las comprobaciones de diagnóstico",
|
||||
"domain_config_cert_renew": "Renovar el certificado Let's Encrypt",
|
||||
"domain_config_cert_summary": "Estado del certificado",
|
||||
"domain_config_cert_summary_abouttoexpire": "El certificado actual está a punto de caducar. Pronto debería renovarse automáticamente.",
|
||||
"domain_config_cert_summary_ok": "Muy bien, ¡el certificado actual tiene buena pinta!",
|
||||
"domain_config_cert_validity": "Validez",
|
||||
"global_settings_setting_admin_strength_help": "Estos requisitos sólo se aplican al inicializar o cambiar la contraseña",
|
||||
"global_settings_setting_pop3_enabled_help": "Habilitar el protocolo POP3 para el servidor de correo",
|
||||
"log_settings_reset_all": "Restablecer todos los ajustes",
|
||||
"log_settings_set": "Aplicar ajustes",
|
||||
"pattern_fullname": "Debe ser un nombre completo válido (al menos 3 caracteres)",
|
||||
"password_confirmation_not_the_same": "La contraseña y su confirmación no coinciden",
|
||||
"password_too_long": "Elija una contraseña de menos de 127 caracteres",
|
||||
"diagnosis_using_yunohost_testing": "<cmd>apt</cmd> (el gestor de paquetes del sistema) está actualmente configurado para instalar cualquier actualización de 'testing' para el núcleo de YunoHost.",
|
||||
"diagnosis_using_yunohost_testing_details": "Esto probablemente esté bien si sabes lo que estás haciendo, ¡pero presta atención a las notas de la versión antes de instalar actualizaciones de YunoHost! Si quieres deshabilitar las actualizaciones de prueba, debes eliminar la palabra clave <cmd>testing</cmd> de <cmd>/etc/apt/sources.list.d/yunohost.list</cmd>.",
|
||||
"global_settings_setting_passwordless_sudo": "Permitir a los administradores utilizar 'sudo' sin tener que volver a escribir sus contraseñas.",
|
||||
"group_update_aliases": "Actualizando alias para el grupo '{group}'",
|
||||
"group_no_change": "Nada que cambiar para el grupo '{group}'",
|
||||
"global_settings_setting_portal_theme": "Tema del portal",
|
||||
"global_settings_setting_portal_theme_help": "Más información sobre la creación de temas de portal personalizados en https://yunohost.org/theming",
|
||||
"invalid_credentials": "Contraseña o nombre de usuario no válidos",
|
||||
"global_settings_setting_root_password": "Nueva contraseña de root",
|
||||
"global_settings_setting_webadmin_allowlist": "Lista de IPs permitidas para Webadmin",
|
||||
"migration_0024_rebuild_python_venv_disclaimer_base": "Tras la actualización a Debian Bullseye, algunas aplicaciones Python necesitan ser parcialmente reconstruidas para ser convertidas a la nueva versión de Python distribuida en Debian (en términos técnicos: lo que se llama el 'virtualenv' necesita ser recreado). Mientras tanto, esas aplicaciones Python pueden no funcionar. YunoHost puede intentar reconstruir el virtualenv para algunas de ellas, como se detalla a continuación. Para otras aplicaciones, o si el intento de reconstrucción falla, necesitarás forzar manualmente una actualización para esas aplicaciones.",
|
||||
"migration_description_0024_rebuild_python_venv": "Reparar la aplicación Python tras la migración a bullseye",
|
||||
"global_settings_setting_smtp_relay_enabled": "Activar el relé SMTP",
|
||||
"domain_config_acme_eligible": "Elegibilidad ACME",
|
||||
"global_settings_setting_ssh_password_authentication": "Autenticación por contraseña",
|
||||
"domain_config_cert_issuer": "Autoridad de certificación",
|
||||
"invalid_shell": "Shell inválido: {shell}",
|
||||
"log_settings_reset": "Restablecer ajuste",
|
||||
"migration_description_0026_new_admins_group": "Migrar al nuevo sistema de 'varios administradores'",
|
||||
"visitors": "Visitantes",
|
||||
"global_settings_setting_smtp_relay_host": "Host de retransmisión SMTP",
|
||||
"migration_0024_rebuild_python_venv_disclaimer_rebuild": "Se intentará reconstruir el virtualenv para las siguientes apps (NB: ¡la operación puede llevar algún tiempo!): {rebuild_apps}",
|
||||
"migration_description_0025_global_settings_to_configpanel": "Migración de la nomenclatura de ajustes globales heredada a la nomenclatura nueva y moderna",
|
||||
"registrar_infos": "Información sobre el registrador"
|
||||
}
|
|
@ -49,7 +49,6 @@
|
|||
"config_validate_email": "Benetazko posta elektronikoa izan behar da",
|
||||
"config_validate_time": "OO:MM formatua duen ordu bat izan behar da",
|
||||
"config_validate_url": "Benetazko URL bat izan behar da",
|
||||
"config_version_not_supported": "Ezinezkoa da konfigurazio-panelaren '{version}' bertsioa erabiltzea.",
|
||||
"app_restore_script_failed": "Errorea gertatu da aplikazioa lehengoratzeko aginduan",
|
||||
"app_upgrade_some_app_failed": "Ezinezkoa izan da aplikazio batzuk eguneratzea",
|
||||
"app_install_failed": "Ezinezkoa izan da {app} instalatzea: {error}",
|
||||
|
@ -736,5 +735,17 @@
|
|||
"password_too_long": "Aukeratu 127 karaktere baino laburragoa den pasahitz bat",
|
||||
"diagnosis_using_stable_codename_details": "Ostatatzaileak zerbait oker ezarri duenean gertatu ohi da hau. Arriskutsua da, Debianen datorren bertsioa 'estable' (egonkorra) bilakatzen denean, <cmd>apt</cmd>-ek sistemaren pakete guztiak bertsio-berritzen saiatuko da, beharrezko migrazio-prozedurarik burutu gabe. Debianen repositorioan apt iturria editatzen konpontzea da gomendioa, <cmd>stable</cmd> gakoa <cmd>bullseye</cmd> gakoarekin ordezkatuz. Ezarpen-fitxategia <cmd>/etc/apt/sources.list</cmd> izan beharko litzateke, edo <cmd>/etc/apt/sources.list.d/</cmd> direktorioko fitxategiren bat.",
|
||||
"group_update_aliases": "'{group}' taldearen aliasak eguneratzen",
|
||||
"group_no_change": "Ez da ezer aldatu behar '{group}' talderako"
|
||||
"group_no_change": "Ez da ezer aldatu behar '{group}' talderako",
|
||||
"app_not_enough_ram": "Aplikazio honek {required} RAM behar ditu instalatu edo bertsio-berritzeko, baina {current} bakarrik daude erabilgarri une honetan.",
|
||||
"domain_cannot_add_muc_upload": "Ezin duzu 'muc.'-ekin hasten den domeinurik gehitu. Mota honetako izenak YunoHosten integratuta dagoen XMPP taldeko txatek erabil ditzaten gordeta daude.",
|
||||
"confirm_app_insufficient_ram": "KONTUZ! Aplikazio honek {required} RAM behar ditu instalatu edo bertsio-berritzeko baina unean {current} bakarrik daude erabilgarri. Aplikazioa ibiliko balitz ere, instalazioak edo bertsio-berritzeak RAM koporu handia eskatzen du eta zure zerbitzaria izoztu eta huts egin lezake. Hala ere arriskatu nahi baduzu idatzi '{answers}'",
|
||||
"confirm_notifications_read": "ADI: ikuskatu aplikazioaren jakinarazpenak jarraitu baino lehen, baliteke jakin beharreko zerbait esatea. [{answers}]",
|
||||
"app_arch_not_supported": "Aplikazio hau {', '.join(required)} arkitekturan instala daiteke bakarrik, baina zure zerbitzariaren arkitektura {current} da",
|
||||
"app_resource_failed": "Huts egin du {app} aplikaziorako baliabideak",
|
||||
"app_not_enough_disk": "Aplikazio honek {required} espazio libre behar ditu.",
|
||||
"app_yunohost_version_not_supported": "Aplikazio honek YunoHost >= {required} behar du baina unean instalatutako bertsioa {current} da",
|
||||
"global_settings_setting_passwordless_sudo": "Baimendu administrariek 'sudo' erabiltzea pasahitzak berriro idatzi beharrik gabe",
|
||||
"global_settings_setting_portal_theme": "Atariko gaia",
|
||||
"global_settings_setting_portal_theme_help": "Atariko gai propioak sortzeari buruzko informazio gehiago: https://yunohost.org/theming",
|
||||
"invalid_shell": "Shell baliogabea: {shell}"
|
||||
}
|
|
@ -590,7 +590,6 @@
|
|||
"config_validate_email": "Doit être un email valide",
|
||||
"config_validate_time": "Doit être une heure valide comme HH:MM",
|
||||
"config_validate_url": "Doit être une URL Web valide",
|
||||
"config_version_not_supported": "Les versions du panneau de configuration '{version}' ne sont pas prises en charge.",
|
||||
"danger": "Danger :",
|
||||
"invalid_number_min": "Doit être supérieur à {min}",
|
||||
"invalid_number_max": "Doit être inférieur à {max}",
|
||||
|
@ -628,7 +627,7 @@
|
|||
"diagnosis_http_special_use_tld": "Le domaine {domain} est basé sur un domaine de premier niveau (TLD) à usage spécial tel que .local ou .test et n'est donc pas censé être exposé en dehors du réseau local.",
|
||||
"domain_dns_conf_special_use_tld": "Ce domaine est basé sur un domaine de premier niveau (TLD) à usage spécial tel que .local ou .test et ne devrait donc pas avoir d'enregistrements DNS réels.",
|
||||
"other_available_options": "... et {n} autres options disponibles non affichées",
|
||||
"domain_config_auth_consumer_key": "La clé utilisateur",
|
||||
"domain_config_auth_consumer_key": "Clé utilisateur",
|
||||
"domain_unknown": "Domaine '{domain}' inconnu",
|
||||
"migration_0021_start": "Démarrage de la migration vers Bullseye",
|
||||
"migration_0021_patching_sources_list": "Mise à jour du fichier sources.lists...",
|
||||
|
@ -656,7 +655,7 @@
|
|||
"global_settings_setting_backup_compress_tar_archives_help": "Lors de la création de nouvelles sauvegardes, compresser automatiquement les archives (.tar.gz) au lieu des archives non compressées (.tar). N.B. : activer cette option permet de créer des archives plus légères, mais la procédure de sauvegarde initiale sera significativement plus longues et plus gourmandes en CPU.",
|
||||
"global_settings_setting_security_experimental_enabled": "Fonctionnalités de sécurité expérimentales",
|
||||
"global_settings_setting_security_experimental_enabled_help": "Activer les fonctionnalités de sécurité expérimentales (ne l'activez pas si vous ne savez pas ce que vous faites !)",
|
||||
"global_settings_setting_nginx_compatibility_help": "Compromis 'compatibilité versus sécurité' pour le serveur web Nginx. Affecte les cryptogrammes utilisés (et d'autres aspects liés à la sécurité)",
|
||||
"global_settings_setting_nginx_compatibility_help": "Compromis 'compatibilité versus sécurité' pour le serveur web NGINX. Affecte les cryptogrammes utilisés (et d'autres aspects liés à la sécurité)",
|
||||
"global_settings_setting_nginx_redirect_to_https_help": "Rediriger les requêtes HTTP vers HTTPS par défaut (NE PAS DÉSACTIVER à moins de savoir vraiment ce que vous faites !)",
|
||||
"global_settings_setting_admin_strength": "Critères pour les mots de passe administrateur",
|
||||
"global_settings_setting_user_strength": "Critères pour les mots de passe utilisateurs",
|
||||
|
@ -709,7 +708,7 @@
|
|||
"visitors": "Visiteurs",
|
||||
"global_settings_reset_success": "Réinitialisation des paramètres généraux",
|
||||
"domain_config_acme_eligible": "Éligibilité au protocole ACME (Automatic Certificate Management Environment, littéralement : environnement de gestion automatique de certificat)",
|
||||
"domain_config_acme_eligible_explain": "Ce domaine ne semble pas près pour installer un certificat Let's Encrypt. Veuillez vérifier votre configuration DNS mais aussi que votre serveur est bien joignable en HTTP. Les sections 'Enregistrements DNS' et 'Web' de <a href='#/diagnosis'>la page Diagnostic</a> peuvent vous aider à comprendre ce qui est mal configuré.",
|
||||
"domain_config_acme_eligible_explain": "Ce domaine ne semble pas prêt pour installer un certificat Let's Encrypt. Veuillez vérifier votre configuration DNS mais aussi que votre serveur est bien joignable en HTTP. Les sections 'Enregistrements DNS' et 'Web' de <a href='#/diagnosis'>la page Diagnostic</a> peuvent vous aider à comprendre ce qui est mal configuré.",
|
||||
"domain_config_cert_install": "Installer un certificat Let's Encrypt",
|
||||
"domain_config_cert_issuer": "Autorité de certification",
|
||||
"domain_config_cert_no_checks": "Ignorer les tests et autres vérifications du diagnostic",
|
||||
|
@ -744,9 +743,13 @@
|
|||
"global_settings_setting_passwordless_sudo": "Permettre aux administrateurs d'utiliser 'sudo' sans retaper leur mot de passe",
|
||||
"app_arch_not_supported": "Cette application ne peut être installée que sur les architectures {', '.join(required)}. L'architecture de votre serveur est {current}",
|
||||
"app_resource_failed": "L'allocation automatique des ressources (provisioning), la suppression d'accès à ces ressources (déprovisioning) ou la mise à jour des ressources pour {app} a échoué : {error}",
|
||||
"confirm_app_insufficient_ram": "ATTENTION ! Cette application requiert {required} de RAM pour l'installation/mise à niveau mais il n'y a que {current} de disponible actuellement. Même si cette application pouvait fonctionner, son processus d'installation/mise à niveau nécessite une grande quantité de RAM. Votre serveur pourrait donc geler (freezer) et planter lamentablement. Si vous êtes prêt à prendre ce risque, tapez '{answers}'",
|
||||
"confirm_app_insufficient_ram": "ATTENTION ! Cette application requiert {required} de RAM pour l'installation/mise à niveau mais il n'y a que {current} de disponible actuellement. Même si cette application pouvait fonctionner, son processus d'installation/mise à niveau nécessite une grande quantité de RAM. Votre serveur pourrait donc geler et planter lamentablement. Si vous êtes prêt à prendre ce risque, tapez '{answers}'",
|
||||
"app_not_enough_disk": "Cette application nécessite {required} d'espace libre.",
|
||||
"app_not_enough_ram": "Cette application nécessite {required} de mémoire vive (RAM) pour être installée/mise à niveau mais seule {current} de mémoire est disponible actuellement.",
|
||||
"app_yunohost_version_not_supported": "Cette application nécessite une version de YunoHost >= {required}. La version installée est {current}",
|
||||
"confirm_notifications_read": "AVERTISSEMENT : Vous devriez vérifier les notifications de l'application susmentionnée avant de continuer, il pourrait y avoir des éléments d'information importants à connaître. [{answers}]"
|
||||
}
|
||||
"confirm_notifications_read": "AVERTISSEMENT : Vous devriez vérifier les notifications de l'application susmentionnée avant de continuer, il pourrait y avoir des informations importantes à connaître. [{answers}]",
|
||||
"invalid_shell": "Shell invalide : {shell}",
|
||||
"global_settings_setting_dns_exposure": "Suites d'IP à prendre en compte pour la configuration et le diagnostic du DNS",
|
||||
"global_settings_setting_dns_exposure_help": "NB : Ceci n'affecte que la configuration DNS recommandée et les vérifications de diagnostic. Cela n'affecte pas les configurations du système.",
|
||||
"diagnosis_ip_no_ipv6_tip_important": "IPv6 devrait généralement être configuré automatiquement par le système ou par votre fournisseur d'accès à internet (FAI) s'il est disponible. Sinon, vous devrez peut-être configurer quelques éléments manuellement, comme expliqué dans la documentation ici : <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>."
|
||||
}
|
||||
|
|
|
@ -590,7 +590,6 @@
|
|||
"log_app_config_set": "Aplicar a configuración á app '{}'",
|
||||
"app_config_unable_to_apply": "Fallou a aplicación dos valores de configuración.",
|
||||
"config_cant_set_value_on_section": "Non podes establecer un valor único na sección completa de configuración.",
|
||||
"config_version_not_supported": "A versión do panel de configuración '{version}' non está soportada.",
|
||||
"invalid_number_max": "Ten que ser menor de {max}",
|
||||
"service_not_reloading_because_conf_broken": "Non se recargou/reiniciou o servizo '{name}' porque a súa configuración está estragada: {errors}",
|
||||
"diagnosis_http_special_use_tld": "O dominio {domain} baséase nun dominio de alto-nivel (TLD) especial como .local ou .test e por isto non é de agardar que esté exposto fóra da rede local.",
|
||||
|
@ -740,5 +739,14 @@
|
|||
"domain_cannot_add_muc_upload": "Non podes engadir dominios que comecen por 'muc.'. Este tipo de dominio está reservado para as salas de conversa de XMPP integradas en YunoHost.",
|
||||
"global_settings_setting_passwordless_sudo": "Permitir a Admins usar 'sudo' sen ter que volver a escribir o contrasinal",
|
||||
"global_settings_setting_portal_theme": "Decorado do Portal",
|
||||
"global_settings_setting_portal_theme_help": "Tes máis info acerca da creación de decorados para o portal de acceso en https://yunohost.org/theming"
|
||||
}
|
||||
"global_settings_setting_portal_theme_help": "Tes máis info acerca da creación de decorados para o portal de acceso en https://yunohost.org/theming",
|
||||
"app_arch_not_supported": "Esta app só pode ser instalada e arquitecturas {', '.join(required)} pero a arquitectura do teu servidor é {current}",
|
||||
"app_not_enough_disk": "Esta app precisa {required} de espazo libre.",
|
||||
"app_yunohost_version_not_supported": "Esta app require YunoHost >= {required} pero a versión actual instalada é {current}",
|
||||
"confirm_app_insufficient_ram": "PERIGO! Esta app precisa {required} de RAM para instalar/actualizar pero só hai {current} dispoñibles. Incluso se a app funcionase, o seu proceso de instalación/actualización require gran cantidade de RAM e o teu servidor podería colgarse e fallar. Se queres asumir o risco, escribe '{answers}'",
|
||||
"confirm_notifications_read": "AVISO: Deberías comprobar as notificacións da app antes de continuar, poderías ter información importante que revisar. [{answers}]",
|
||||
"app_not_enough_ram": "Esta app require {required} de RAM para instalar/actualizar pero só hai {current} dispoñible.",
|
||||
"global_settings_setting_dns_exposure": "Versións de IP a ter en conta para a configuración DNS e diagnóstico",
|
||||
"global_settings_setting_dns_exposure_help": "Nota: Esto só lle afecta á configuración DNS recomendada e diagnóstico do sistema. Non lle afecta aos axustes do sistema.",
|
||||
"diagnosis_ip_no_ipv6_tip_important": "Se está dispoñible, IPv6 debería estar automáticamente configurado polo sistema ou o teu provedor. Se non, pode que teñas que facer algúns axustes manualmente tal como se explica na documentación: <a href='https://yunohost.org/#/ipv6'>https://yunohost.org/#/ipv6</a>."
|
||||
}
|
||||
|
|
|
@ -618,7 +618,6 @@
|
|||
"domain_config_auth_consumer_key": "Chiave consumatore",
|
||||
"ldap_attribute_already_exists": "L’attributo LDAP '{attribute}' esiste già con il valore '{value}'",
|
||||
"config_validate_time": "È necessario inserire un orario valido, come HH:MM",
|
||||
"config_version_not_supported": "Le versioni '{version}' del pannello di configurazione non sono supportate.",
|
||||
"danger": "Attenzione:",
|
||||
"log_domain_config_set": "Aggiorna la configurazione per il dominio '{}'",
|
||||
"domain_dns_push_managed_in_parent_domain": "La configurazione automatica del DNS è gestita nel dominio genitore {parent_domain}.",
|
||||
|
|
1
locales/lt.json
Normal file
1
locales/lt.json
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -204,7 +204,6 @@
|
|||
"config_cant_set_value_on_section": "Você não pode setar um único valor na seção de configuração inteira.",
|
||||
"config_validate_time": "Deve ser um horário válido como HH:MM",
|
||||
"config_validate_url": "Deve ser uma URL válida",
|
||||
"config_version_not_supported": "Versões do painel de configuração '{version}' não são suportadas.",
|
||||
"danger": "Perigo:",
|
||||
"diagnosis_basesystem_ynh_inconsistent_versions": "Você está executando versões inconsistentes dos pacotes YunoHost... provavelmente por causa de uma atualização parcial ou que falhou.",
|
||||
"diagnosis_description_basesystem": "Sistema base",
|
||||
|
|
|
@ -106,7 +106,6 @@
|
|||
"certmanager_domain_dns_ip_differs_from_public_ip": "DNS-записи для домена '{domain}' отличаются от IP этого сервера. Пожалуйста, проверьте категорию 'DNS-записи' (основные) в диагностике для получения дополнительной информации. Если вы недавно изменили свою A-запись, пожалуйста, подождите, пока она распространится (некоторые программы проверки распространения DNS доступны в интернете). (Если вы знаете, что делаете, используйте '--no-checks', чтобы отключить эти проверки.)",
|
||||
"certmanager_domain_not_diagnosed_yet": "Для домена {domain} еще нет результатов диагностики. Пожалуйста, перезапустите диагностику для категорий 'DNS-записи' и 'Домены', чтобы проверить, готов ли домен к Let's Encrypt. (Или, если вы знаете, что делаете, используйте '--no-checks', чтобы отключить эти проверки.)",
|
||||
"config_validate_url": "Должна быть правильная ссылка",
|
||||
"config_version_not_supported": "Версии конфигурационной панели '{version}' не поддерживаются.",
|
||||
"confirm_app_install_danger": "ОПАСНО! Это приложение все еще является экспериментальным (если не сказать, что оно явно не работает)! Вам НЕ следует устанавливать его, если вы НЕ знаете, что делаете. Если это приложение не будет работать или сломает вашу систему, мы НЕ будем оказывать техническую поддержку... Если вы все равно готовы рискнуть, введите '{answers}'",
|
||||
"confirm_app_install_thirdparty": "ВАЖНО! Это приложение не входит в каталог приложений YunoHost. Установка сторонних приложений может нарушить целостность и безопасность вашей системы. Вам НЕ следует устанавливать его, если вы НЕ знаете, что делаете. Если это приложение не будет работать или сломает вашу систему, мы НЕ будем оказывать техническую поддержку... Если вы все равно готовы рискнуть, введите '{answers}'",
|
||||
"config_apply_failed": "Не удалось применить новую конфигурацию: {error}",
|
||||
|
|
|
@ -144,7 +144,6 @@
|
|||
"config_validate_email": "Toto by mal byť platný e-mail",
|
||||
"config_validate_time": "Toto by mal byť platný čas vo formáte HH:MM",
|
||||
"config_validate_url": "Toto by mala byť platná URL adresa webu",
|
||||
"config_version_not_supported": "Verzie konfiguračného panela '{version}' nie sú podporované.",
|
||||
"danger": "Nebezpečenstvo:",
|
||||
"confirm_app_install_danger": "NEBEZPEČENSTVO! Táto aplikácia je experimentálna (ak vôbec funguje)! Pravdepodobne by ste ju NEMALI inštalovať, pokiaľ si nie ste istý, čo robíte. NEPOSKYTNEME VÁM ŽIADNU POMOC, ak táto aplikácia nebude fungovať alebo rozbije Váš systém… Ak sa rozhodnete i napriek tomu podstúpiť toto riziko, zadajte '{answers}'",
|
||||
"confirm_app_install_thirdparty": "NEBEZPEČENSTVO! Táto aplikácia nie je súčasťou katalógu aplikácií YunoHost. Inštalovaním aplikácií tretích strán môžete ohroziť integritu a bezpečnosť Vášho systému. Pravdepodobne by ste NEMALI pokračovať v inštalácií, pokiaľ neviete, čo robíte. NEPOSKYTNEME VÁM ŽIADNU POMOC, ak táto aplikácia nebude fungovať alebo rozbije Váš systém… Ak sa rozhodnete i napriek tomu podstúpiť toto riziko, zadajte '{answers}'",
|
||||
|
|
|
@ -5,5 +5,15 @@
|
|||
"already_up_to_date": "Yapılacak yeni bir şey yok. Her şey zaten güncel.",
|
||||
"app_action_broke_system": "Bu işlem bazı hizmetleri bozmuş olabilir: {services}",
|
||||
"good_practices_about_user_password": "Şimdi yeni bir kullanıcı şifresi tanımlamak üzeresiniz. Parola en az 8 karakter uzunluğunda olmalıdır - ancak daha uzun bir parola (yani bir parola) ve/veya çeşitli karakterler (büyük harf, küçük harf, rakamlar ve özel karakterler) daha iyidir.",
|
||||
"aborting": "İptal ediliyor."
|
||||
}
|
||||
"aborting": "İptal ediliyor.",
|
||||
"app_action_failed": "{app} uygulaması için {action} eylemini çalıştırma başarısız",
|
||||
"admins": "Yöneticiler",
|
||||
"all_users": "Tüm YunoHost kullanıcıları",
|
||||
"app_already_up_to_date": "{app} zaten güncel",
|
||||
"app_already_installed": "{app} zaten kurulu",
|
||||
"app_already_installed_cant_change_url": "Bu uygulama zaten kurulu. URL yalnızca bu işlev kullanarak değiştirilemez. Eğer varsa `app changeurl`'i kontrol edin.",
|
||||
"additional_urls_already_added": "Ek URL '{url}' zaten '{permission}' izni için ek URL'ye eklendi",
|
||||
"additional_urls_already_removed": "Ek URL '{url}', '{permission}' izni için ek URL'de zaten kaldırıldı",
|
||||
"app_action_cannot_be_ran_because_required_services_down": "Bu eylemi gerçekleştirmek için şu servisler çalışıyor olmalıdır: {services}. Devam etmek için onları yeniden başlatın (ve muhtemelen neden çalışmadığını araştırın).",
|
||||
"app_arch_not_supported": "Bu uygulama yalnızca {', '.join(required)} işlemci mimarisi üzerine kurulabilir ancak sunucunuzun işlemci mimarisi {current}."
|
||||
}
|
||||
|
|
|
@ -587,7 +587,6 @@
|
|||
"config_validate_email": "Е-пошта має бути дійсною",
|
||||
"config_validate_time": "Час має бути дійсним, наприклад ГГ:ХХ",
|
||||
"config_validate_url": "Вебадреса має бути дійсною",
|
||||
"config_version_not_supported": "Версії конфігураційної панелі '{version}' не підтримуються.",
|
||||
"danger": "Небезпека:",
|
||||
"invalid_number_min": "Має бути більшим за {min}",
|
||||
"invalid_number_max": "Має бути меншим за {max}",
|
||||
|
|
|
@ -571,7 +571,6 @@
|
|||
"config_validate_email": "是有效的电子邮件",
|
||||
"config_validate_time": "应该是像 HH:MM 这样的有效时间",
|
||||
"config_validate_url": "应该是有效的URL",
|
||||
"config_version_not_supported": "不支持配置面板版本“{ version }”。",
|
||||
"danger": "警告:",
|
||||
"diagnosis_apps_allgood": "所有已安装的应用程序都遵守基本的打包原则",
|
||||
"diagnosis_apps_deprecated_practices": "此应用程序的安装 版本仍然使用一些超旧的弃用打包原则。推荐您升级它。",
|
||||
|
|
|
@ -32,7 +32,6 @@ def autofix_i18n_placeholders():
|
|||
|
||||
# We iterate over all keys/string in en.json
|
||||
for key, string in reference.items():
|
||||
|
||||
# Ignore check if there's no translation yet for this key
|
||||
if key not in this_locale:
|
||||
continue
|
||||
|
@ -89,7 +88,6 @@ Please fix it manually !
|
|||
|
||||
def autofix_orthotypography_and_standardized_words():
|
||||
def reformat(lang, transformations):
|
||||
|
||||
locale = open(f"{LOCALE_FOLDER}{lang}.json").read()
|
||||
for pattern, replace in transformations.items():
|
||||
locale = re.compile(pattern).sub(replace, locale)
|
||||
|
@ -146,11 +144,9 @@ def autofix_orthotypography_and_standardized_words():
|
|||
|
||||
|
||||
def remove_stale_translated_strings():
|
||||
|
||||
reference = json.loads(open(LOCALE_FOLDER + "en.json").read())
|
||||
|
||||
for locale_file in TRANSLATION_FILES:
|
||||
|
||||
print(locale_file)
|
||||
this_locale = json.loads(
|
||||
open(LOCALE_FOLDER + locale_file).read(), object_pairs_hook=OrderedDict
|
||||
|
|
|
@ -19,7 +19,6 @@ REFERENCE_FILE = LOCALE_FOLDER + "en.json"
|
|||
|
||||
|
||||
def find_expected_string_keys():
|
||||
|
||||
# Try to find :
|
||||
# m18n.n( "foo"
|
||||
# YunohostError("foo"
|
||||
|
@ -197,7 +196,6 @@ undefined_keys = sorted(undefined_keys)
|
|||
|
||||
mode = sys.argv[1].strip("-")
|
||||
if mode == "check":
|
||||
|
||||
# Unused keys are not too problematic, will be automatically
|
||||
# removed by the other autoreformat script,
|
||||
# but still informative to display them
|
||||
|
|
|
@ -116,6 +116,11 @@ user:
|
|||
pattern: &pattern_mailbox_quota
|
||||
- !!str ^(\d+[bkMGT])|0$
|
||||
- "pattern_mailbox_quota"
|
||||
-s:
|
||||
full: --loginShell
|
||||
help: The login shell used
|
||||
default: "/bin/bash"
|
||||
|
||||
|
||||
### user_delete()
|
||||
delete:
|
||||
|
@ -195,6 +200,10 @@ user:
|
|||
metavar: "{SIZE|0}"
|
||||
extra:
|
||||
pattern: *pattern_mailbox_quota
|
||||
-s:
|
||||
full: --loginShell
|
||||
help: The login shell used
|
||||
default: "/bin/bash"
|
||||
|
||||
### user_info()
|
||||
info:
|
||||
|
|
|
@ -64,24 +64,24 @@ name = "Certificate"
|
|||
[cert.cert.acme_eligible_explain]
|
||||
type = "alert"
|
||||
style = "warning"
|
||||
visible = "acme_eligible == false || acme_elligible == null"
|
||||
visible = "acme_eligible == false || acme_eligible == null"
|
||||
|
||||
[cert.cert.cert_no_checks]
|
||||
ask = "Ignore diagnosis checks"
|
||||
type = "boolean"
|
||||
default = false
|
||||
visible = "acme_eligible == false || acme_elligible == null"
|
||||
visible = "acme_eligible == false || acme_eligible == null"
|
||||
|
||||
[cert.cert.cert_install]
|
||||
type = "button"
|
||||
icon = "star"
|
||||
style = "success"
|
||||
visible = "issuer != 'letsencrypt'"
|
||||
visible = "cert_issuer != 'letsencrypt'"
|
||||
enabled = "acme_eligible || cert_no_checks"
|
||||
|
||||
[cert.cert.cert_renew]
|
||||
type = "button"
|
||||
icon = "refresh"
|
||||
style = "warning"
|
||||
visible = "issuer == 'letsencrypt'"
|
||||
visible = "cert_issuer == 'letsencrypt'"
|
||||
enabled = "acme_eligible || cert_no_checks"
|
||||
|
|
|
@ -160,3 +160,12 @@ name = "Other"
|
|||
[misc.backup.backup_compress_tar_archives]
|
||||
type = "boolean"
|
||||
default = false
|
||||
|
||||
[misc.network]
|
||||
name = "Network"
|
||||
[misc.network.dns_exposure]
|
||||
type = "select"
|
||||
choices.both = "Both"
|
||||
choices.ipv4 = "IPv4 Only"
|
||||
choices.ipv6 = "IPv6 Only"
|
||||
default = "both"
|
||||
|
|
|
@ -32,7 +32,6 @@ def is_installed():
|
|||
|
||||
|
||||
def cli(debug, quiet, output_as, timeout, args, parser):
|
||||
|
||||
init_logging(interface="cli", debug=debug, quiet=quiet)
|
||||
|
||||
# Check that YunoHost is installed
|
||||
|
@ -51,7 +50,6 @@ def cli(debug, quiet, output_as, timeout, args, parser):
|
|||
|
||||
|
||||
def api(debug, host, port):
|
||||
|
||||
init_logging(interface="api", debug=debug)
|
||||
|
||||
def is_installed_api():
|
||||
|
@ -71,7 +69,6 @@ def api(debug, host, port):
|
|||
|
||||
|
||||
def check_command_is_valid_before_postinstall(args):
|
||||
|
||||
allowed_if_not_postinstalled = [
|
||||
"tools postinstall",
|
||||
"tools versions",
|
||||
|
@ -109,7 +106,6 @@ def init_i18n():
|
|||
|
||||
|
||||
def init_logging(interface="cli", debug=False, quiet=False, logdir="/var/log/yunohost"):
|
||||
|
||||
logfile = os.path.join(logdir, "yunohost-%s.log" % interface)
|
||||
|
||||
if not os.path.isdir(logdir):
|
||||
|
|
36
src/app.py
36
src/app.py
|
@ -238,7 +238,6 @@ def app_info(app, full=False, upgradable=False):
|
|||
|
||||
|
||||
def _app_upgradable(app_infos):
|
||||
|
||||
# Determine upgradability
|
||||
|
||||
app_in_catalog = app_infos.get("from_catalog")
|
||||
|
@ -374,7 +373,6 @@ def app_map(app=None, raw=False, user=None):
|
|||
)
|
||||
|
||||
for url in perm_all_urls:
|
||||
|
||||
# Here, we decide to completely ignore regex-type urls ...
|
||||
# Because :
|
||||
# - displaying them in regular "yunohost app map" output creates
|
||||
|
@ -716,7 +714,6 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
|
|||
),
|
||||
)
|
||||
finally:
|
||||
|
||||
# If upgrade failed, try to restore the safety backup
|
||||
if (
|
||||
upgrade_failed
|
||||
|
@ -762,7 +759,6 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
|
|||
# If upgrade failed or broke the system,
|
||||
# raise an error and interrupt all other pending upgrades
|
||||
if upgrade_failed or broke_the_system:
|
||||
|
||||
# display this if there are remaining apps
|
||||
if apps[number + 1 :]:
|
||||
not_upgraded_apps = apps[number:]
|
||||
|
@ -843,7 +839,6 @@ def app_upgrade(app=[], url=None, file=None, force=False, no_safety_backup=False
|
|||
|
||||
|
||||
def app_manifest(app, with_screenshot=False):
|
||||
|
||||
manifest, extracted_app_folder = _extract_app(app)
|
||||
|
||||
raw_questions = manifest.get("install", {}).values()
|
||||
|
@ -886,7 +881,6 @@ def app_manifest(app, with_screenshot=False):
|
|||
|
||||
|
||||
def _confirm_app_install(app, force=False):
|
||||
|
||||
# Ignore if there's nothing for confirm (good quality app), if --force is used
|
||||
# or if request on the API (confirm already implemented on the API side)
|
||||
if force or Moulinette.interface.type == "api":
|
||||
|
@ -1036,7 +1030,6 @@ def app_install(
|
|||
# If packaging_format v2+, save all install questions as settings
|
||||
if packaging_format >= 2:
|
||||
for question in questions:
|
||||
|
||||
# Except user-provider passwords
|
||||
if question.type == "password":
|
||||
continue
|
||||
|
@ -1135,7 +1128,6 @@ def app_install(
|
|||
|
||||
# If the install failed or broke the system, we remove it
|
||||
if install_failed or broke_the_system:
|
||||
|
||||
# This option is meant for packagers to debug their apps more easily
|
||||
if no_remove_on_failure:
|
||||
raise YunohostError(
|
||||
|
@ -1390,7 +1382,6 @@ def app_setting(app, key, value=None, delete=False):
|
|||
)
|
||||
|
||||
if is_legacy_permission_setting:
|
||||
|
||||
from yunohost.permission import (
|
||||
user_permission_list,
|
||||
user_permission_update,
|
||||
|
@ -1433,7 +1424,6 @@ def app_setting(app, key, value=None, delete=False):
|
|||
|
||||
# SET
|
||||
else:
|
||||
|
||||
urls = value
|
||||
# If the request is about the root of the app (/), ( = the vast majority of cases)
|
||||
# we interpret this as a change for the main permission
|
||||
|
@ -1445,7 +1435,6 @@ def app_setting(app, key, value=None, delete=False):
|
|||
else:
|
||||
user_permission_update(app + ".main", remove="visitors")
|
||||
else:
|
||||
|
||||
urls = urls.split(",")
|
||||
if key.endswith("_regex"):
|
||||
urls = ["re:" + url for url in urls]
|
||||
|
@ -1604,7 +1593,6 @@ def app_ssowatconf():
|
|||
)
|
||||
|
||||
for app in _installed_apps():
|
||||
|
||||
app_settings = read_yaml(APPS_SETTING_PATH + app + "/settings.yml") or {}
|
||||
|
||||
# Redirected
|
||||
|
@ -1630,7 +1618,6 @@ def app_ssowatconf():
|
|||
|
||||
# New permission system
|
||||
for perm_name, perm_info in all_permissions.items():
|
||||
|
||||
uris = (
|
||||
[]
|
||||
+ ([perm_info["url"]] if perm_info["url"] else [])
|
||||
|
@ -1694,13 +1681,11 @@ def app_change_label(app, new_label):
|
|||
|
||||
|
||||
def app_action_list(app):
|
||||
|
||||
return AppConfigPanel(app).list_actions()
|
||||
|
||||
|
||||
@is_unit_operation()
|
||||
def app_action_run(operation_logger, app, action, args=None, args_file=None):
|
||||
|
||||
return AppConfigPanel(app).run_action(
|
||||
action, args=args, args_file=args_file, operation_logger=operation_logger
|
||||
)
|
||||
|
@ -2036,12 +2021,10 @@ def _get_manifest_of_app(path):
|
|||
|
||||
|
||||
def _parse_app_doc_and_notifications(path):
|
||||
|
||||
doc = {}
|
||||
notification_names = ["PRE_INSTALL", "POST_INSTALL", "PRE_UPGRADE", "POST_UPGRADE"]
|
||||
|
||||
for filepath in glob.glob(os.path.join(path, "doc") + "/*.md"):
|
||||
|
||||
# to be improved : [a-z]{2,3} is a clumsy way of parsing the
|
||||
# lang code ... some lang code are more complex that this é_è
|
||||
m = re.match("([A-Z]*)(_[a-z]{2,3})?.md", filepath.split("/")[-1])
|
||||
|
@ -2091,11 +2074,9 @@ def _parse_app_doc_and_notifications(path):
|
|||
|
||||
|
||||
def _hydrate_app_template(template, data):
|
||||
|
||||
stuff_to_replace = set(re.findall(r"__[A-Z0-9]+?[A-Z0-9_]*?[A-Z0-9]*?__", template))
|
||||
|
||||
for stuff in stuff_to_replace:
|
||||
|
||||
varname = stuff.strip("_").lower()
|
||||
|
||||
if varname in data:
|
||||
|
@ -2105,7 +2086,6 @@ def _hydrate_app_template(template, data):
|
|||
|
||||
|
||||
def _convert_v1_manifest_to_v2(manifest):
|
||||
|
||||
manifest = copy.deepcopy(manifest)
|
||||
|
||||
if "upstream" not in manifest:
|
||||
|
@ -2186,7 +2166,6 @@ def _convert_v1_manifest_to_v2(manifest):
|
|||
|
||||
|
||||
def _set_default_ask_questions(questions, script_name="install"):
|
||||
|
||||
# arguments is something like
|
||||
# { "domain":
|
||||
# {
|
||||
|
@ -2244,7 +2223,6 @@ def _set_default_ask_questions(questions, script_name="install"):
|
|||
|
||||
|
||||
def _is_app_repo_url(string: str) -> bool:
|
||||
|
||||
string = string.strip()
|
||||
|
||||
# Dummy test for ssh-based stuff ... should probably be improved somehow
|
||||
|
@ -2261,7 +2239,6 @@ def _app_quality(src: str) -> str:
|
|||
|
||||
raw_app_catalog = _load_apps_catalog()["apps"]
|
||||
if src in raw_app_catalog or _is_app_repo_url(src):
|
||||
|
||||
# If we got an app name directly (e.g. just "wordpress"), we gonna test this name
|
||||
if src in raw_app_catalog:
|
||||
app_name_to_test = src
|
||||
|
@ -2274,7 +2251,6 @@ def _app_quality(src: str) -> str:
|
|||
return "thirdparty"
|
||||
|
||||
if app_name_to_test in raw_app_catalog:
|
||||
|
||||
state = raw_app_catalog[app_name_to_test].get("state", "notworking")
|
||||
level = raw_app_catalog[app_name_to_test].get("level", None)
|
||||
if state in ["working", "validated"]:
|
||||
|
@ -2385,7 +2361,6 @@ def _extract_app_from_folder(path: str) -> Tuple[Dict, str]:
|
|||
def _extract_app_from_gitrepo(
|
||||
url: str, branch: Optional[str] = None, revision: str = "HEAD", app_info: Dict = {}
|
||||
) -> Tuple[Dict, str]:
|
||||
|
||||
logger.debug("Checking default branch")
|
||||
|
||||
try:
|
||||
|
@ -2635,7 +2610,6 @@ def _check_manifest_requirements(
|
|||
|
||||
|
||||
def _guess_webapp_path_requirement(app_folder: str) -> str:
|
||||
|
||||
# If there's only one "domain" and "path", validate that domain/path
|
||||
# is an available url and normalize the path.
|
||||
|
||||
|
@ -2681,7 +2655,6 @@ def _guess_webapp_path_requirement(app_folder: str) -> str:
|
|||
def _validate_webpath_requirement(
|
||||
args: Dict[str, Any], path_requirement: str, ignore_app=None
|
||||
) -> None:
|
||||
|
||||
domain = args.get("domain")
|
||||
path = args.get("path")
|
||||
|
||||
|
@ -2729,7 +2702,6 @@ def _get_conflicting_apps(domain, path, ignore_app=None):
|
|||
|
||||
|
||||
def _assert_no_conflicting_apps(domain, path, ignore_app=None, full_domain=False):
|
||||
|
||||
conflicts = _get_conflicting_apps(domain, path, ignore_app)
|
||||
|
||||
if conflicts:
|
||||
|
@ -2748,7 +2720,6 @@ def _assert_no_conflicting_apps(domain, path, ignore_app=None, full_domain=False
|
|||
def _make_environment_for_app_script(
|
||||
app, args={}, args_prefix="APP_ARG_", workdir=None, action=None
|
||||
):
|
||||
|
||||
app_setting_path = os.path.join(APPS_SETTING_PATH, app)
|
||||
|
||||
manifest = _get_manifest_of_app(app_setting_path)
|
||||
|
@ -2777,7 +2748,6 @@ def _make_environment_for_app_script(
|
|||
if manifest["packaging_format"] >= 2:
|
||||
env_dict["app"] = app
|
||||
for setting_name, setting_value in _get_app_settings(app).items():
|
||||
|
||||
# Ignore special internal settings like checksum__
|
||||
# (not a huge deal to load them but idk...)
|
||||
if setting_name.startswith("checksum__"):
|
||||
|
@ -2822,7 +2792,6 @@ def _parse_app_instance_name(app_instance_name: str) -> Tuple[str, int]:
|
|||
|
||||
|
||||
def _next_instance_number_for_app(app):
|
||||
|
||||
# Get list of sibling apps, such as {app}, {app}__2, {app}__4
|
||||
apps = _installed_apps()
|
||||
sibling_app_ids = [a for a in apps if a == app or a.startswith(f"{app}__")]
|
||||
|
@ -2840,7 +2809,6 @@ def _next_instance_number_for_app(app):
|
|||
|
||||
|
||||
def _make_tmp_workdir_for_app(app=None):
|
||||
|
||||
# Create parent dir if it doesn't exists yet
|
||||
if not os.path.exists(APP_TMP_WORKDIRS):
|
||||
os.makedirs(APP_TMP_WORKDIRS)
|
||||
|
@ -2870,12 +2838,10 @@ def _make_tmp_workdir_for_app(app=None):
|
|||
|
||||
|
||||
def unstable_apps():
|
||||
|
||||
output = []
|
||||
deprecated_apps = ["mailman", "ffsync"]
|
||||
|
||||
for infos in app_list(full=True)["apps"]:
|
||||
|
||||
if (
|
||||
not infos.get("from_catalog")
|
||||
or infos.get("from_catalog").get("state")
|
||||
|
@ -2891,7 +2857,6 @@ def unstable_apps():
|
|||
|
||||
|
||||
def _assert_system_is_sane_for_app(manifest, when):
|
||||
|
||||
from yunohost.service import service_status
|
||||
|
||||
logger.debug("Checking that required services are up and running...")
|
||||
|
@ -2954,7 +2919,6 @@ def _assert_system_is_sane_for_app(manifest, when):
|
|||
|
||||
|
||||
def app_dismiss_notification(app, name):
|
||||
|
||||
assert isinstance(name, str)
|
||||
name = name.lower()
|
||||
assert name in ["post_install", "post_upgrade"]
|
||||
|
|
|
@ -157,7 +157,6 @@ def _read_apps_catalog_list():
|
|||
|
||||
|
||||
def _actual_apps_catalog_api_url(base_url):
|
||||
|
||||
return f"{base_url}/v{APPS_CATALOG_API_VERSION}/apps.json"
|
||||
|
||||
|
||||
|
@ -269,7 +268,6 @@ def _load_apps_catalog():
|
|||
merged_catalog = {"apps": {}, "categories": [], "antifeatures": []}
|
||||
|
||||
for apps_catalog_id in [L["id"] for L in _read_apps_catalog_list()]:
|
||||
|
||||
# Let's load the json from cache for this catalog
|
||||
cache_file = f"{APPS_CATALOG_CACHE}/{apps_catalog_id}.json"
|
||||
|
||||
|
@ -298,7 +296,6 @@ def _load_apps_catalog():
|
|||
|
||||
# Add apps from this catalog to the output
|
||||
for app, info in apps_catalog_content["apps"].items():
|
||||
|
||||
# (N.B. : there's a small edge case where multiple apps catalog could be listing the same apps ...
|
||||
# in which case we keep only the first one found)
|
||||
if app in merged_catalog["apps"]:
|
||||
|
|
|
@ -38,14 +38,12 @@ AUTH_DN = "uid={uid},ou=users,dc=yunohost,dc=org"
|
|||
|
||||
|
||||
class Authenticator(BaseAuthenticator):
|
||||
|
||||
name = "ldap_admin"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def _authenticate_credentials(self, credentials=None):
|
||||
|
||||
try:
|
||||
admins = (
|
||||
_get_ldap_interface()
|
||||
|
@ -125,7 +123,6 @@ class Authenticator(BaseAuthenticator):
|
|||
con.unbind_s()
|
||||
|
||||
def set_session_cookie(self, infos):
|
||||
|
||||
from bottle import response
|
||||
|
||||
assert isinstance(infos, dict)
|
||||
|
@ -145,7 +142,6 @@ class Authenticator(BaseAuthenticator):
|
|||
)
|
||||
|
||||
def get_session_cookie(self, raise_if_no_session_exists=True):
|
||||
|
||||
from bottle import request
|
||||
|
||||
try:
|
||||
|
@ -174,7 +170,6 @@ class Authenticator(BaseAuthenticator):
|
|||
return infos
|
||||
|
||||
def delete_session_cookie(self):
|
||||
|
||||
from bottle import response
|
||||
|
||||
response.set_cookie("yunohost.admin", "", max_age=-1)
|
||||
|
|
|
@ -93,7 +93,6 @@ class BackupRestoreTargetsManager:
|
|||
"""
|
||||
|
||||
def __init__(self):
|
||||
|
||||
self.targets = {}
|
||||
self.results = {"system": {}, "apps": {}}
|
||||
|
||||
|
@ -349,7 +348,6 @@ class BackupManager:
|
|||
if not os.path.isdir(self.work_dir):
|
||||
mkdir(self.work_dir, 0o750, parents=True)
|
||||
elif self.is_tmp_work_dir:
|
||||
|
||||
logger.debug(
|
||||
"temporary directory for backup '%s' already exists... attempting to clean it",
|
||||
self.work_dir,
|
||||
|
@ -887,7 +885,6 @@ class RestoreManager:
|
|||
|
||||
@property
|
||||
def success(self):
|
||||
|
||||
successful_apps = self.targets.list("apps", include=["Success", "Warning"])
|
||||
successful_system = self.targets.list("system", include=["Success", "Warning"])
|
||||
|
||||
|
@ -1443,7 +1440,6 @@ class RestoreManager:
|
|||
existing_groups = user_group_list()["groups"]
|
||||
|
||||
for permission_name, permission_infos in permissions.items():
|
||||
|
||||
if "allowed" not in permission_infos:
|
||||
logger.warning(
|
||||
f"'allowed' key corresponding to allowed groups for permission {permission_name} not found when restoring app {app_instance_name} … You might have to reconfigure permissions yourself."
|
||||
|
@ -1547,7 +1543,6 @@ class RestoreManager:
|
|||
self.targets.set_result("apps", app_instance_name, "Success")
|
||||
operation_logger.success()
|
||||
else:
|
||||
|
||||
self.targets.set_result("apps", app_instance_name, "Error")
|
||||
|
||||
remove_script = os.path.join(app_scripts_in_archive, "remove")
|
||||
|
@ -1938,12 +1933,10 @@ class CopyBackupMethod(BackupMethod):
|
|||
|
||||
|
||||
class TarBackupMethod(BackupMethod):
|
||||
|
||||
method_name = "tar"
|
||||
|
||||
@property
|
||||
def _archive_file(self):
|
||||
|
||||
if isinstance(self.manager, BackupManager) and settings_get(
|
||||
"misc.backup.backup_compress_tar_archives"
|
||||
):
|
||||
|
@ -2430,7 +2423,6 @@ def backup_list(with_info=False, human_readable=False):
|
|||
|
||||
|
||||
def backup_download(name):
|
||||
|
||||
if Moulinette.interface.type != "api":
|
||||
logger.error(
|
||||
"This option is only meant for the API/webadmin and doesn't make sense for the command line."
|
||||
|
@ -2571,7 +2563,6 @@ def backup_info(name, with_details=False, human_readable=False):
|
|||
if "size_details" in info.keys():
|
||||
for category in ["apps", "system"]:
|
||||
for name, key_info in info[category].items():
|
||||
|
||||
if category == "system":
|
||||
# Stupid legacy fix for weird format between 3.5 and 3.6
|
||||
if isinstance(key_info, dict):
|
||||
|
|
|
@ -20,7 +20,7 @@ import os
|
|||
import sys
|
||||
import shutil
|
||||
import subprocess
|
||||
import glob
|
||||
from glob import glob
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
|
@ -124,10 +124,8 @@ def certificate_install(domain_list, force=False, no_checks=False, self_signed=F
|
|||
|
||||
|
||||
def _certificate_install_selfsigned(domain_list, force=False):
|
||||
|
||||
failed_cert_install = []
|
||||
for domain in domain_list:
|
||||
|
||||
operation_logger = OperationLogger(
|
||||
"selfsigned_cert_install", [("domain", domain)], args={"force": force}
|
||||
)
|
||||
|
@ -238,7 +236,6 @@ def _certificate_install_letsencrypt(domains, force=False, no_checks=False):
|
|||
# certificates
|
||||
if domains == []:
|
||||
for domain in domain_list()["domains"]:
|
||||
|
||||
status = _get_status(domain)
|
||||
if status["CA_type"] != "selfsigned":
|
||||
continue
|
||||
|
@ -260,7 +257,6 @@ def _certificate_install_letsencrypt(domains, force=False, no_checks=False):
|
|||
# Actual install steps
|
||||
failed_cert_install = []
|
||||
for domain in domains:
|
||||
|
||||
if not no_checks:
|
||||
try:
|
||||
_check_domain_is_ready_for_ACME(domain)
|
||||
|
@ -317,7 +313,6 @@ def certificate_renew(domains, force=False, no_checks=False, email=False):
|
|||
# certificates
|
||||
if domains == []:
|
||||
for domain in domain_list()["domains"]:
|
||||
|
||||
# Does it have a Let's Encrypt cert?
|
||||
status = _get_status(domain)
|
||||
if status["CA_type"] != "letsencrypt":
|
||||
|
@ -342,7 +337,6 @@ def certificate_renew(domains, force=False, no_checks=False, email=False):
|
|||
# Else, validate the domain list given
|
||||
else:
|
||||
for domain in domains:
|
||||
|
||||
# Is it in Yunohost domain list?
|
||||
_assert_domain_exists(domain)
|
||||
|
||||
|
@ -369,7 +363,6 @@ def certificate_renew(domains, force=False, no_checks=False, email=False):
|
|||
# Actual renew steps
|
||||
failed_cert_install = []
|
||||
for domain in domains:
|
||||
|
||||
if not no_checks:
|
||||
try:
|
||||
_check_domain_is_ready_for_ACME(domain)
|
||||
|
@ -468,13 +461,11 @@ investigate :
|
|||
|
||||
|
||||
def _check_acme_challenge_configuration(domain):
|
||||
|
||||
domain_conf = f"/etc/nginx/conf.d/{domain}.conf"
|
||||
return "include /etc/nginx/conf.d/acme-challenge.conf.inc" in read_file(domain_conf)
|
||||
|
||||
|
||||
def _fetch_and_enable_new_certificate(domain, no_checks=False):
|
||||
|
||||
if not os.path.exists(ACCOUNT_KEY_FILE):
|
||||
_generate_account_key()
|
||||
|
||||
|
@ -628,7 +619,6 @@ def _prepare_certificate_signing_request(domain, key_file, output_folder):
|
|||
|
||||
|
||||
def _get_status(domain):
|
||||
|
||||
cert_file = os.path.join(CERT_FOLDER, domain, "crt.pem")
|
||||
|
||||
if not os.path.isfile(cert_file):
|
||||
|
@ -744,10 +734,10 @@ def _enable_certificate(domain, new_cert_folder):
|
|||
logger.debug("Restarting services...")
|
||||
|
||||
for service in ("dovecot", "metronome"):
|
||||
# Ugly trick to not restart metronome if it's not installed
|
||||
if (
|
||||
service == "metronome"
|
||||
and os.system("dpkg --list | grep -q 'ii *metronome'") != 0
|
||||
# Ugly trick to not restart metronome if it's not installed or no domain configured for XMPP
|
||||
if service == "metronome" and (
|
||||
os.system("dpkg --list | grep -q 'ii *metronome'") != 0
|
||||
or not glob("/etc/metronome/conf.d/*.cfg.lua")
|
||||
):
|
||||
continue
|
||||
_run_service_command("restart", service)
|
||||
|
@ -777,7 +767,6 @@ def _backup_current_cert(domain):
|
|||
|
||||
|
||||
def _check_domain_is_ready_for_ACME(domain):
|
||||
|
||||
from yunohost.domain import _get_parent_domain_of
|
||||
from yunohost.dns import _get_dns_zone_for_domain
|
||||
from yunohost.utils.dns import is_yunohost_dyndns_domain
|
||||
|
@ -864,9 +853,8 @@ def _regen_dnsmasq_if_needed():
|
|||
do_regen = False
|
||||
|
||||
# For all domain files in DNSmasq conf...
|
||||
domainsconf = glob.glob("/etc/dnsmasq.d/*.*")
|
||||
domainsconf = glob("/etc/dnsmasq.d/*.*")
|
||||
for domainconf in domainsconf:
|
||||
|
||||
# Look for the IP, it's in the lines with this format :
|
||||
# host-record=the.domain.tld,11.22.33.44
|
||||
for line in open(domainconf).readlines():
|
||||
|
|
|
@ -35,13 +35,11 @@ logger = log.getActionLogger("yunohost.diagnosis")
|
|||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 600
|
||||
dependencies: List[str] = []
|
||||
|
||||
def run(self):
|
||||
|
||||
virt = system_virt()
|
||||
if virt.lower() == "none":
|
||||
virt = "bare-metal"
|
||||
|
@ -193,7 +191,6 @@ class MyDiagnoser(Diagnoser):
|
|||
)
|
||||
|
||||
def bad_sury_packages(self):
|
||||
|
||||
packages_to_check = ["openssl", "libssl1.1", "libssl-dev"]
|
||||
for package in packages_to_check:
|
||||
cmd = "dpkg --list | grep '^ii' | grep gbp | grep -q -w %s" % package
|
||||
|
@ -209,12 +206,10 @@ class MyDiagnoser(Diagnoser):
|
|||
yield (package, version_to_downgrade_to)
|
||||
|
||||
def backports_in_sources_list(self):
|
||||
|
||||
cmd = "grep -q -nr '^ *deb .*-backports' /etc/apt/sources.list*"
|
||||
return os.system(cmd) == 0
|
||||
|
||||
def number_of_recent_auth_failure(self):
|
||||
|
||||
# Those syslog facilities correspond to auth and authpriv
|
||||
# c.f. https://unix.stackexchange.com/a/401398
|
||||
# and https://wiki.archlinux.org/title/Systemd/Journal#Facility
|
||||
|
|
|
@ -28,18 +28,17 @@ from moulinette.utils.filesystem import read_file
|
|||
|
||||
from yunohost.diagnosis import Diagnoser
|
||||
from yunohost.utils.network import get_network_interfaces
|
||||
from yunohost.settings import settings_get
|
||||
|
||||
logger = log.getActionLogger("yunohost.diagnosis")
|
||||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 600
|
||||
dependencies: List[str] = []
|
||||
|
||||
def run(self):
|
||||
|
||||
# ############################################################ #
|
||||
# PING : Check that we can ping outside at least in ipv4 or v6 #
|
||||
# ############################################################ #
|
||||
|
@ -118,10 +117,17 @@ class MyDiagnoser(Diagnoser):
|
|||
else:
|
||||
return local_ip
|
||||
|
||||
def is_ipvx_important(x):
|
||||
return settings_get("misc.network.dns_exposure") in ["both", "ipv" + str(x)]
|
||||
|
||||
yield dict(
|
||||
meta={"test": "ipv4"},
|
||||
data={"global": ipv4, "local": get_local_ip("ipv4")},
|
||||
status="SUCCESS" if ipv4 else "ERROR",
|
||||
status="SUCCESS"
|
||||
if ipv4
|
||||
else "ERROR"
|
||||
if is_ipvx_important(4)
|
||||
else "WARNING",
|
||||
summary="diagnosis_ip_connected_ipv4" if ipv4 else "diagnosis_ip_no_ipv4",
|
||||
details=["diagnosis_ip_global", "diagnosis_ip_local"] if ipv4 else None,
|
||||
)
|
||||
|
@ -129,17 +135,24 @@ class MyDiagnoser(Diagnoser):
|
|||
yield dict(
|
||||
meta={"test": "ipv6"},
|
||||
data={"global": ipv6, "local": get_local_ip("ipv6")},
|
||||
status="SUCCESS" if ipv6 else "WARNING",
|
||||
status="SUCCESS"
|
||||
if ipv6
|
||||
else "ERROR"
|
||||
if settings_get("misc.network.dns_exposure") == "ipv6"
|
||||
else "WARNING",
|
||||
summary="diagnosis_ip_connected_ipv6" if ipv6 else "diagnosis_ip_no_ipv6",
|
||||
details=["diagnosis_ip_global", "diagnosis_ip_local"]
|
||||
if ipv6
|
||||
else ["diagnosis_ip_no_ipv6_tip"],
|
||||
else [
|
||||
"diagnosis_ip_no_ipv6_tip_important"
|
||||
if is_ipvx_important(6)
|
||||
else "diagnosis_ip_no_ipv6_tip"
|
||||
],
|
||||
)
|
||||
|
||||
# TODO / FIXME : add some attempt to detect ISP (using whois ?) ?
|
||||
|
||||
def can_ping_outside(self, protocol=4):
|
||||
|
||||
assert protocol in [
|
||||
4,
|
||||
6,
|
||||
|
@ -218,7 +231,6 @@ class MyDiagnoser(Diagnoser):
|
|||
return len(content) == 1 and content[0].split() == ["nameserver", "127.0.0.1"]
|
||||
|
||||
def get_public_ip(self, protocol=4):
|
||||
|
||||
# FIXME - TODO : here we assume that DNS resolution for ip.yunohost.org is working
|
||||
# but if we want to be able to diagnose DNS resolution issues independently from
|
||||
# internet connectivity, we gotta rely on fixed IPs first....
|
||||
|
|
|
@ -43,13 +43,11 @@ logger = log.getActionLogger("yunohost.diagnosis")
|
|||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 600
|
||||
dependencies: List[str] = ["ip"]
|
||||
|
||||
def run(self):
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
|
||||
major_domains = domain_list(exclude_subdomains=True)["domains"]
|
||||
|
@ -77,7 +75,6 @@ class MyDiagnoser(Diagnoser):
|
|||
yield report
|
||||
|
||||
def check_domain(self, domain, is_main_domain):
|
||||
|
||||
if is_special_use_tld(domain):
|
||||
yield dict(
|
||||
meta={"domain": domain},
|
||||
|
@ -97,13 +94,11 @@ class MyDiagnoser(Diagnoser):
|
|||
categories = ["basic", "mail", "xmpp", "extra"]
|
||||
|
||||
for category in categories:
|
||||
|
||||
records = expected_configuration[category]
|
||||
discrepancies = []
|
||||
results = {}
|
||||
|
||||
for r in records:
|
||||
|
||||
id_ = r["type"] + ":" + r["name"]
|
||||
fqdn = r["name"] + "." + base_dns_zone if r["name"] != "@" else domain
|
||||
|
||||
|
@ -182,7 +177,6 @@ class MyDiagnoser(Diagnoser):
|
|||
yield output
|
||||
|
||||
def get_current_record(self, fqdn, type_):
|
||||
|
||||
success, answers = dig(fqdn, type_, resolvers="force_external")
|
||||
|
||||
if success != "ok":
|
||||
|
|
|
@ -21,16 +21,15 @@ from typing import List
|
|||
|
||||
from yunohost.diagnosis import Diagnoser
|
||||
from yunohost.service import _get_services
|
||||
from yunohost.settings import settings_get
|
||||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 600
|
||||
dependencies: List[str] = ["ip"]
|
||||
|
||||
def run(self):
|
||||
|
||||
# TODO: report a warning if port 53 or 5353 is exposed to the outside world...
|
||||
|
||||
# This dict is something like :
|
||||
|
@ -46,7 +45,10 @@ class MyDiagnoser(Diagnoser):
|
|||
|
||||
ipversions = []
|
||||
ipv4 = Diagnoser.get_cached_report("ip", item={"test": "ipv4"}) or {}
|
||||
if ipv4.get("status") == "SUCCESS":
|
||||
if (
|
||||
ipv4.get("status") == "SUCCESS"
|
||||
or settings_get("misc.network.dns_exposure") != "ipv6"
|
||||
):
|
||||
ipversions.append(4)
|
||||
|
||||
# To be discussed: we could also make this check dependent on the
|
||||
|
@ -120,7 +122,10 @@ class MyDiagnoser(Diagnoser):
|
|||
for record in dnsrecords.get("items", [])
|
||||
)
|
||||
|
||||
if failed == 4 or ipv6_is_important():
|
||||
if (
|
||||
failed == 4
|
||||
and settings_get("misc.network.dns_exposure") in ["both", "ipv4"]
|
||||
) or (failed == 6 and ipv6_is_important()):
|
||||
yield dict(
|
||||
meta={"port": port},
|
||||
data={
|
||||
|
|
|
@ -26,22 +26,20 @@ from moulinette.utils.filesystem import read_file, mkdir, rm
|
|||
from yunohost.diagnosis import Diagnoser
|
||||
from yunohost.domain import domain_list
|
||||
from yunohost.utils.dns import is_special_use_tld
|
||||
from yunohost.settings import settings_get
|
||||
|
||||
DIAGNOSIS_SERVER = "diagnosis.yunohost.org"
|
||||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 600
|
||||
dependencies: List[str] = ["ip"]
|
||||
|
||||
def run(self):
|
||||
|
||||
all_domains = domain_list()["domains"]
|
||||
domains_to_check = []
|
||||
for domain in all_domains:
|
||||
|
||||
# If the diagnosis location ain't defined, can't do diagnosis,
|
||||
# probably because nginx conf manually modified...
|
||||
nginx_conf = "/etc/nginx/conf.d/%s.conf" % domain
|
||||
|
@ -76,7 +74,9 @@ class MyDiagnoser(Diagnoser):
|
|||
|
||||
ipversions = []
|
||||
ipv4 = Diagnoser.get_cached_report("ip", item={"test": "ipv4"}) or {}
|
||||
if ipv4.get("status") == "SUCCESS":
|
||||
if ipv4.get("status") == "SUCCESS" and settings_get(
|
||||
"misc.network.dns_exposure"
|
||||
) in ["both", "ipv4"]:
|
||||
ipversions.append(4)
|
||||
|
||||
# To be discussed: we could also make this check dependent on the
|
||||
|
@ -96,7 +96,10 @@ class MyDiagnoser(Diagnoser):
|
|||
# "curl --head the.global.ip" will simply timeout...
|
||||
if self.do_hairpinning_test:
|
||||
global_ipv4 = ipv4.get("data", {}).get("global", None)
|
||||
if global_ipv4:
|
||||
if global_ipv4 and settings_get("misc.network.dns_exposure") in [
|
||||
"both",
|
||||
"ipv4",
|
||||
]:
|
||||
try:
|
||||
requests.head("http://" + global_ipv4, timeout=5)
|
||||
except requests.exceptions.Timeout:
|
||||
|
@ -113,7 +116,6 @@ class MyDiagnoser(Diagnoser):
|
|||
pass
|
||||
|
||||
def test_http(self, domains, ipversions):
|
||||
|
||||
results = {}
|
||||
for ipversion in ipversions:
|
||||
try:
|
||||
|
@ -138,7 +140,6 @@ class MyDiagnoser(Diagnoser):
|
|||
return
|
||||
|
||||
for domain in domains:
|
||||
|
||||
# i18n: diagnosis_http_bad_status_code
|
||||
# i18n: diagnosis_http_connection_error
|
||||
# i18n: diagnosis_http_timeout
|
||||
|
@ -147,7 +148,10 @@ class MyDiagnoser(Diagnoser):
|
|||
if all(
|
||||
results[ipversion][domain]["status"] == "ok" for ipversion in ipversions
|
||||
):
|
||||
if 4 in ipversions:
|
||||
if 4 in ipversions and settings_get("misc.network.dns_exposure") in [
|
||||
"both",
|
||||
"ipv4",
|
||||
]:
|
||||
self.do_hairpinning_test = True
|
||||
yield dict(
|
||||
meta={"domain": domain},
|
||||
|
@ -185,7 +189,9 @@ class MyDiagnoser(Diagnoser):
|
|||
)
|
||||
AAAA_status = dnsrecords.get("data", {}).get("AAAA:@")
|
||||
|
||||
return AAAA_status in ["OK", "WRONG"]
|
||||
return AAAA_status in ["OK", "WRONG"] or settings_get(
|
||||
"misc.network.dns_exposure"
|
||||
) in ["both", "ipv6"]
|
||||
|
||||
if failed == 4 or ipv6_is_important_for_this_domain():
|
||||
yield dict(
|
||||
|
|
|
@ -38,13 +38,11 @@ logger = log.getActionLogger("yunohost.diagnosis")
|
|||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 600
|
||||
dependencies: List[str] = ["ip"]
|
||||
|
||||
def run(self):
|
||||
|
||||
self.ehlo_domain = _get_maindomain()
|
||||
self.mail_domains = domain_list()["domains"]
|
||||
self.ipversions, self.ips = self.get_ips_checked()
|
||||
|
@ -301,13 +299,17 @@ class MyDiagnoser(Diagnoser):
|
|||
outgoing_ipversions = []
|
||||
outgoing_ips = []
|
||||
ipv4 = Diagnoser.get_cached_report("ip", {"test": "ipv4"}) or {}
|
||||
if ipv4.get("status") == "SUCCESS":
|
||||
if ipv4.get("status") == "SUCCESS" and settings_get(
|
||||
"misc.network.dns_exposure"
|
||||
) in ["both", "ipv4"]:
|
||||
outgoing_ipversions.append(4)
|
||||
global_ipv4 = ipv4.get("data", {}).get("global", {})
|
||||
if global_ipv4:
|
||||
outgoing_ips.append(global_ipv4)
|
||||
|
||||
if settings_get("email.smtp.smtp_allow_ipv6"):
|
||||
if settings_get("email.smtp.smtp_allow_ipv6") or settings_get(
|
||||
"misc.network.dns_exposure"
|
||||
) in ["both", "ipv6"]:
|
||||
ipv6 = Diagnoser.get_cached_report("ip", {"test": "ipv6"}) or {}
|
||||
if ipv6.get("status") == "SUCCESS":
|
||||
outgoing_ipversions.append(6)
|
||||
|
|
|
@ -24,17 +24,14 @@ from yunohost.service import service_status
|
|||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 300
|
||||
dependencies: List[str] = []
|
||||
|
||||
def run(self):
|
||||
|
||||
all_result = service_status()
|
||||
|
||||
for service, result in sorted(all_result.items()):
|
||||
|
||||
item = dict(
|
||||
meta={"service": service},
|
||||
data={
|
||||
|
|
|
@ -28,13 +28,11 @@ from yunohost.diagnosis import Diagnoser
|
|||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 300
|
||||
dependencies: List[str] = []
|
||||
|
||||
def run(self):
|
||||
|
||||
MB = 1024**2
|
||||
GB = MB * 1024
|
||||
|
||||
|
@ -189,7 +187,6 @@ class MyDiagnoser(Diagnoser):
|
|||
return []
|
||||
|
||||
def analyzed_kern_log():
|
||||
|
||||
cmd = 'tail -n 10000 /var/log/kern.log | grep "oom_reaper: reaped process" || true'
|
||||
out = check_output(cmd)
|
||||
lines = out.split("\n") if out else []
|
||||
|
|
|
@ -27,13 +27,11 @@ from moulinette.utils.filesystem import read_file
|
|||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 300
|
||||
dependencies: List[str] = []
|
||||
|
||||
def run(self):
|
||||
|
||||
regenconf_modified_files = list(self.manually_modified_files())
|
||||
|
||||
if not regenconf_modified_files:
|
||||
|
@ -82,7 +80,6 @@ class MyDiagnoser(Diagnoser):
|
|||
)
|
||||
|
||||
def manually_modified_files(self):
|
||||
|
||||
for category, infos in _get_regenconf_infos().items():
|
||||
for path, hash_ in infos["conffiles"].items():
|
||||
if hash_ != _calculate_hash(path):
|
||||
|
|
|
@ -25,13 +25,11 @@ from yunohost.diagnosis import Diagnoser
|
|||
|
||||
|
||||
class MyDiagnoser(Diagnoser):
|
||||
|
||||
id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1]
|
||||
cache_duration = 300
|
||||
dependencies: List[str] = []
|
||||
|
||||
def run(self):
|
||||
|
||||
apps = app_list(full=True)["apps"]
|
||||
for app in apps:
|
||||
app["issues"] = list(self.issues(app))
|
||||
|
@ -44,7 +42,6 @@ class MyDiagnoser(Diagnoser):
|
|||
)
|
||||
else:
|
||||
for app in apps:
|
||||
|
||||
if not app["issues"]:
|
||||
continue
|
||||
|
||||
|
@ -62,7 +59,6 @@ class MyDiagnoser(Diagnoser):
|
|||
)
|
||||
|
||||
def issues(self, app):
|
||||
|
||||
# Check quality level in catalog
|
||||
|
||||
if not app.get("from_catalog") or app["from_catalog"].get("state") != "working":
|
||||
|
|
|
@ -45,7 +45,6 @@ def diagnosis_list():
|
|||
|
||||
|
||||
def diagnosis_get(category, item):
|
||||
|
||||
# Get all the categories
|
||||
all_categories_names = _list_diagnosis_categories()
|
||||
|
||||
|
@ -69,7 +68,6 @@ def diagnosis_get(category, item):
|
|||
def diagnosis_show(
|
||||
categories=[], issues=False, full=False, share=False, human_readable=False
|
||||
):
|
||||
|
||||
if not os.path.exists(DIAGNOSIS_CACHE):
|
||||
logger.warning(m18n.n("diagnosis_never_ran_yet"))
|
||||
return
|
||||
|
@ -90,7 +88,6 @@ def diagnosis_show(
|
|||
# Fetch all reports
|
||||
all_reports = []
|
||||
for category in categories:
|
||||
|
||||
try:
|
||||
report = Diagnoser.get_cached_report(category)
|
||||
except Exception as e:
|
||||
|
@ -139,7 +136,6 @@ def diagnosis_show(
|
|||
|
||||
|
||||
def _dump_human_readable_reports(reports):
|
||||
|
||||
output = ""
|
||||
|
||||
for report in reports:
|
||||
|
@ -159,7 +155,6 @@ def _dump_human_readable_reports(reports):
|
|||
def diagnosis_run(
|
||||
categories=[], force=False, except_if_never_ran_yet=False, email=False
|
||||
):
|
||||
|
||||
if (email or except_if_never_ran_yet) and not os.path.exists(DIAGNOSIS_CACHE):
|
||||
return
|
||||
|
||||
|
@ -263,7 +258,6 @@ def _diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
|
|||
return {"ignore_filters": configuration.get("ignore_filters", {})}
|
||||
|
||||
def validate_filter_criterias(filter_):
|
||||
|
||||
# Get all the categories
|
||||
all_categories_names = _list_diagnosis_categories()
|
||||
|
||||
|
@ -286,7 +280,6 @@ def _diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
|
|||
return category, criterias
|
||||
|
||||
if add_filter:
|
||||
|
||||
category, criterias = validate_filter_criterias(add_filter)
|
||||
|
||||
# Fetch current issues for the requested category
|
||||
|
@ -320,7 +313,6 @@ def _diagnosis_ignore(add_filter=None, remove_filter=None, list=False):
|
|||
return
|
||||
|
||||
if remove_filter:
|
||||
|
||||
category, criterias = validate_filter_criterias(remove_filter)
|
||||
|
||||
# Make sure the subdicts/lists exists
|
||||
|
@ -394,12 +386,10 @@ def add_ignore_flag_to_issues(report):
|
|||
|
||||
class Diagnoser:
|
||||
def __init__(self):
|
||||
|
||||
self.cache_file = Diagnoser.cache_file(self.id_)
|
||||
self.description = Diagnoser.get_description(self.id_)
|
||||
|
||||
def cached_time_ago(self):
|
||||
|
||||
if not os.path.exists(self.cache_file):
|
||||
return 99999999
|
||||
return time.time() - os.path.getmtime(self.cache_file)
|
||||
|
@ -410,7 +400,6 @@ class Diagnoser:
|
|||
return write_to_json(self.cache_file, report)
|
||||
|
||||
def diagnose(self, force=False):
|
||||
|
||||
if not force and self.cached_time_ago() < self.cache_duration:
|
||||
logger.debug(f"Cache still valid : {self.cache_file}")
|
||||
logger.info(
|
||||
|
@ -548,7 +537,6 @@ class Diagnoser:
|
|||
|
||||
@staticmethod
|
||||
def i18n(report, force_remove_html_tags=False):
|
||||
|
||||
# "Render" the strings with m18n.n
|
||||
# N.B. : we do those m18n.n right now instead of saving the already-translated report
|
||||
# because we can't be sure we'll redisplay the infos with the same locale as it
|
||||
|
@ -558,7 +546,6 @@ class Diagnoser:
|
|||
report["description"] = Diagnoser.get_description(report["id"])
|
||||
|
||||
for item in report["items"]:
|
||||
|
||||
# For the summary and each details, we want to call
|
||||
# m18n() on the string, with the appropriate data for string
|
||||
# formatting which can come from :
|
||||
|
@ -597,7 +584,6 @@ class Diagnoser:
|
|||
|
||||
@staticmethod
|
||||
def remote_diagnosis(uri, data, ipversion, timeout=30):
|
||||
|
||||
# Lazy loading for performance
|
||||
import requests
|
||||
import socket
|
||||
|
@ -646,7 +632,6 @@ class Diagnoser:
|
|||
|
||||
|
||||
def _list_diagnosis_categories():
|
||||
|
||||
paths = glob.glob(os.path.dirname(__file__) + "/diagnosers/??-*.py")
|
||||
names = [
|
||||
name.split("-")[-1]
|
||||
|
@ -657,7 +642,6 @@ def _list_diagnosis_categories():
|
|||
|
||||
|
||||
def _load_diagnoser(diagnoser_name):
|
||||
|
||||
logger.debug(f"Loading diagnoser {diagnoser_name}")
|
||||
|
||||
paths = glob.glob(os.path.dirname(__file__) + f"/diagnosers/??-{diagnoser_name}.py")
|
||||
|
|
17
src/dns.py
17
src/dns.py
|
@ -38,6 +38,7 @@ from yunohost.domain import (
|
|||
from yunohost.utils.dns import dig, is_yunohost_dyndns_domain, is_special_use_tld
|
||||
from yunohost.utils.error import YunohostValidationError, YunohostError
|
||||
from yunohost.utils.network import get_public_ip
|
||||
from yunohost.settings import settings_get
|
||||
from yunohost.log import is_unit_operation
|
||||
from yunohost.hook import hook_callback
|
||||
|
||||
|
@ -168,7 +169,6 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
|
|||
base_dns_zone = _get_dns_zone_for_domain(base_domain)
|
||||
|
||||
for domain, settings in domains_settings.items():
|
||||
|
||||
# Domain # Base DNS zone # Basename # Suffix #
|
||||
# ------------------ # ----------------- # --------- # -------- #
|
||||
# domain.tld # domain.tld # @ # #
|
||||
|
@ -185,7 +185,7 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
|
|||
###########################
|
||||
# Basic ipv4/ipv6 records #
|
||||
###########################
|
||||
if ipv4:
|
||||
if ipv4 and settings_get("misc.network.dns_exposure") in ["both", "ipv4"]:
|
||||
basic.append([basename, ttl, "A", ipv4])
|
||||
|
||||
if ipv6:
|
||||
|
@ -240,7 +240,7 @@ def _build_dns_conf(base_domain, include_empty_AAAA_if_no_ipv6=False):
|
|||
|
||||
# Only recommend wildcard and CAA for the top level
|
||||
if domain == base_domain:
|
||||
if ipv4:
|
||||
if ipv4 and settings_get("misc.network.dns_exposure") in ["both", "ipv4"]:
|
||||
extra.append([f"*{suffix}", ttl, "A", ipv4])
|
||||
|
||||
if ipv6:
|
||||
|
@ -461,7 +461,6 @@ def _get_dns_zone_for_domain(domain):
|
|||
|
||||
# We don't wan't to do A NS request on the tld
|
||||
for parent in parent_list[0:-1]:
|
||||
|
||||
# Check if there's a NS record for that domain
|
||||
answer = dig(parent, rdtype="NS", full_answers=True, resolvers="force_external")
|
||||
|
||||
|
@ -502,7 +501,6 @@ def _get_relative_name_for_dns_zone(domain, base_dns_zone):
|
|||
|
||||
|
||||
def _get_registrar_config_section(domain):
|
||||
|
||||
from lexicon.providers.auto import _relevant_provider_for_domain
|
||||
|
||||
registrar_infos = {
|
||||
|
@ -516,7 +514,6 @@ def _get_registrar_config_section(domain):
|
|||
# If parent domain exists in yunohost
|
||||
parent_domain = _get_parent_domain_of(domain, topest=True)
|
||||
if parent_domain:
|
||||
|
||||
# Dirty hack to have a link on the webadmin
|
||||
if Moulinette.interface.type == "api":
|
||||
parent_domain_link = f"[{parent_domain}](#/domains/{parent_domain}/dns)"
|
||||
|
@ -571,7 +568,6 @@ def _get_registrar_config_section(domain):
|
|||
}
|
||||
)
|
||||
else:
|
||||
|
||||
registrar_infos["registrar"] = OrderedDict(
|
||||
{
|
||||
"type": "alert",
|
||||
|
@ -605,7 +601,6 @@ def _get_registrar_config_section(domain):
|
|||
|
||||
|
||||
def _get_registar_settings(domain):
|
||||
|
||||
_assert_domain_exists(domain)
|
||||
|
||||
settings = domain_config_get(domain, key="dns.registrar", export=True)
|
||||
|
@ -669,7 +664,6 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=
|
|||
wanted_records = []
|
||||
for records in _build_dns_conf(domain).values():
|
||||
for record in records:
|
||||
|
||||
# Make sure the name is a FQDN
|
||||
name = (
|
||||
f"{record['name']}.{base_dns_zone}"
|
||||
|
@ -744,7 +738,6 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=
|
|||
]
|
||||
|
||||
for record in current_records:
|
||||
|
||||
# Try to get rid of weird stuff like ".domain.tld" or "@.domain.tld"
|
||||
record["name"] = record["name"].strip("@").strip(".")
|
||||
|
||||
|
@ -794,7 +787,6 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=
|
|||
comparison[(record["type"], record["name"])]["wanted"].append(record)
|
||||
|
||||
for type_and_name, records in comparison.items():
|
||||
|
||||
#
|
||||
# Step 1 : compute a first "diff" where we remove records which are the same on both sides
|
||||
#
|
||||
|
@ -938,9 +930,7 @@ def domain_dns_push(operation_logger, domain, dry_run=False, force=False, purge=
|
|||
results = {"warnings": [], "errors": []}
|
||||
|
||||
for action in ["delete", "create", "update"]:
|
||||
|
||||
for record in changes[action]:
|
||||
|
||||
relative_name = _get_relative_name_for_dns_zone(
|
||||
record["name"], base_dns_zone
|
||||
)
|
||||
|
@ -1025,7 +1015,6 @@ def _set_managed_dns_records_hashes(domain: str, hashes: list) -> None:
|
|||
|
||||
|
||||
def _hash_dns_record(record: dict) -> int:
|
||||
|
||||
fields = ["name", "type", "content"]
|
||||
record_ = {f: record.get(f) for f in fields}
|
||||
|
||||
|
|
|
@ -187,7 +187,6 @@ def _assert_domain_exists(domain):
|
|||
|
||||
|
||||
def _list_subdomains_of(parent_domain):
|
||||
|
||||
_assert_domain_exists(parent_domain)
|
||||
|
||||
out = []
|
||||
|
@ -199,7 +198,6 @@ def _list_subdomains_of(parent_domain):
|
|||
|
||||
|
||||
def _get_parent_domain_of(domain, return_self=False, topest=False):
|
||||
|
||||
domains = _get_domains(exclude_subdomains=topest)
|
||||
|
||||
domain_ = domain
|
||||
|
@ -248,7 +246,6 @@ def domain_add(operation_logger, domain, dyndns=False):
|
|||
|
||||
# DynDNS domain
|
||||
if dyndns:
|
||||
|
||||
from yunohost.utils.dns import is_yunohost_dyndns_domain
|
||||
from yunohost.dyndns import _guess_current_dyndns_domain
|
||||
|
||||
|
@ -589,7 +586,6 @@ class DomainConfigPanel(ConfigPanel):
|
|||
regen_conf(names=stuff_to_regen_conf)
|
||||
|
||||
def _get_toml(self):
|
||||
|
||||
toml = super()._get_toml()
|
||||
|
||||
toml["feature"]["xmpp"]["xmpp"]["default"] = (
|
||||
|
@ -611,7 +607,6 @@ class DomainConfigPanel(ConfigPanel):
|
|||
|
||||
# Cert stuff
|
||||
if not filter_key or filter_key[0] == "cert":
|
||||
|
||||
from yunohost.certificate import certificate_status
|
||||
|
||||
status = certificate_status([self.entity], full=True)["certificates"][
|
||||
|
@ -638,7 +633,6 @@ class DomainConfigPanel(ConfigPanel):
|
|||
return toml
|
||||
|
||||
def _load_current_values(self):
|
||||
|
||||
# TODO add mechanism to share some settings with other domains on the same zone
|
||||
super()._load_current_values()
|
||||
|
||||
|
@ -656,7 +650,6 @@ class DomainConfigPanel(ConfigPanel):
|
|||
|
||||
|
||||
def domain_action_run(domain, action, args=None):
|
||||
|
||||
import urllib.parse
|
||||
|
||||
if action == "cert.cert.cert_install":
|
||||
|
@ -671,7 +664,6 @@ def domain_action_run(domain, action, args=None):
|
|||
|
||||
|
||||
def _get_domain_settings(domain: str) -> dict:
|
||||
|
||||
_assert_domain_exists(domain)
|
||||
|
||||
if os.path.exists(f"{DOMAIN_SETTINGS_DIR}/{domain}.yml"):
|
||||
|
@ -681,7 +673,6 @@ def _get_domain_settings(domain: str) -> dict:
|
|||
|
||||
|
||||
def _set_domain_settings(domain: str, settings: dict) -> None:
|
||||
|
||||
_assert_domain_exists(domain)
|
||||
|
||||
write_to_yaml(f"{DOMAIN_SETTINGS_DIR}/{domain}.yml", settings)
|
||||
|
|
|
@ -227,7 +227,6 @@ def dyndns_update(
|
|||
|
||||
for dns_auth in DYNDNS_DNS_AUTH:
|
||||
for type_ in ["A", "AAAA"]:
|
||||
|
||||
ok, result = dig(dns_auth, type_)
|
||||
if ok == "ok" and len(result) and result[0]:
|
||||
auth_resolvers.append(result[0])
|
||||
|
@ -238,7 +237,6 @@ def dyndns_update(
|
|||
)
|
||||
|
||||
def resolve_domain(domain, rdtype):
|
||||
|
||||
ok, result = dig(domain, rdtype, resolvers=auth_resolvers)
|
||||
if ok == "ok":
|
||||
return result[0] if len(result) else None
|
||||
|
|
|
@ -415,7 +415,6 @@ def firewall_upnp(action="status", no_refresh=False):
|
|||
for protocol in ["TCP", "UDP"]:
|
||||
if protocol + "_TO_CLOSE" in firewall["uPnP"]:
|
||||
for port in firewall["uPnP"][protocol + "_TO_CLOSE"]:
|
||||
|
||||
if not isinstance(port, int):
|
||||
# FIXME : how should we handle port ranges ?
|
||||
logger.warning("Can't use UPnP to close '%s'" % port)
|
||||
|
@ -430,7 +429,6 @@ def firewall_upnp(action="status", no_refresh=False):
|
|||
firewall["uPnP"][protocol + "_TO_CLOSE"] = []
|
||||
|
||||
for port in firewall["uPnP"][protocol]:
|
||||
|
||||
if not isinstance(port, int):
|
||||
# FIXME : how should we handle port ranges ?
|
||||
logger.warning("Can't use UPnP to open '%s'" % port)
|
||||
|
|
|
@ -339,7 +339,6 @@ def hook_exec(
|
|||
raise YunohostError("file_does_not_exist", path=path)
|
||||
|
||||
def is_relevant_warning(msg):
|
||||
|
||||
# Ignore empty warning messages...
|
||||
if not msg:
|
||||
return False
|
||||
|
@ -389,7 +388,6 @@ def hook_exec(
|
|||
|
||||
|
||||
def _hook_exec_bash(path, args, chdir, env, user, return_format, loggers):
|
||||
|
||||
from moulinette.utils.process import call_async_output
|
||||
|
||||
# Construct command variables
|
||||
|
@ -477,7 +475,6 @@ def _hook_exec_bash(path, args, chdir, env, user, return_format, loggers):
|
|||
|
||||
|
||||
def _hook_exec_python(path, args, env, loggers):
|
||||
|
||||
dir_ = os.path.dirname(path)
|
||||
name = os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
|
@ -497,7 +494,6 @@ def _hook_exec_python(path, args, env, loggers):
|
|||
|
||||
|
||||
def hook_exec_with_script_debug_if_failure(*args, **kwargs):
|
||||
|
||||
operation_logger = kwargs.pop("operation_logger")
|
||||
error_message_if_failed = kwargs.pop("error_message_if_failed")
|
||||
error_message_if_script_failed = kwargs.pop("error_message_if_script_failed")
|
||||
|
|
|
@ -95,7 +95,6 @@ def log_list(limit=None, with_details=False, with_suboperations=False):
|
|||
logs = logs[: limit * 5]
|
||||
|
||||
for log in logs:
|
||||
|
||||
base_filename = log[: -len(METADATA_FILE_EXT)]
|
||||
md_path = os.path.join(OPERATIONS_PATH, log)
|
||||
|
||||
|
@ -264,7 +263,6 @@ def log_show(
|
|||
return
|
||||
|
||||
for filename in os.listdir(OPERATIONS_PATH):
|
||||
|
||||
if not filename.endswith(METADATA_FILE_EXT):
|
||||
continue
|
||||
|
||||
|
@ -438,7 +436,6 @@ class RedactingFormatter(Formatter):
|
|||
return msg
|
||||
|
||||
def identify_data_to_redact(self, record):
|
||||
|
||||
# Wrapping this in a try/except because we don't want this to
|
||||
# break everything in case it fails miserably for some reason :s
|
||||
try:
|
||||
|
@ -497,7 +494,6 @@ class OperationLogger:
|
|||
os.makedirs(self.path)
|
||||
|
||||
def parent_logger(self):
|
||||
|
||||
# If there are other operation logger instances
|
||||
for instance in reversed(self._instances):
|
||||
# Is one of these operation logger started but not yet done ?
|
||||
|
@ -732,7 +728,6 @@ class OperationLogger:
|
|||
self.error(m18n.n("log_operation_unit_unclosed_properly"))
|
||||
|
||||
def dump_script_log_extract_for_debugging(self):
|
||||
|
||||
with open(self.log_path, "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
|
@ -774,7 +769,6 @@ class OperationLogger:
|
|||
|
||||
|
||||
def _get_datetime_from_name(name):
|
||||
|
||||
# Filenames are expected to follow the format:
|
||||
# 20200831-170740-short_description-and-stuff
|
||||
|
||||
|
|
|
@ -72,13 +72,11 @@ def _backup_pip_freeze_for_python_app_venvs():
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Upgrade the system to Debian Bullseye and Yunohost 11.x"
|
||||
|
||||
mode = "manual"
|
||||
|
||||
def run(self):
|
||||
|
||||
self.check_assertions()
|
||||
|
||||
logger.info(m18n.n("migration_0021_start"))
|
||||
|
@ -389,7 +387,6 @@ class MyMigration(Migration):
|
|||
return int(get_ynh_package_version("yunohost")["version"].split(".")[0])
|
||||
|
||||
def check_assertions(self):
|
||||
|
||||
# Be on buster (10.x) and yunohost 4.x
|
||||
# NB : we do both check to cover situations where the upgrade crashed
|
||||
# in the middle and debian version could be > 9.x but yunohost package
|
||||
|
@ -453,7 +450,6 @@ class MyMigration(Migration):
|
|||
|
||||
@property
|
||||
def disclaimer(self):
|
||||
|
||||
# Avoid having a super long disclaimer + uncessary check if we ain't
|
||||
# on buster / yunohost 4.x anymore
|
||||
# NB : we do both check to cover situations where the upgrade crashed
|
||||
|
@ -494,7 +490,6 @@ class MyMigration(Migration):
|
|||
return message
|
||||
|
||||
def patch_apt_sources_list(self):
|
||||
|
||||
sources_list = glob.glob("/etc/apt/sources.list.d/*.list")
|
||||
if os.path.exists("/etc/apt/sources.list"):
|
||||
sources_list.append("/etc/apt/sources.list")
|
||||
|
@ -516,7 +511,6 @@ class MyMigration(Migration):
|
|||
os.system(command)
|
||||
|
||||
def get_apps_equivs_packages(self):
|
||||
|
||||
command = (
|
||||
"dpkg --get-selections"
|
||||
" | grep -v deinstall"
|
||||
|
|
|
@ -27,7 +27,6 @@ MIGRATION_COMMENT = (
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate php7.3-fpm 'pool' conf files to php7.4"
|
||||
|
||||
dependencies = ["migrate_to_bullseye"]
|
||||
|
@ -43,7 +42,6 @@ class MyMigration(Migration):
|
|||
oldphp_pool_files = [f for f in oldphp_pool_files if f != "www.conf"]
|
||||
|
||||
for pf in oldphp_pool_files:
|
||||
|
||||
# Copy the files to the php7.3 pool
|
||||
src = "{}/{}".format(OLDPHP_POOLS, pf)
|
||||
dest = "{}/{}".format(NEWPHP_POOLS, pf)
|
||||
|
|
|
@ -13,13 +13,11 @@ logger = getActionLogger("yunohost.migration")
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate DBs from Postgresql 11 to 13 after migrating to Bullseye"
|
||||
|
||||
dependencies = ["migrate_to_bullseye"]
|
||||
|
||||
def run(self):
|
||||
|
||||
if (
|
||||
os.system(
|
||||
'grep -A10 "ynh-deps" /var/lib/dpkg/status | grep -E "Package:|Depends:" | grep -B1 postgresql'
|
||||
|
@ -63,7 +61,6 @@ class MyMigration(Migration):
|
|||
self.runcmd("systemctl start postgresql")
|
||||
|
||||
def package_is_installed(self, package_name):
|
||||
|
||||
(returncode, out, err) = self.runcmd(
|
||||
"dpkg --list | grep '^ii ' | grep -q -w {}".format(package_name),
|
||||
raise_on_errors=False,
|
||||
|
@ -71,7 +68,6 @@ class MyMigration(Migration):
|
|||
return returncode == 0
|
||||
|
||||
def runcmd(self, cmd, raise_on_errors=True):
|
||||
|
||||
logger.debug("Running command: " + cmd)
|
||||
|
||||
p = subprocess.Popen(
|
||||
|
|
|
@ -14,7 +14,6 @@ VENV_REQUIREMENTS_SUFFIX = ".requirements_backup_for_bullseye_upgrade.txt"
|
|||
|
||||
|
||||
def extract_app_from_venv_path(venv_path):
|
||||
|
||||
venv_path = venv_path.replace("/var/www/", "")
|
||||
venv_path = venv_path.replace("/opt/yunohost/", "")
|
||||
venv_path = venv_path.replace("/opt/", "")
|
||||
|
@ -137,13 +136,11 @@ class MyMigration(Migration):
|
|||
return msg
|
||||
|
||||
def run(self):
|
||||
|
||||
if self.mode == "auto":
|
||||
return
|
||||
|
||||
venvs = _get_all_venvs("/opt/") + _get_all_venvs("/var/www/")
|
||||
for venv in venvs:
|
||||
|
||||
app_corresponding_to_venv = extract_app_from_venv_path(venv)
|
||||
|
||||
# Search for ignore apps
|
||||
|
|
|
@ -14,7 +14,6 @@ OLD_SETTINGS_PATH = "/etc/yunohost/settings.json"
|
|||
|
||||
|
||||
class MyMigration(Migration):
|
||||
|
||||
"Migrate old global settings to the new ConfigPanel global settings"
|
||||
|
||||
dependencies = ["migrate_to_bullseye"]
|
||||
|
|
|
@ -21,7 +21,6 @@ class MyMigration(Migration):
|
|||
|
||||
@Migration.ldap_migration
|
||||
def run(self, *args):
|
||||
|
||||
from yunohost.user import (
|
||||
user_list,
|
||||
user_info,
|
||||
|
|
|
@ -79,7 +79,6 @@ def user_permission_list(
|
|||
|
||||
permissions = {}
|
||||
for infos in permissions_infos:
|
||||
|
||||
name = infos["cn"][0]
|
||||
app = name.split(".")[0]
|
||||
|
||||
|
@ -654,7 +653,6 @@ def permission_sync_to_user():
|
|||
permissions = user_permission_list(full=True)["permissions"]
|
||||
|
||||
for permission_name, permission_infos in permissions.items():
|
||||
|
||||
# These are the users currently allowed because there's an 'inheritPermission' object corresponding to it
|
||||
currently_allowed_users = set(permission_infos["corresponding_users"])
|
||||
|
||||
|
@ -740,7 +738,6 @@ def _update_ldap_group_permission(
|
|||
update["isProtected"] = [str(protected).upper()]
|
||||
|
||||
if show_tile is not None:
|
||||
|
||||
if show_tile is True:
|
||||
if not existing_permission["url"]:
|
||||
logger.warning(
|
||||
|
@ -876,7 +873,6 @@ def _validate_and_sanitize_permission_url(url, app_base_path, app):
|
|||
raise YunohostValidationError("invalid_regex", regex=regex)
|
||||
|
||||
if url.startswith("re:"):
|
||||
|
||||
# regex without domain
|
||||
# we check for the first char after 're:'
|
||||
if url[3] in ["/", "^", "\\"]:
|
||||
|
|
|
@ -77,7 +77,6 @@ def regen_conf(
|
|||
|
||||
for category, conf_files in pending_conf.items():
|
||||
for system_path, pending_path in conf_files.items():
|
||||
|
||||
pending_conf[category][system_path] = {
|
||||
"pending_conf": pending_path,
|
||||
"diff": _get_files_diff(system_path, pending_path, True),
|
||||
|
@ -595,7 +594,6 @@ def _update_conf_hashes(category, hashes):
|
|||
|
||||
|
||||
def _force_clear_hashes(paths):
|
||||
|
||||
categories = _get_regenconf_infos()
|
||||
for path in paths:
|
||||
for category in categories.keys():
|
||||
|
@ -675,7 +673,6 @@ def _process_regen_conf(system_conf, new_conf=None, save=True):
|
|||
|
||||
|
||||
def manually_modified_files():
|
||||
|
||||
output = []
|
||||
regenconf_categories = _get_regenconf_infos()
|
||||
for category, infos in regenconf_categories.items():
|
||||
|
@ -690,7 +687,6 @@ def manually_modified_files():
|
|||
def manually_modified_files_compared_to_debian_default(
|
||||
ignore_handled_by_regenconf=False,
|
||||
):
|
||||
|
||||
# from https://serverfault.com/a/90401
|
||||
files = check_output(
|
||||
"dpkg-query -W -f='${Conffiles}\n' '*' \
|
||||
|
|
|
@ -249,12 +249,10 @@ def service_reload_or_restart(names, test_conf=True):
|
|||
services = _get_services()
|
||||
|
||||
for name in names:
|
||||
|
||||
logger.debug(f"Reloading service {name}")
|
||||
|
||||
test_conf_cmd = services.get(name, {}).get("test_conf")
|
||||
if test_conf and test_conf_cmd:
|
||||
|
||||
p = subprocess.Popen(
|
||||
test_conf_cmd,
|
||||
shell=True,
|
||||
|
@ -393,7 +391,6 @@ def _get_service_information_from_systemd(service):
|
|||
|
||||
|
||||
def _get_and_format_service_status(service, infos):
|
||||
|
||||
systemd_service = infos.get("actual_systemd_service", service)
|
||||
raw_status, raw_service = _get_service_information_from_systemd(systemd_service)
|
||||
|
||||
|
@ -414,7 +411,6 @@ def _get_and_format_service_status(service, infos):
|
|||
|
||||
# If no description was there, try to get it from the .json locales
|
||||
if not description:
|
||||
|
||||
translation_key = f"service_description_{service}"
|
||||
if m18n.key_exists(translation_key):
|
||||
description = m18n.n(translation_key)
|
||||
|
@ -521,7 +517,6 @@ def service_log(name, number=50):
|
|||
result["journalctl"] = _get_journalctl_logs(name, number).splitlines()
|
||||
|
||||
for log_path in log_list:
|
||||
|
||||
if not os.path.exists(log_path):
|
||||
continue
|
||||
|
||||
|
@ -620,7 +615,6 @@ def _run_service_command(action, service):
|
|||
|
||||
|
||||
def _give_lock(action, service, p):
|
||||
|
||||
# Depending of the action, systemctl calls the PID differently :/
|
||||
if action == "start" or action == "restart":
|
||||
systemctl_PID_name = "MainPID"
|
||||
|
@ -712,6 +706,10 @@ def _get_services():
|
|||
"category": "web",
|
||||
}
|
||||
|
||||
# Ignore metronome entirely if XMPP was disabled on all domains
|
||||
if "metronome" in services and not glob("/etc/metronome/conf.d/*.cfg.lua"):
|
||||
del services["metronome"]
|
||||
|
||||
# Remove legacy /var/log/daemon.log and /var/log/syslog from log entries
|
||||
# because they are too general. Instead, now the journalctl log is
|
||||
# returned by default which is more relevant.
|
||||
|
@ -740,7 +738,6 @@ def _save_services(services):
|
|||
diff = {}
|
||||
|
||||
for service_name, service_infos in services.items():
|
||||
|
||||
# Ignore php-fpm services, they are to be added dynamically by the core,
|
||||
# but not actually saved
|
||||
if service_name.startswith("php") and service_name.endswith("-fpm"):
|
||||
|
|
|
@ -59,7 +59,6 @@ def settings_get(key="", full=False, export=False):
|
|||
|
||||
|
||||
def settings_list(full=False):
|
||||
|
||||
settings = settings_get(full=full)
|
||||
|
||||
if full:
|
||||
|
@ -126,7 +125,6 @@ class SettingsConfigPanel(ConfigPanel):
|
|||
super().__init__("settings")
|
||||
|
||||
def _apply(self):
|
||||
|
||||
root_password = self.new_values.pop("root_password", None)
|
||||
root_password_confirm = self.new_values.pop("root_password_confirm", None)
|
||||
passwordless_sudo = self.new_values.pop("passwordless_sudo", None)
|
||||
|
@ -141,7 +139,6 @@ class SettingsConfigPanel(ConfigPanel):
|
|||
assert all(v not in self.future_values for v in self.virtual_settings)
|
||||
|
||||
if root_password and root_password.strip():
|
||||
|
||||
if root_password != root_password_confirm:
|
||||
raise YunohostValidationError("password_confirmation_not_the_same")
|
||||
|
||||
|
@ -173,7 +170,6 @@ class SettingsConfigPanel(ConfigPanel):
|
|||
raise
|
||||
|
||||
def _get_toml(self):
|
||||
|
||||
toml = super()._get_toml()
|
||||
|
||||
# Dynamic choice list for portal themes
|
||||
|
@ -187,7 +183,6 @@ class SettingsConfigPanel(ConfigPanel):
|
|||
return toml
|
||||
|
||||
def _load_current_values(self):
|
||||
|
||||
super()._load_current_values()
|
||||
|
||||
# Specific logic for those settings who are "virtual" settings
|
||||
|
@ -203,11 +198,10 @@ class SettingsConfigPanel(ConfigPanel):
|
|||
self.values["passwordless_sudo"] = "!authenticate" in ldap.search(
|
||||
"ou=sudo", "cn=admins", ["sudoOption"]
|
||||
)[0].get("sudoOption", [])
|
||||
except:
|
||||
except Exception:
|
||||
self.values["passwordless_sudo"] = False
|
||||
|
||||
def get(self, key="", mode="classic"):
|
||||
|
||||
result = super().get(key=key, mode=mode)
|
||||
|
||||
if mode == "full":
|
||||
|
|
|
@ -51,7 +51,6 @@ old_translate = moulinette.core.Translator.translate
|
|||
|
||||
|
||||
def new_translate(self, key, *args, **kwargs):
|
||||
|
||||
if key not in self._translations[self.default_locale].keys():
|
||||
raise KeyError("Unable to retrieve key %s for default locale !" % key)
|
||||
|
||||
|
@ -67,7 +66,6 @@ moulinette.core.Translator.translate = new_translate
|
|||
|
||||
|
||||
def pytest_cmdline_main(config):
|
||||
|
||||
import sys
|
||||
|
||||
sys.path.insert(0, "/usr/lib/moulinette/")
|
||||
|
@ -76,7 +74,6 @@ def pytest_cmdline_main(config):
|
|||
yunohost.init(debug=config.option.yunodebug)
|
||||
|
||||
class DummyInterface:
|
||||
|
||||
type = "cli"
|
||||
|
||||
def prompt(self, *args, **kwargs):
|
||||
|
|
|
@ -44,7 +44,6 @@ class AnyStringWith(str):
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
# Clear apps catalog cache
|
||||
shutil.rmtree(APPS_CATALOG_CACHE, ignore_errors=True)
|
||||
|
||||
|
@ -54,7 +53,6 @@ def setup_function(function):
|
|||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
# Clear apps catalog cache
|
||||
# Otherwise when using apps stuff after running the test,
|
||||
# we'll still have the dummy unusable list
|
||||
|
@ -67,7 +65,6 @@ def teardown_function(function):
|
|||
|
||||
|
||||
def test_apps_catalog_init(mocker):
|
||||
|
||||
# Cache is empty
|
||||
assert not glob.glob(APPS_CATALOG_CACHE + "/*")
|
||||
# Conf doesn't exist yet
|
||||
|
@ -91,7 +88,6 @@ def test_apps_catalog_init(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_emptylist():
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
|
@ -104,7 +100,6 @@ def test_apps_catalog_emptylist():
|
|||
|
||||
|
||||
def test_apps_catalog_update_nominal(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
|
@ -113,7 +108,6 @@ def test_apps_catalog_update_nominal(mocker):
|
|||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
_actual_apps_catalog_api_url,
|
||||
# Mock the server response with a dummy apps catalog
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
|
@ -139,12 +133,10 @@ def test_apps_catalog_update_nominal(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_update_404(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# 404 error
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, status_code=404)
|
||||
|
||||
|
@ -155,12 +147,10 @@ def test_apps_catalog_update_404(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_update_timeout(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Timeout
|
||||
m.register_uri(
|
||||
"GET", APPS_CATALOG_DEFAULT_URL_FULL, exc=requests.exceptions.ConnectTimeout
|
||||
|
@ -173,12 +163,10 @@ def test_apps_catalog_update_timeout(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_update_sslerror(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# SSL error
|
||||
m.register_uri(
|
||||
"GET", APPS_CATALOG_DEFAULT_URL_FULL, exc=requests.exceptions.SSLError
|
||||
|
@ -191,12 +179,10 @@ def test_apps_catalog_update_sslerror(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_update_corrupted(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Corrupted json
|
||||
m.register_uri(
|
||||
"GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG[:-2]
|
||||
|
@ -209,7 +195,6 @@ def test_apps_catalog_update_corrupted(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_load_with_empty_cache(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
|
@ -218,7 +203,6 @@ def test_apps_catalog_load_with_empty_cache(mocker):
|
|||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy apps catalog
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
|
||||
|
@ -237,7 +221,6 @@ def test_apps_catalog_load_with_empty_cache(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_load_with_conflicts_between_lists(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
|
@ -253,7 +236,6 @@ def test_apps_catalog_load_with_conflicts_between_lists(mocker):
|
|||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy apps catalog
|
||||
# + the same apps catalog for the second list
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
|
@ -277,13 +259,11 @@ def test_apps_catalog_load_with_conflicts_between_lists(mocker):
|
|||
|
||||
|
||||
def test_apps_catalog_load_with_oudated_api_version(mocker):
|
||||
|
||||
# Initialize ...
|
||||
_initialize_apps_catalog_system()
|
||||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
mocker.spy(m18n, "n")
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
_update_apps_catalog()
|
||||
|
@ -300,7 +280,6 @@ def test_apps_catalog_load_with_oudated_api_version(mocker):
|
|||
|
||||
# Update
|
||||
with requests_mock.Mocker() as m:
|
||||
|
||||
# Mock the server response with a dummy apps catalog
|
||||
m.register_uri("GET", APPS_CATALOG_DEFAULT_URL_FULL, text=DUMMY_APP_CATALOG)
|
||||
|
||||
|
|
|
@ -25,17 +25,14 @@ from yunohost.utils.error import YunohostError, YunohostValidationError
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def clean():
|
||||
|
||||
# Make sure we have a ssowat
|
||||
os.system("mkdir -p /etc/ssowat/")
|
||||
app_ssowatconf()
|
||||
|
@ -43,7 +40,6 @@ def clean():
|
|||
test_apps = ["config_app", "legacy_app"]
|
||||
|
||||
for test_app in test_apps:
|
||||
|
||||
if _is_installed(test_app):
|
||||
app_remove(test_app)
|
||||
|
||||
|
@ -66,7 +62,6 @@ def clean():
|
|||
|
||||
@pytest.fixture()
|
||||
def legacy_app(request):
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
|
||||
app_install(
|
||||
|
@ -85,7 +80,6 @@ def legacy_app(request):
|
|||
|
||||
@pytest.fixture()
|
||||
def config_app(request):
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "config_app_ynh"),
|
||||
args="",
|
||||
|
@ -101,7 +95,6 @@ def config_app(request):
|
|||
|
||||
|
||||
def test_app_config_get(config_app):
|
||||
|
||||
user_create("alice", _get_maindomain(), "test123Ynh", fullname="Alice White")
|
||||
|
||||
assert isinstance(app_config_get(config_app), dict)
|
||||
|
@ -115,13 +108,11 @@ def test_app_config_get(config_app):
|
|||
|
||||
|
||||
def test_app_config_nopanel(legacy_app):
|
||||
|
||||
with pytest.raises(YunohostValidationError):
|
||||
app_config_get(legacy_app)
|
||||
|
||||
|
||||
def test_app_config_get_nonexistentstuff(config_app):
|
||||
|
||||
with pytest.raises(YunohostValidationError):
|
||||
app_config_get("nonexistent")
|
||||
|
||||
|
@ -140,7 +131,6 @@ def test_app_config_get_nonexistentstuff(config_app):
|
|||
|
||||
|
||||
def test_app_config_regular_setting(config_app):
|
||||
|
||||
assert app_config_get(config_app, "main.components.boolean") == 0
|
||||
|
||||
app_config_set(config_app, "main.components.boolean", "no")
|
||||
|
@ -160,7 +150,6 @@ def test_app_config_regular_setting(config_app):
|
|||
|
||||
|
||||
def test_app_config_bind_on_file(config_app):
|
||||
|
||||
# c.f. conf/test.php in the config app
|
||||
assert '$arg5= "Arg5 value";' in read_file("/var/www/config_app/test.php")
|
||||
assert app_config_get(config_app, "bind.variable.arg5") == "Arg5 value"
|
||||
|
@ -184,7 +173,6 @@ def test_app_config_bind_on_file(config_app):
|
|||
|
||||
|
||||
def test_app_config_custom_validator(config_app):
|
||||
|
||||
# c.f. the config script
|
||||
# arg8 is a password that must be at least 8 chars
|
||||
assert not os.path.exists("/var/www/config_app/password")
|
||||
|
@ -198,7 +186,6 @@ def test_app_config_custom_validator(config_app):
|
|||
|
||||
|
||||
def test_app_config_custom_set(config_app):
|
||||
|
||||
assert not os.path.exists("/var/www/config_app/password")
|
||||
assert app_setting(config_app, "arg8") is None
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ dummyfile = "/tmp/dummyappresource-testapp"
|
|||
|
||||
|
||||
class DummyAppResource(AppResource):
|
||||
|
||||
type = "dummy"
|
||||
|
||||
default_properties = {
|
||||
|
@ -26,14 +25,12 @@ class DummyAppResource(AppResource):
|
|||
}
|
||||
|
||||
def provision_or_update(self, context):
|
||||
|
||||
open(self.file, "w").write(self.content)
|
||||
|
||||
if self.content == "forbiddenvalue":
|
||||
raise Exception("Emeged you used the forbidden value!1!£&")
|
||||
|
||||
def deprovision(self, context):
|
||||
|
||||
os.system(f"rm -f {self.file}")
|
||||
|
||||
|
||||
|
@ -41,7 +38,6 @@ AppResourceClassesByType["dummy"] = DummyAppResource
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
os.system("mkdir /etc/yunohost/apps/testapp")
|
||||
|
@ -51,12 +47,10 @@ def setup_function(function):
|
|||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def clean():
|
||||
|
||||
os.system(f"rm -f {dummyfile}")
|
||||
os.system("rm -rf /etc/yunohost/apps/testapp")
|
||||
os.system("rm -rf /var/www/testapp")
|
||||
|
@ -70,7 +64,6 @@ def clean():
|
|||
|
||||
|
||||
def test_provision_dummy():
|
||||
|
||||
current = {"resources": {}}
|
||||
wanted = {"resources": {"dummy": {}}}
|
||||
|
||||
|
@ -82,7 +75,6 @@ def test_provision_dummy():
|
|||
|
||||
|
||||
def test_deprovision_dummy():
|
||||
|
||||
current = {"resources": {"dummy": {}}}
|
||||
wanted = {"resources": {}}
|
||||
|
||||
|
@ -96,7 +88,6 @@ def test_deprovision_dummy():
|
|||
|
||||
|
||||
def test_provision_dummy_nondefaultvalue():
|
||||
|
||||
current = {"resources": {}}
|
||||
wanted = {"resources": {"dummy": {"content": "bar"}}}
|
||||
|
||||
|
@ -108,7 +99,6 @@ def test_provision_dummy_nondefaultvalue():
|
|||
|
||||
|
||||
def test_update_dummy():
|
||||
|
||||
current = {"resources": {"dummy": {}}}
|
||||
wanted = {"resources": {"dummy": {"content": "bar"}}}
|
||||
|
||||
|
@ -122,7 +112,6 @@ def test_update_dummy():
|
|||
|
||||
|
||||
def test_update_dummy_failwithrollback():
|
||||
|
||||
current = {"resources": {"dummy": {}}}
|
||||
wanted = {"resources": {"dummy": {"content": "forbiddenvalue"}}}
|
||||
|
||||
|
@ -137,7 +126,6 @@ def test_update_dummy_failwithrollback():
|
|||
|
||||
|
||||
def test_resource_system_user():
|
||||
|
||||
r = AppResourceClassesByType["system_user"]
|
||||
|
||||
conf = {}
|
||||
|
@ -161,7 +149,6 @@ def test_resource_system_user():
|
|||
|
||||
|
||||
def test_resource_install_dir():
|
||||
|
||||
r = AppResourceClassesByType["install_dir"]
|
||||
conf = {"owner": "nobody:rx", "group": "nogroup:rx"}
|
||||
|
||||
|
@ -196,7 +183,6 @@ def test_resource_install_dir():
|
|||
|
||||
|
||||
def test_resource_data_dir():
|
||||
|
||||
r = AppResourceClassesByType["data_dir"]
|
||||
conf = {"owner": "nobody:rx", "group": "nogroup:rx"}
|
||||
|
||||
|
@ -228,7 +214,6 @@ def test_resource_data_dir():
|
|||
|
||||
|
||||
def test_resource_ports():
|
||||
|
||||
r = AppResourceClassesByType["ports"]
|
||||
conf = {}
|
||||
|
||||
|
@ -244,7 +229,6 @@ def test_resource_ports():
|
|||
|
||||
|
||||
def test_resource_ports_several():
|
||||
|
||||
r = AppResourceClassesByType["ports"]
|
||||
conf = {"main": {"default": 12345}, "foobar": {"default": 23456}}
|
||||
|
||||
|
@ -263,7 +247,6 @@ def test_resource_ports_several():
|
|||
|
||||
|
||||
def test_resource_ports_firewall():
|
||||
|
||||
r = AppResourceClassesByType["ports"]
|
||||
conf = {"main": {"default": 12345}}
|
||||
|
||||
|
@ -283,7 +266,6 @@ def test_resource_ports_firewall():
|
|||
|
||||
|
||||
def test_resource_database():
|
||||
|
||||
r = AppResourceClassesByType["database"]
|
||||
conf = {"type": "mysql"}
|
||||
|
||||
|
@ -308,7 +290,6 @@ def test_resource_database():
|
|||
|
||||
|
||||
def test_resource_apt():
|
||||
|
||||
r = AppResourceClassesByType["apt"]
|
||||
conf = {
|
||||
"packages": "nyancat, sl",
|
||||
|
@ -356,7 +337,6 @@ def test_resource_apt():
|
|||
|
||||
|
||||
def test_resource_permissions():
|
||||
|
||||
maindomain = _get_maindomain()
|
||||
os.system(f"echo 'domain: {maindomain}' >> /etc/yunohost/apps/testapp/settings.yml")
|
||||
os.system("echo 'path: /testapp' >> /etc/yunohost/apps/testapp/settings.yml")
|
||||
|
|
|
@ -28,17 +28,14 @@ from yunohost.permission import user_permission_list, permission_delete
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def clean():
|
||||
|
||||
# Make sure we have a ssowat
|
||||
os.system("mkdir -p /etc/ssowat/")
|
||||
app_ssowatconf()
|
||||
|
@ -53,7 +50,6 @@ def clean():
|
|||
]
|
||||
|
||||
for test_app in test_apps:
|
||||
|
||||
if _is_installed(test_app):
|
||||
app_remove(test_app)
|
||||
|
||||
|
@ -95,7 +91,6 @@ def check_permission_for_apps_call():
|
|||
|
||||
@pytest.fixture(scope="module")
|
||||
def secondary_domain(request):
|
||||
|
||||
if "example.test" not in domain_list()["domains"]:
|
||||
domain_add("example.test")
|
||||
|
||||
|
@ -113,7 +108,6 @@ def secondary_domain(request):
|
|||
|
||||
|
||||
def app_expected_files(domain, app):
|
||||
|
||||
yield "/etc/nginx/conf.d/{}.d/{}.conf".format(domain, app)
|
||||
if app.startswith("legacy_app"):
|
||||
yield "/var/www/%s/index.html" % app
|
||||
|
@ -127,21 +121,18 @@ def app_expected_files(domain, app):
|
|||
|
||||
|
||||
def app_is_installed(domain, app):
|
||||
|
||||
return _is_installed(app) and all(
|
||||
os.path.exists(f) for f in app_expected_files(domain, app)
|
||||
)
|
||||
|
||||
|
||||
def app_is_not_installed(domain, app):
|
||||
|
||||
return not _is_installed(app) and not all(
|
||||
os.path.exists(f) for f in app_expected_files(domain, app)
|
||||
)
|
||||
|
||||
|
||||
def app_is_exposed_on_http(domain, path, message_in_page):
|
||||
|
||||
try:
|
||||
r = requests.get(
|
||||
"https://127.0.0.1" + path + "/",
|
||||
|
@ -155,7 +146,6 @@ def app_is_exposed_on_http(domain, path, message_in_page):
|
|||
|
||||
|
||||
def install_legacy_app(domain, path, public=True):
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "legacy_app_ynh"),
|
||||
args="domain={}&path={}&is_public={}".format(domain, path, 1 if public else 0),
|
||||
|
@ -164,7 +154,6 @@ def install_legacy_app(domain, path, public=True):
|
|||
|
||||
|
||||
def install_manifestv2_app(domain, path, public=True):
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "manifestv2_app_ynh"),
|
||||
args="domain={}&path={}&init_main_permission={}".format(
|
||||
|
@ -175,7 +164,6 @@ def install_manifestv2_app(domain, path, public=True):
|
|||
|
||||
|
||||
def install_full_domain_app(domain):
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "full_domain_app_ynh"),
|
||||
args="domain=%s" % domain,
|
||||
|
@ -184,7 +172,6 @@ def install_full_domain_app(domain):
|
|||
|
||||
|
||||
def install_break_yo_system(domain, breakwhat):
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "break_yo_system_ynh"),
|
||||
args="domain={}&breakwhat={}".format(domain, breakwhat),
|
||||
|
@ -193,7 +180,6 @@ def install_break_yo_system(domain, breakwhat):
|
|||
|
||||
|
||||
def test_legacy_app_install_main_domain():
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
|
||||
install_legacy_app(main_domain, "/legacy")
|
||||
|
@ -213,7 +199,6 @@ def test_legacy_app_install_main_domain():
|
|||
|
||||
|
||||
def test_legacy_app_manifest_preinstall():
|
||||
|
||||
m = app_manifest(os.path.join(get_test_apps_dir(), "legacy_app_ynh"))
|
||||
# v1 manifesto are expected to have been autoconverted to v2
|
||||
|
||||
|
@ -231,7 +216,6 @@ def test_legacy_app_manifest_preinstall():
|
|||
|
||||
|
||||
def test_manifestv2_app_manifest_preinstall():
|
||||
|
||||
m = app_manifest(os.path.join(get_test_apps_dir(), "manifestv2_app_ynh"))
|
||||
|
||||
assert "id" in m
|
||||
|
@ -258,7 +242,6 @@ def test_manifestv2_app_manifest_preinstall():
|
|||
|
||||
|
||||
def test_manifestv2_app_install_main_domain():
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
|
||||
install_manifestv2_app(main_domain, "/manifestv2")
|
||||
|
@ -278,7 +261,6 @@ def test_manifestv2_app_install_main_domain():
|
|||
|
||||
|
||||
def test_manifestv2_app_info_postinstall():
|
||||
|
||||
main_domain = _get_maindomain()
|
||||
install_manifestv2_app(main_domain, "/manifestv2")
|
||||
m = app_info("manifestv2_app", full=True)["manifest"]
|
||||
|
@ -308,13 +290,11 @@ def test_manifestv2_app_info_postinstall():
|
|||
|
||||
|
||||
def test_manifestv2_app_info_preupgrade(monkeypatch):
|
||||
|
||||
manifest = app_manifest(os.path.join(get_test_apps_dir(), "manifestv2_app_ynh"))
|
||||
|
||||
from yunohost.app_catalog import _load_apps_catalog as original_load_apps_catalog
|
||||
|
||||
def custom_load_apps_catalog(*args, **kwargs):
|
||||
|
||||
res = original_load_apps_catalog(*args, **kwargs)
|
||||
res["apps"]["manifestv2_app"] = {
|
||||
"id": "manifestv2_app",
|
||||
|
@ -372,7 +352,6 @@ def test_app_from_catalog():
|
|||
|
||||
|
||||
def test_legacy_app_install_secondary_domain(secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/legacy")
|
||||
|
||||
assert app_is_installed(secondary_domain, "legacy_app")
|
||||
|
@ -384,7 +363,6 @@ def test_legacy_app_install_secondary_domain(secondary_domain):
|
|||
|
||||
|
||||
def test_legacy_app_install_secondary_domain_on_root(secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/")
|
||||
|
||||
app_map_ = app_map(raw=True)
|
||||
|
@ -402,7 +380,6 @@ def test_legacy_app_install_secondary_domain_on_root(secondary_domain):
|
|||
|
||||
|
||||
def test_legacy_app_install_private(secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/legacy", public=False)
|
||||
|
||||
assert app_is_installed(secondary_domain, "legacy_app")
|
||||
|
@ -416,7 +393,6 @@ def test_legacy_app_install_private(secondary_domain):
|
|||
|
||||
|
||||
def test_legacy_app_install_unknown_domain(mocker):
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
with message(mocker, "app_argument_invalid"):
|
||||
install_legacy_app("whatever.nope", "/legacy")
|
||||
|
@ -425,7 +401,6 @@ def test_legacy_app_install_unknown_domain(mocker):
|
|||
|
||||
|
||||
def test_legacy_app_install_multiple_instances(secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/foo")
|
||||
install_legacy_app(secondary_domain, "/bar")
|
||||
|
||||
|
@ -447,7 +422,6 @@ def test_legacy_app_install_multiple_instances(secondary_domain):
|
|||
|
||||
|
||||
def test_legacy_app_install_path_unavailable(mocker, secondary_domain):
|
||||
|
||||
# These will be removed in teardown
|
||||
install_legacy_app(secondary_domain, "/legacy")
|
||||
|
||||
|
@ -460,7 +434,6 @@ def test_legacy_app_install_path_unavailable(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_legacy_app_install_with_nginx_down(mocker, secondary_domain):
|
||||
|
||||
os.system("systemctl stop nginx")
|
||||
|
||||
with raiseYunohostError(
|
||||
|
@ -470,7 +443,6 @@ def test_legacy_app_install_with_nginx_down(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_legacy_app_failed_install(mocker, secondary_domain):
|
||||
|
||||
# This will conflict with the folder that the app
|
||||
# attempts to create, making the install fail
|
||||
mkdir("/var/www/legacy_app/", 0o750)
|
||||
|
@ -483,7 +455,6 @@ def test_legacy_app_failed_install(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_legacy_app_failed_remove(mocker, secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/legacy")
|
||||
|
||||
# The remove script runs with set -eu and attempt to remove this
|
||||
|
@ -503,14 +474,12 @@ def test_legacy_app_failed_remove(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_full_domain_app(secondary_domain):
|
||||
|
||||
install_full_domain_app(secondary_domain)
|
||||
|
||||
assert app_is_exposed_on_http(secondary_domain, "/", "This is a dummy app")
|
||||
|
||||
|
||||
def test_full_domain_app_with_conflicts(mocker, secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/legacy")
|
||||
|
||||
with raiseYunohostError(mocker, "app_full_domain_unavailable"):
|
||||
|
@ -518,7 +487,6 @@ def test_full_domain_app_with_conflicts(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_systemfuckedup_during_app_install(mocker, secondary_domain):
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
with message(mocker, "app_install_failed"):
|
||||
with message(mocker, "app_action_broke_system"):
|
||||
|
@ -528,7 +496,6 @@ def test_systemfuckedup_during_app_install(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_systemfuckedup_during_app_remove(mocker, secondary_domain):
|
||||
|
||||
install_break_yo_system(secondary_domain, breakwhat="remove")
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
|
@ -540,7 +507,6 @@ def test_systemfuckedup_during_app_remove(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_systemfuckedup_during_app_install_and_remove(mocker, secondary_domain):
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
with message(mocker, "app_install_failed"):
|
||||
with message(mocker, "app_action_broke_system"):
|
||||
|
@ -550,7 +516,6 @@ def test_systemfuckedup_during_app_install_and_remove(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_systemfuckedup_during_app_upgrade(mocker, secondary_domain):
|
||||
|
||||
install_break_yo_system(secondary_domain, breakwhat="upgrade")
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
|
@ -562,7 +527,6 @@ def test_systemfuckedup_during_app_upgrade(mocker, secondary_domain):
|
|||
|
||||
|
||||
def test_failed_multiple_app_upgrade(mocker, secondary_domain):
|
||||
|
||||
install_legacy_app(secondary_domain, "/legacy")
|
||||
install_break_yo_system(secondary_domain, breakwhat="upgrade")
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ maindomain = _get_maindomain()
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
try:
|
||||
app_remove("register_url_app")
|
||||
except Exception:
|
||||
|
@ -26,7 +25,6 @@ def setup_function(function):
|
|||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
try:
|
||||
app_remove("register_url_app")
|
||||
except Exception:
|
||||
|
@ -34,7 +32,6 @@ def teardown_function(function):
|
|||
|
||||
|
||||
def test_parse_app_instance_name():
|
||||
|
||||
assert _parse_app_instance_name("yolo") == ("yolo", 1)
|
||||
assert _parse_app_instance_name("yolo1") == ("yolo1", 1)
|
||||
assert _parse_app_instance_name("yolo__0") == ("yolo__0", 1)
|
||||
|
@ -86,7 +83,6 @@ def test_repo_url_definition():
|
|||
|
||||
|
||||
def test_urlavailable():
|
||||
|
||||
# Except the maindomain/macnuggets to be available
|
||||
assert domain_url_available(maindomain, "/macnuggets")
|
||||
|
||||
|
@ -96,7 +92,6 @@ def test_urlavailable():
|
|||
|
||||
|
||||
def test_registerurl():
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "register_url_app_ynh"),
|
||||
args="domain={}&path={}".format(maindomain, "/urlregisterapp"),
|
||||
|
@ -115,7 +110,6 @@ def test_registerurl():
|
|||
|
||||
|
||||
def test_registerurl_baddomain():
|
||||
|
||||
with pytest.raises(YunohostError):
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "register_url_app_ynh"),
|
||||
|
|
|
@ -30,7 +30,6 @@ maindomain = ""
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
global maindomain
|
||||
maindomain = _get_maindomain()
|
||||
|
||||
|
@ -89,7 +88,6 @@ def setup_function(function):
|
|||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
assert tmp_backup_directory_is_empty()
|
||||
|
||||
reset_ssowat_conf()
|
||||
|
@ -133,7 +131,6 @@ def check_permission_for_apps_call():
|
|||
|
||||
|
||||
def app_is_installed(app):
|
||||
|
||||
if app == "permissions_app":
|
||||
return _is_installed(app)
|
||||
|
||||
|
@ -147,7 +144,6 @@ def app_is_installed(app):
|
|||
|
||||
|
||||
def backup_test_dependencies_are_met():
|
||||
|
||||
# Dummy test apps (or backup archives)
|
||||
assert os.path.exists(
|
||||
os.path.join(get_test_apps_dir(), "backup_wordpress_from_4p2")
|
||||
|
@ -161,7 +157,6 @@ def backup_test_dependencies_are_met():
|
|||
|
||||
|
||||
def tmp_backup_directory_is_empty():
|
||||
|
||||
if not os.path.exists("/home/yunohost.backup/tmp/"):
|
||||
return True
|
||||
else:
|
||||
|
@ -169,7 +164,6 @@ def tmp_backup_directory_is_empty():
|
|||
|
||||
|
||||
def clean_tmp_backup_directory():
|
||||
|
||||
if tmp_backup_directory_is_empty():
|
||||
return
|
||||
|
||||
|
@ -191,27 +185,23 @@ def clean_tmp_backup_directory():
|
|||
|
||||
|
||||
def reset_ssowat_conf():
|
||||
|
||||
# Make sure we have a ssowat
|
||||
os.system("mkdir -p /etc/ssowat/")
|
||||
app_ssowatconf()
|
||||
|
||||
|
||||
def delete_all_backups():
|
||||
|
||||
for archive in backup_list()["archives"]:
|
||||
backup_delete(archive)
|
||||
|
||||
|
||||
def uninstall_test_apps_if_needed():
|
||||
|
||||
for app in ["legacy_app", "backup_recommended_app", "wordpress", "permissions_app"]:
|
||||
if _is_installed(app):
|
||||
app_remove(app)
|
||||
|
||||
|
||||
def install_app(app, path, additionnal_args=""):
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), app),
|
||||
args="domain={}&path={}{}".format(maindomain, path, additionnal_args),
|
||||
|
@ -220,7 +210,6 @@ def install_app(app, path, additionnal_args=""):
|
|||
|
||||
|
||||
def add_archive_wordpress_from_4p2():
|
||||
|
||||
os.system("mkdir -p /home/yunohost.backup/archives")
|
||||
|
||||
os.system(
|
||||
|
@ -231,7 +220,6 @@ def add_archive_wordpress_from_4p2():
|
|||
|
||||
|
||||
def add_archive_system_from_4p2():
|
||||
|
||||
os.system("mkdir -p /home/yunohost.backup/archives")
|
||||
|
||||
os.system(
|
||||
|
@ -247,7 +235,6 @@ def add_archive_system_from_4p2():
|
|||
|
||||
|
||||
def test_backup_only_ldap(mocker):
|
||||
|
||||
# Create the backup
|
||||
with message(mocker, "backup_created"):
|
||||
backup_create(system=["conf_ldap"], apps=None)
|
||||
|
@ -262,7 +249,6 @@ def test_backup_only_ldap(mocker):
|
|||
|
||||
|
||||
def test_backup_system_part_that_does_not_exists(mocker):
|
||||
|
||||
# Create the backup
|
||||
with message(mocker, "backup_hook_unknown", hook="doesnt_exist"):
|
||||
with raiseYunohostError(mocker, "backup_nothings_done"):
|
||||
|
@ -275,7 +261,6 @@ def test_backup_system_part_that_does_not_exists(mocker):
|
|||
|
||||
|
||||
def test_backup_and_restore_all_sys(mocker):
|
||||
|
||||
# Create the backup
|
||||
with message(mocker, "backup_created"):
|
||||
backup_create(system=[], apps=None)
|
||||
|
@ -309,7 +294,6 @@ def test_backup_and_restore_all_sys(mocker):
|
|||
|
||||
@pytest.mark.with_system_archive_from_4p2
|
||||
def test_restore_system_from_Ynh4p2(monkeypatch, mocker):
|
||||
|
||||
# Backup current system
|
||||
with message(mocker, "backup_created"):
|
||||
backup_create(system=[], apps=None)
|
||||
|
@ -337,7 +321,6 @@ def test_restore_system_from_Ynh4p2(monkeypatch, mocker):
|
|||
@pytest.mark.with_backup_recommended_app_installed
|
||||
def test_backup_script_failure_handling(monkeypatch, mocker):
|
||||
def custom_hook_exec(name, *args, **kwargs):
|
||||
|
||||
if os.path.basename(name).startswith("backup_"):
|
||||
raise Exception
|
||||
else:
|
||||
|
@ -373,7 +356,6 @@ def test_backup_not_enough_free_space(monkeypatch, mocker):
|
|||
|
||||
|
||||
def test_backup_app_not_installed(mocker):
|
||||
|
||||
assert not _is_installed("wordpress")
|
||||
|
||||
with message(mocker, "unbackup_app", app="wordpress"):
|
||||
|
@ -383,7 +365,6 @@ def test_backup_app_not_installed(mocker):
|
|||
|
||||
@pytest.mark.with_backup_recommended_app_installed
|
||||
def test_backup_app_with_no_backup_script(mocker):
|
||||
|
||||
backup_script = "/etc/yunohost/apps/backup_recommended_app/scripts/backup"
|
||||
os.system("rm %s" % backup_script)
|
||||
assert not os.path.exists(backup_script)
|
||||
|
@ -397,7 +378,6 @@ def test_backup_app_with_no_backup_script(mocker):
|
|||
|
||||
@pytest.mark.with_backup_recommended_app_installed
|
||||
def test_backup_app_with_no_restore_script(mocker):
|
||||
|
||||
restore_script = "/etc/yunohost/apps/backup_recommended_app/scripts/restore"
|
||||
os.system("rm %s" % restore_script)
|
||||
assert not os.path.exists(restore_script)
|
||||
|
@ -413,7 +393,6 @@ def test_backup_app_with_no_restore_script(mocker):
|
|||
|
||||
@pytest.mark.clean_opt_dir
|
||||
def test_backup_with_different_output_directory(mocker):
|
||||
|
||||
# Create the backup
|
||||
with message(mocker, "backup_created"):
|
||||
backup_create(
|
||||
|
@ -436,7 +415,6 @@ def test_backup_with_different_output_directory(mocker):
|
|||
|
||||
@pytest.mark.clean_opt_dir
|
||||
def test_backup_using_copy_method(mocker):
|
||||
|
||||
# Create the backup
|
||||
with message(mocker, "backup_created"):
|
||||
backup_create(
|
||||
|
@ -458,7 +436,6 @@ def test_backup_using_copy_method(mocker):
|
|||
@pytest.mark.with_wordpress_archive_from_4p2
|
||||
@pytest.mark.with_custom_domain("yolo.test")
|
||||
def test_restore_app_wordpress_from_Ynh4p2(mocker):
|
||||
|
||||
with message(mocker, "restore_complete"):
|
||||
backup_restore(
|
||||
system=None, name=backup_list()["archives"][0], apps=["wordpress"]
|
||||
|
@ -507,7 +484,6 @@ def test_restore_app_not_enough_free_space(monkeypatch, mocker):
|
|||
|
||||
@pytest.mark.with_wordpress_archive_from_4p2
|
||||
def test_restore_app_not_in_backup(mocker):
|
||||
|
||||
assert not _is_installed("wordpress")
|
||||
assert not _is_installed("yoloswag")
|
||||
|
||||
|
@ -524,7 +500,6 @@ def test_restore_app_not_in_backup(mocker):
|
|||
@pytest.mark.with_wordpress_archive_from_4p2
|
||||
@pytest.mark.with_custom_domain("yolo.test")
|
||||
def test_restore_app_already_installed(mocker):
|
||||
|
||||
assert not _is_installed("wordpress")
|
||||
|
||||
with message(mocker, "restore_complete"):
|
||||
|
@ -544,25 +519,21 @@ def test_restore_app_already_installed(mocker):
|
|||
|
||||
@pytest.mark.with_legacy_app_installed
|
||||
def test_backup_and_restore_legacy_app(mocker):
|
||||
|
||||
_test_backup_and_restore_app(mocker, "legacy_app")
|
||||
|
||||
|
||||
@pytest.mark.with_backup_recommended_app_installed
|
||||
def test_backup_and_restore_recommended_app(mocker):
|
||||
|
||||
_test_backup_and_restore_app(mocker, "backup_recommended_app")
|
||||
|
||||
|
||||
@pytest.mark.with_backup_recommended_app_installed_with_ynh_restore
|
||||
def test_backup_and_restore_with_ynh_restore(mocker):
|
||||
|
||||
_test_backup_and_restore_app(mocker, "backup_recommended_app")
|
||||
|
||||
|
||||
@pytest.mark.with_permission_app_installed
|
||||
def test_backup_and_restore_permission_app(mocker):
|
||||
|
||||
res = user_permission_list(full=True)["permissions"]
|
||||
assert "permissions_app.main" in res
|
||||
assert "permissions_app.admin" in res
|
||||
|
@ -593,7 +564,6 @@ def test_backup_and_restore_permission_app(mocker):
|
|||
|
||||
|
||||
def _test_backup_and_restore_app(mocker, app):
|
||||
|
||||
# Create a backup of this app
|
||||
with message(mocker, "backup_created"):
|
||||
backup_create(system=None, apps=[app])
|
||||
|
@ -628,7 +598,6 @@ def _test_backup_and_restore_app(mocker, app):
|
|||
|
||||
|
||||
def test_restore_archive_with_no_json(mocker):
|
||||
|
||||
# Create a backup with no info.json associated
|
||||
os.system("touch /tmp/afile")
|
||||
os.system("tar -cvf /home/yunohost.backup/archives/badbackup.tar /tmp/afile")
|
||||
|
@ -641,7 +610,6 @@ def test_restore_archive_with_no_json(mocker):
|
|||
|
||||
@pytest.mark.with_wordpress_archive_from_4p2
|
||||
def test_restore_archive_with_bad_archive(mocker):
|
||||
|
||||
# Break the archive
|
||||
os.system(
|
||||
"head -n 1000 /home/yunohost.backup/archives/backup_wordpress_from_4p2.tar > /home/yunohost.backup/archives/backup_wordpress_from_4p2_bad.tar"
|
||||
|
@ -656,7 +624,6 @@ def test_restore_archive_with_bad_archive(mocker):
|
|||
|
||||
|
||||
def test_restore_archive_with_custom_hook(mocker):
|
||||
|
||||
custom_restore_hook_folder = os.path.join(CUSTOM_HOOK_FOLDER, "restore")
|
||||
os.system("touch %s/99-yolo" % custom_restore_hook_folder)
|
||||
|
||||
|
|
|
@ -12,12 +12,10 @@ from yunohost.dns import (
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
|
@ -76,7 +74,6 @@ def example_domain():
|
|||
|
||||
|
||||
def test_domain_dns_suggest(example_domain):
|
||||
|
||||
assert _build_dns_conf(example_domain)
|
||||
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ TEST_DOMAINS = ["example.tld", "sub.example.tld", "other-example.com"]
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
# Save domain list in variable to avoid multiple calls to domain_list()
|
||||
domains = domain_list()["domains"]
|
||||
|
||||
|
@ -52,7 +51,6 @@ def setup_function(function):
|
|||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
|
@ -102,7 +100,6 @@ def test_domain_config_get_default():
|
|||
|
||||
|
||||
def test_domain_config_get_export():
|
||||
|
||||
assert domain_config_get(TEST_DOMAINS[0], export=True)["xmpp"] == 1
|
||||
assert domain_config_get(TEST_DOMAINS[1], export=True)["xmpp"] == 0
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ from moulinette.core import MoulinetteError
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
for u in user_list()["users"]:
|
||||
user_delete(u, purge=True)
|
||||
|
||||
|
@ -24,7 +23,6 @@ def setup_function(function):
|
|||
|
||||
|
||||
def teardown_function():
|
||||
|
||||
os.system("systemctl is-active slapd >/dev/null || systemctl start slapd; sleep 5")
|
||||
|
||||
for u in user_list()["users"]:
|
||||
|
@ -36,7 +34,6 @@ def test_authenticate():
|
|||
|
||||
|
||||
def test_authenticate_with_no_user():
|
||||
|
||||
with pytest.raises(MoulinetteError):
|
||||
LDAPAuth().authenticate_credentials(credentials="Yunohost")
|
||||
|
||||
|
@ -45,7 +42,6 @@ def test_authenticate_with_no_user():
|
|||
|
||||
|
||||
def test_authenticate_with_user_who_is_not_admin():
|
||||
|
||||
with pytest.raises(MoulinetteError) as exception:
|
||||
LDAPAuth().authenticate_credentials(credentials="bob:test123Ynh")
|
||||
|
||||
|
@ -70,7 +66,6 @@ def test_authenticate_server_down(mocker):
|
|||
|
||||
|
||||
def test_authenticate_change_password():
|
||||
|
||||
LDAPAuth().authenticate_credentials(credentials="alice:Yunohost")
|
||||
|
||||
user_update("alice", change_password="plopette")
|
||||
|
|
|
@ -354,7 +354,6 @@ def check_permission_for_apps():
|
|||
|
||||
|
||||
def can_access_webpage(webpath, logged_as=None):
|
||||
|
||||
webpath = webpath.rstrip("/")
|
||||
sso_url = "https://" + maindomain + "/yunohost/sso/"
|
||||
|
||||
|
@ -1094,7 +1093,6 @@ def test_permission_protection_management_by_helper():
|
|||
|
||||
@pytest.mark.other_domains(number=1)
|
||||
def test_permission_app_propagation_on_ssowat():
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "permissions_app_ynh"),
|
||||
args="domain=%s&domain_2=%s&path=%s&is_public=1&admin=%s"
|
||||
|
@ -1131,7 +1129,6 @@ def test_permission_app_propagation_on_ssowat():
|
|||
|
||||
@pytest.mark.other_domains(number=1)
|
||||
def test_permission_legacy_app_propagation_on_ssowat():
|
||||
|
||||
app_install(
|
||||
os.path.join(get_test_apps_dir(), "legacy_app_ynh"),
|
||||
args="domain=%s&domain_2=%s&path=%s&is_public=1"
|
||||
|
|
|
@ -49,7 +49,6 @@ def test_question_empty():
|
|||
|
||||
|
||||
def test_question_string():
|
||||
|
||||
questions = {
|
||||
"some_string": {
|
||||
"type": "string",
|
||||
|
@ -65,7 +64,6 @@ def test_question_string():
|
|||
|
||||
|
||||
def test_question_string_from_query_string():
|
||||
|
||||
questions = {
|
||||
"some_string": {
|
||||
"type": "string",
|
||||
|
@ -1539,7 +1537,6 @@ def test_question_user_two_users_default_input():
|
|||
os, "isatty", return_value=True
|
||||
):
|
||||
with patch.object(user, "user_info", return_value={}):
|
||||
|
||||
with patch.object(Moulinette, "prompt", return_value=username):
|
||||
out = ask_questions_and_parse_answers(questions, answers)[0]
|
||||
|
||||
|
@ -1843,7 +1840,6 @@ def test_question_display_text():
|
|||
|
||||
|
||||
def test_question_file_from_cli():
|
||||
|
||||
FileQuestion.clean_upload_dirs()
|
||||
|
||||
filename = "/tmp/ynh_test_question_file"
|
||||
|
@ -1874,7 +1870,6 @@ def test_question_file_from_cli():
|
|||
|
||||
|
||||
def test_question_file_from_api():
|
||||
|
||||
FileQuestion.clean_upload_dirs()
|
||||
|
||||
from base64 import b64encode
|
||||
|
@ -1907,7 +1902,6 @@ def test_question_file_from_api():
|
|||
|
||||
|
||||
def test_normalize_boolean_nominal():
|
||||
|
||||
assert BooleanQuestion.normalize("yes") == 1
|
||||
assert BooleanQuestion.normalize("Yes") == 1
|
||||
assert BooleanQuestion.normalize(" yes ") == 1
|
||||
|
@ -1937,7 +1931,6 @@ def test_normalize_boolean_nominal():
|
|||
|
||||
|
||||
def test_normalize_boolean_humanize():
|
||||
|
||||
assert BooleanQuestion.humanize("yes") == "yes"
|
||||
assert BooleanQuestion.humanize("true") == "yes"
|
||||
assert BooleanQuestion.humanize("on") == "yes"
|
||||
|
@ -1948,7 +1941,6 @@ def test_normalize_boolean_humanize():
|
|||
|
||||
|
||||
def test_normalize_boolean_invalid():
|
||||
|
||||
with pytest.raises(YunohostValidationError):
|
||||
BooleanQuestion.normalize("yesno")
|
||||
with pytest.raises(YunohostValidationError):
|
||||
|
@ -1958,7 +1950,6 @@ def test_normalize_boolean_invalid():
|
|||
|
||||
|
||||
def test_normalize_boolean_special_yesno():
|
||||
|
||||
customyesno = {"yes": "enabled", "no": "disabled"}
|
||||
|
||||
assert BooleanQuestion.normalize("yes", customyesno) == "enabled"
|
||||
|
@ -1977,14 +1968,12 @@ def test_normalize_boolean_special_yesno():
|
|||
|
||||
|
||||
def test_normalize_domain():
|
||||
|
||||
assert DomainQuestion.normalize("https://yolo.swag/") == "yolo.swag"
|
||||
assert DomainQuestion.normalize("http://yolo.swag") == "yolo.swag"
|
||||
assert DomainQuestion.normalize("yolo.swag/") == "yolo.swag"
|
||||
|
||||
|
||||
def test_normalize_path():
|
||||
|
||||
assert PathQuestion.normalize("") == "/"
|
||||
assert PathQuestion.normalize("") == "/"
|
||||
assert PathQuestion.normalize("macnuggets") == "/macnuggets"
|
||||
|
|
|
@ -16,19 +16,16 @@ SSHD_CONFIG = "/etc/ssh/sshd_config"
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
_force_clear_hashes([TEST_DOMAIN_NGINX_CONFIG])
|
||||
clean()
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
clean()
|
||||
_force_clear_hashes([TEST_DOMAIN_NGINX_CONFIG])
|
||||
|
||||
|
||||
def clean():
|
||||
|
||||
assert os.system("pgrep slapd >/dev/null") == 0
|
||||
assert os.system("pgrep nginx >/dev/null") == 0
|
||||
|
||||
|
@ -48,7 +45,6 @@ def clean():
|
|||
|
||||
|
||||
def test_add_domain():
|
||||
|
||||
domain_add(TEST_DOMAIN)
|
||||
|
||||
assert TEST_DOMAIN in domain_list()["domains"]
|
||||
|
@ -60,7 +56,6 @@ def test_add_domain():
|
|||
|
||||
|
||||
def test_add_and_edit_domain_conf():
|
||||
|
||||
domain_add(TEST_DOMAIN)
|
||||
|
||||
assert os.path.exists(TEST_DOMAIN_NGINX_CONFIG)
|
||||
|
@ -73,7 +68,6 @@ def test_add_and_edit_domain_conf():
|
|||
|
||||
|
||||
def test_add_domain_conf_already_exists():
|
||||
|
||||
os.system("echo ' ' >> %s" % TEST_DOMAIN_NGINX_CONFIG)
|
||||
|
||||
domain_add(TEST_DOMAIN)
|
||||
|
@ -84,7 +78,6 @@ def test_add_domain_conf_already_exists():
|
|||
|
||||
|
||||
def test_ssh_conf_unmanaged():
|
||||
|
||||
_force_clear_hashes([SSHD_CONFIG])
|
||||
|
||||
assert SSHD_CONFIG not in _get_conf_hashes("ssh")
|
||||
|
@ -95,7 +88,6 @@ def test_ssh_conf_unmanaged():
|
|||
|
||||
|
||||
def test_ssh_conf_unmanaged_and_manually_modified(mocker):
|
||||
|
||||
_force_clear_hashes([SSHD_CONFIG])
|
||||
os.system("echo ' ' >> %s" % SSHD_CONFIG)
|
||||
|
||||
|
|
|
@ -14,17 +14,14 @@ from yunohost.service import (
|
|||
|
||||
|
||||
def setup_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def teardown_function(function):
|
||||
|
||||
clean()
|
||||
|
||||
|
||||
def clean():
|
||||
|
||||
# To run these tests, we assume ssh(d) service exists and is running
|
||||
assert os.system("pgrep sshd >/dev/null") == 0
|
||||
|
||||
|
@ -45,46 +42,39 @@ def clean():
|
|||
|
||||
|
||||
def test_service_status_all():
|
||||
|
||||
status = service_status()
|
||||
assert "ssh" in status.keys()
|
||||
assert status["ssh"]["status"] == "running"
|
||||
|
||||
|
||||
def test_service_status_single():
|
||||
|
||||
status = service_status("ssh")
|
||||
assert "status" in status.keys()
|
||||
assert status["status"] == "running"
|
||||
|
||||
|
||||
def test_service_log():
|
||||
|
||||
logs = service_log("ssh")
|
||||
assert "journalctl" in logs.keys()
|
||||
assert "/var/log/auth.log" in logs.keys()
|
||||
|
||||
|
||||
def test_service_status_unknown_service(mocker):
|
||||
|
||||
with raiseYunohostError(mocker, "service_unknown"):
|
||||
service_status(["ssh", "doesnotexists"])
|
||||
|
||||
|
||||
def test_service_add():
|
||||
|
||||
service_add("dummyservice", description="A dummy service to run tests")
|
||||
assert "dummyservice" in service_status().keys()
|
||||
|
||||
|
||||
def test_service_add_real_service():
|
||||
|
||||
service_add("networking")
|
||||
assert "networking" in service_status().keys()
|
||||
|
||||
|
||||
def test_service_remove():
|
||||
|
||||
service_add("dummyservice", description="A dummy service to run tests")
|
||||
assert "dummyservice" in service_status().keys()
|
||||
service_remove("dummyservice")
|
||||
|
@ -92,7 +82,6 @@ def test_service_remove():
|
|||
|
||||
|
||||
def test_service_remove_service_that_doesnt_exists(mocker):
|
||||
|
||||
assert "dummyservice" not in service_status().keys()
|
||||
|
||||
with raiseYunohostError(mocker, "service_unknown"):
|
||||
|
@ -102,7 +91,6 @@ def test_service_remove_service_that_doesnt_exists(mocker):
|
|||
|
||||
|
||||
def test_service_update_to_add_properties():
|
||||
|
||||
service_add("dummyservice", description="dummy")
|
||||
assert not _get_services()["dummyservice"].get("test_status")
|
||||
service_add("dummyservice", description="dummy", test_status="true")
|
||||
|
@ -110,7 +98,6 @@ def test_service_update_to_add_properties():
|
|||
|
||||
|
||||
def test_service_update_to_change_properties():
|
||||
|
||||
service_add("dummyservice", description="dummy", test_status="false")
|
||||
assert _get_services()["dummyservice"].get("test_status") == "false"
|
||||
service_add("dummyservice", description="dummy", test_status="true")
|
||||
|
@ -118,7 +105,6 @@ def test_service_update_to_change_properties():
|
|||
|
||||
|
||||
def test_service_update_to_remove_properties():
|
||||
|
||||
service_add("dummyservice", description="dummy", test_status="false")
|
||||
assert _get_services()["dummyservice"].get("test_status") == "false"
|
||||
service_add("dummyservice", description="dummy", test_status="")
|
||||
|
@ -126,7 +112,6 @@ def test_service_update_to_remove_properties():
|
|||
|
||||
|
||||
def test_service_conf_broken():
|
||||
|
||||
os.system("echo pwet > /etc/nginx/conf.d/broken.conf")
|
||||
|
||||
status = service_status("nginx")
|
||||
|
|
|
@ -65,7 +65,6 @@ old_translate = moulinette.core.Translator.translate
|
|||
|
||||
|
||||
def _monkeypatch_translator(self, key, *args, **kwargs):
|
||||
|
||||
if key.startswith("global_settings_setting_"):
|
||||
return f"Dummy translation for {key}"
|
||||
|
||||
|
@ -175,7 +174,6 @@ def test_settings_set_doesexit():
|
|||
|
||||
|
||||
def test_settings_set_bad_type_bool():
|
||||
|
||||
with patch.object(os, "isatty", return_value=False):
|
||||
with pytest.raises(YunohostError):
|
||||
settings_set("example.example.boolean", 42)
|
||||
|
|
|
@ -92,7 +92,6 @@ def test_list_groups():
|
|||
|
||||
|
||||
def test_create_user(mocker):
|
||||
|
||||
with message(mocker, "user_created"):
|
||||
user_create("albert", maindomain, "test123Ynh", fullname="Albert Good")
|
||||
|
||||
|
@ -104,7 +103,6 @@ def test_create_user(mocker):
|
|||
|
||||
|
||||
def test_del_user(mocker):
|
||||
|
||||
with message(mocker, "user_deleted"):
|
||||
user_delete("alice")
|
||||
|
||||
|
@ -185,7 +183,6 @@ def test_export_user(mocker):
|
|||
|
||||
|
||||
def test_create_group(mocker):
|
||||
|
||||
with message(mocker, "group_created", group="adminsys"):
|
||||
user_group_create("adminsys")
|
||||
|
||||
|
@ -196,7 +193,6 @@ def test_create_group(mocker):
|
|||
|
||||
|
||||
def test_del_group(mocker):
|
||||
|
||||
with message(mocker, "group_deleted", group="dev"):
|
||||
user_group_delete("dev")
|
||||
|
||||
|
|
17
src/tools.py
17
src/tools.py
|
@ -62,7 +62,6 @@ def tools_versions():
|
|||
|
||||
|
||||
def tools_rootpw(new_password, check_strength=True):
|
||||
|
||||
from yunohost.user import _hash_user_password
|
||||
from yunohost.utils.password import (
|
||||
assert_password_is_strong_enough,
|
||||
|
@ -154,7 +153,6 @@ def tools_postinstall(
|
|||
ignore_dyndns=False,
|
||||
force_diskspace=False,
|
||||
):
|
||||
|
||||
from yunohost.dyndns import _dyndns_available
|
||||
from yunohost.utils.dns import is_yunohost_dyndns_domain
|
||||
from yunohost.utils.password import (
|
||||
|
@ -193,7 +191,6 @@ def tools_postinstall(
|
|||
|
||||
# If this is a nohost.me/noho.st, actually check for availability
|
||||
if not ignore_dyndns and is_yunohost_dyndns_domain(domain):
|
||||
|
||||
available = None
|
||||
|
||||
# Check if the domain is available...
|
||||
|
@ -281,7 +278,6 @@ def tools_postinstall(
|
|||
def tools_regen_conf(
|
||||
names=[], with_diff=False, force=False, dry_run=False, list_pending=False
|
||||
):
|
||||
|
||||
# Make sure the settings are migrated before running the migration,
|
||||
# which may otherwise fuck things up such as the ssh config ...
|
||||
# We do this here because the regen-conf is called before the migration in debian/postinst
|
||||
|
@ -312,7 +308,6 @@ def tools_update(target=None):
|
|||
|
||||
upgradable_system_packages = []
|
||||
if target in ["system", "all"]:
|
||||
|
||||
# Update APT cache
|
||||
# LC_ALL=C is here to make sure the results are in english
|
||||
command = (
|
||||
|
@ -426,7 +421,6 @@ def tools_upgrade(operation_logger, target=None):
|
|||
#
|
||||
|
||||
if target == "apps":
|
||||
|
||||
# Make sure there's actually something to upgrade
|
||||
|
||||
upgradable_apps = [app["id"] for app in app_list(upgradable=True)["apps"]]
|
||||
|
@ -450,7 +444,6 @@ def tools_upgrade(operation_logger, target=None):
|
|||
#
|
||||
|
||||
if target == "system":
|
||||
|
||||
# Check that there's indeed some packages to upgrade
|
||||
upgradables = list(_list_upgradable_apt_packages())
|
||||
if not upgradables:
|
||||
|
@ -498,7 +491,6 @@ def tools_upgrade(operation_logger, target=None):
|
|||
any(p["name"] == "yunohost" for p in upgradables)
|
||||
and Moulinette.interface.type == "api"
|
||||
):
|
||||
|
||||
# Restart the API after 10 sec (at now doesn't support sub-minute times...)
|
||||
# We do this so that the API / webadmin still gets the proper HTTP response
|
||||
# It's then up to the webadmin to implement a proper UX process to wait 10 sec and then auto-fresh the webadmin
|
||||
|
@ -722,7 +714,6 @@ def tools_migrations_run(
|
|||
|
||||
# Actually run selected migrations
|
||||
for migration in targets:
|
||||
|
||||
# If we are migrating in "automatic mode" (i.e. from debian configure
|
||||
# during an upgrade of the package) but we are asked for running
|
||||
# migrations to be ran manually by the user, stop there and ask the
|
||||
|
@ -778,7 +769,6 @@ def tools_migrations_run(
|
|||
_write_migration_state(migration.id, "skipped")
|
||||
operation_logger.success()
|
||||
else:
|
||||
|
||||
try:
|
||||
migration.operation_logger = operation_logger
|
||||
logger.info(m18n.n("migrations_running_forward", id=migration.id))
|
||||
|
@ -810,14 +800,12 @@ def tools_migrations_state():
|
|||
|
||||
|
||||
def _write_migration_state(migration_id, state):
|
||||
|
||||
current_states = tools_migrations_state()
|
||||
current_states["migrations"][migration_id] = state
|
||||
write_to_yaml(MIGRATIONS_STATE_PATH, current_states)
|
||||
|
||||
|
||||
def _get_migrations_list():
|
||||
|
||||
# states is a datastructure that represents the last run migration
|
||||
# it has this form:
|
||||
# {
|
||||
|
@ -868,7 +856,6 @@ def _get_migration_by_name(migration_name):
|
|||
|
||||
|
||||
def _load_migration(migration_file):
|
||||
|
||||
migration_id = migration_file[: -len(".py")]
|
||||
|
||||
logger.debug(m18n.n("migrations_loading_migration", id=migration_id))
|
||||
|
@ -903,7 +890,6 @@ def _skip_all_migrations():
|
|||
|
||||
|
||||
def _tools_migrations_run_after_system_restore(backup_version):
|
||||
|
||||
all_migrations = _get_migrations_list()
|
||||
|
||||
current_version = version.parse(ynh_packages_version()["yunohost"]["version"])
|
||||
|
@ -930,7 +916,6 @@ def _tools_migrations_run_after_system_restore(backup_version):
|
|||
|
||||
|
||||
def _tools_migrations_run_before_app_restore(backup_version, app_id):
|
||||
|
||||
all_migrations = _get_migrations_list()
|
||||
|
||||
current_version = version.parse(ynh_packages_version()["yunohost"]["version"])
|
||||
|
@ -957,7 +942,6 @@ def _tools_migrations_run_before_app_restore(backup_version, app_id):
|
|||
|
||||
|
||||
class Migration:
|
||||
|
||||
# Those are to be implemented by daughter classes
|
||||
|
||||
mode = "auto"
|
||||
|
@ -985,7 +969,6 @@ class Migration:
|
|||
|
||||
def ldap_migration(run):
|
||||
def func(self):
|
||||
|
||||
# Backup LDAP before the migration
|
||||
logger.info(m18n.n("migration_ldap_backup_before_migration"))
|
||||
try:
|
||||
|
|
46
src/user.py
46
src/user.py
|
@ -53,7 +53,6 @@ ADMIN_ALIASES = ["root", "admin", "admins", "webmaster", "postmaster", "abuse"]
|
|||
|
||||
|
||||
def user_list(fields=None):
|
||||
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
ldap_attrs = {
|
||||
|
@ -123,6 +122,18 @@ def user_list(fields=None):
|
|||
return {"users": users}
|
||||
|
||||
|
||||
def list_shells():
|
||||
with open("/etc/shells", "r") as f:
|
||||
content = f.readlines()
|
||||
|
||||
return [line.strip() for line in content if line.startswith("/")]
|
||||
|
||||
|
||||
def shellexists(shell):
|
||||
"""Check if the provided shell exists and is executable."""
|
||||
return os.path.isfile(shell) and os.access(shell, os.X_OK)
|
||||
|
||||
|
||||
@is_unit_operation([("username", "user")])
|
||||
def user_create(
|
||||
operation_logger,
|
||||
|
@ -135,8 +146,8 @@ def user_create(
|
|||
mailbox_quota="0",
|
||||
admin=False,
|
||||
from_import=False,
|
||||
loginShell=None,
|
||||
):
|
||||
|
||||
if firstname or lastname:
|
||||
logger.warning(
|
||||
"Options --firstname / --lastname of 'yunohost user create' are deprecated. We recommend using --fullname instead."
|
||||
|
@ -230,6 +241,12 @@ def user_create(
|
|||
uid = str(random.randint(1001, 65000))
|
||||
uid_guid_found = uid not in all_uid and uid not in all_gid
|
||||
|
||||
if not loginShell:
|
||||
loginShell = "/bin/bash"
|
||||
else:
|
||||
if not shellexists(loginShell) or loginShell not in list_shells():
|
||||
raise YunohostValidationError("invalid_shell", shell=loginShell)
|
||||
|
||||
attr_dict = {
|
||||
"objectClass": [
|
||||
"mailAccount",
|
||||
|
@ -249,7 +266,7 @@ def user_create(
|
|||
"gidNumber": [uid],
|
||||
"uidNumber": [uid],
|
||||
"homeDirectory": ["/home/" + username],
|
||||
"loginShell": ["/bin/bash"],
|
||||
"loginShell": [loginShell],
|
||||
}
|
||||
|
||||
try:
|
||||
|
@ -300,7 +317,6 @@ def user_create(
|
|||
|
||||
@is_unit_operation([("username", "user")])
|
||||
def user_delete(operation_logger, username, purge=False, from_import=False):
|
||||
|
||||
from yunohost.hook import hook_callback
|
||||
from yunohost.utils.ldap import _get_ldap_interface
|
||||
|
||||
|
@ -359,8 +375,8 @@ def user_update(
|
|||
mailbox_quota=None,
|
||||
from_import=False,
|
||||
fullname=None,
|
||||
loginShell=None,
|
||||
):
|
||||
|
||||
if firstname or lastname:
|
||||
logger.warning(
|
||||
"Options --firstname / --lastname of 'yunohost user create' are deprecated. We recommend using --fullname instead."
|
||||
|
@ -519,6 +535,12 @@ def user_update(
|
|||
new_attr_dict["mailuserquota"] = [mailbox_quota]
|
||||
env_dict["YNH_USER_MAILQUOTA"] = mailbox_quota
|
||||
|
||||
if loginShell is not None:
|
||||
if not shellexists(loginShell) or loginShell not in list_shells():
|
||||
raise YunohostValidationError("invalid_shell", shell=loginShell)
|
||||
new_attr_dict["loginShell"] = [loginShell]
|
||||
env_dict["YNH_USER_LOGINSHELL"] = loginShell
|
||||
|
||||
if not from_import:
|
||||
operation_logger.start()
|
||||
|
||||
|
@ -527,6 +549,10 @@ def user_update(
|
|||
except Exception as e:
|
||||
raise YunohostError("user_update_failed", user=username, error=e)
|
||||
|
||||
# Invalidate passwd and group to update the loginShell
|
||||
subprocess.call(["nscd", "-i", "passwd"])
|
||||
subprocess.call(["nscd", "-i", "group"])
|
||||
|
||||
# Trigger post_user_update hooks
|
||||
hook_callback("post_user_update", env=env_dict)
|
||||
|
||||
|
@ -548,7 +574,7 @@ def user_info(username):
|
|||
|
||||
ldap = _get_ldap_interface()
|
||||
|
||||
user_attrs = ["cn", "mail", "uid", "maildrop", "mailuserquota"]
|
||||
user_attrs = ["cn", "mail", "uid", "maildrop", "mailuserquota", "loginShell"]
|
||||
|
||||
if len(username.split("@")) == 2:
|
||||
filter = "mail=" + username
|
||||
|
@ -566,6 +592,7 @@ def user_info(username):
|
|||
"username": user["uid"][0],
|
||||
"fullname": user["cn"][0],
|
||||
"mail": user["mail"][0],
|
||||
"loginShell": user["loginShell"][0],
|
||||
"mail-aliases": [],
|
||||
"mail-forward": [],
|
||||
}
|
||||
|
@ -704,7 +731,6 @@ def user_import(operation_logger, csvfile, update=False, delete=False):
|
|||
)
|
||||
|
||||
for user in reader:
|
||||
|
||||
# Validate column values against regexes
|
||||
format_errors = [
|
||||
f"{key}: '{user[key]}' doesn't match the expected format"
|
||||
|
@ -960,7 +986,6 @@ def user_group_list(short=False, full=False, include_primary_groups=True):
|
|||
users = user_list()["users"]
|
||||
groups = {}
|
||||
for infos in groups_infos:
|
||||
|
||||
name = infos["cn"][0]
|
||||
|
||||
if not include_primary_groups and name in users:
|
||||
|
@ -1110,7 +1135,6 @@ def user_group_update(
|
|||
sync_perm=True,
|
||||
from_import=False,
|
||||
):
|
||||
|
||||
from yunohost.permission import permission_sync_to_user
|
||||
from yunohost.utils.ldap import _get_ldap_interface, _ldap_path_extract
|
||||
|
||||
|
@ -1153,7 +1177,6 @@ def user_group_update(
|
|||
new_attr_dict = {}
|
||||
|
||||
if add:
|
||||
|
||||
users_to_add = [add] if not isinstance(add, list) else add
|
||||
|
||||
for user in users_to_add:
|
||||
|
@ -1194,7 +1217,6 @@ def user_group_update(
|
|||
|
||||
# Check the whole alias situation
|
||||
if add_mailalias:
|
||||
|
||||
from yunohost.domain import domain_list
|
||||
|
||||
domains = domain_list()["domains"]
|
||||
|
@ -1238,7 +1260,6 @@ def user_group_update(
|
|||
raise YunohostValidationError("mail_alias_remove_failed", mail=mail)
|
||||
|
||||
if set(new_group_mail) != set(current_group_mail):
|
||||
|
||||
logger.info(m18n.n("group_update_aliases", group=groupname))
|
||||
new_attr_dict["mail"] = set(new_group_mail)
|
||||
|
||||
|
@ -1446,7 +1467,6 @@ def _hash_user_password(password):
|
|||
|
||||
|
||||
def _update_admins_group_aliases(old_main_domain, new_main_domain):
|
||||
|
||||
current_admin_aliases = user_group_info("admins")["mail-aliases"]
|
||||
|
||||
aliases_to_remove = [
|
||||
|
|
|
@ -264,7 +264,6 @@ class ConfigPanel:
|
|||
|
||||
# In 'classic' mode, we display the current value if key refer to an option
|
||||
if self.filter_key.count(".") == 2 and mode == "classic":
|
||||
|
||||
option = self.filter_key.split(".")[-1]
|
||||
value = self.values.get(option, None)
|
||||
|
||||
|
@ -280,7 +279,6 @@ class ConfigPanel:
|
|||
logger.debug(f"Formating result in '{mode}' mode")
|
||||
result = {}
|
||||
for panel, section, option in self._iterate():
|
||||
|
||||
if section["is_action_section"] and mode != "full":
|
||||
continue
|
||||
|
||||
|
@ -323,7 +321,6 @@ class ConfigPanel:
|
|||
return result
|
||||
|
||||
def list_actions(self):
|
||||
|
||||
actions = {}
|
||||
|
||||
# FIXME : meh, loading the entire config panel is again going to cause
|
||||
|
@ -462,7 +459,6 @@ class ConfigPanel:
|
|||
return read_toml(self.config_path)
|
||||
|
||||
def _get_config_panel(self):
|
||||
|
||||
# Split filter_key
|
||||
filter_key = self.filter_key.split(".") if self.filter_key != "" else []
|
||||
if len(filter_key) > 3:
|
||||
|
@ -639,7 +635,6 @@ class ConfigPanel:
|
|||
# Hydrating config panel with current value
|
||||
for _, section, option in self._iterate():
|
||||
if option["id"] not in self.values:
|
||||
|
||||
allowed_empty_types = [
|
||||
"alert",
|
||||
"display_text",
|
||||
|
@ -701,7 +696,6 @@ class ConfigPanel:
|
|||
Moulinette.display(colorize(message, "purple"))
|
||||
|
||||
for panel, section, obj in self._iterate(["panel", "section"]):
|
||||
|
||||
if (
|
||||
section
|
||||
and section.get("visible")
|
||||
|
@ -814,7 +808,6 @@ class ConfigPanel:
|
|||
write_to_yaml(self.save_path, values_to_save)
|
||||
|
||||
def _reload_services(self):
|
||||
|
||||
from yunohost.service import service_reload_or_restart
|
||||
|
||||
services_to_reload = set()
|
||||
|
@ -905,7 +898,6 @@ class Question:
|
|||
)
|
||||
|
||||
def ask_if_needed(self):
|
||||
|
||||
if self.visible and not evaluate_simple_js_expression(
|
||||
self.visible, context=self.context
|
||||
):
|
||||
|
@ -980,7 +972,6 @@ class Question:
|
|||
)
|
||||
|
||||
def _format_text_for_user_input_in_cli(self):
|
||||
|
||||
text_for_user_input_in_cli = _value_for_locale(self.ask)
|
||||
|
||||
if self.readonly:
|
||||
|
@ -991,7 +982,6 @@ class Question:
|
|||
)
|
||||
return text_for_user_input_in_cli + f" {self.humanize(self.current_value)}"
|
||||
elif self.choices:
|
||||
|
||||
# Prevent displaying a shitload of choices
|
||||
# (e.g. 100+ available users when choosing an app admin...)
|
||||
choices = (
|
||||
|
@ -1160,7 +1150,6 @@ class PathQuestion(Question):
|
|||
|
||||
@staticmethod
|
||||
def normalize(value, option={}):
|
||||
|
||||
option = option.__dict__ if isinstance(option, Question) else option
|
||||
|
||||
if not value.strip():
|
||||
|
@ -1187,7 +1176,6 @@ class BooleanQuestion(Question):
|
|||
|
||||
@staticmethod
|
||||
def humanize(value, option={}):
|
||||
|
||||
option = option.__dict__ if isinstance(option, Question) else option
|
||||
|
||||
yes = option.get("yes", 1)
|
||||
|
@ -1211,7 +1199,6 @@ class BooleanQuestion(Question):
|
|||
|
||||
@staticmethod
|
||||
def normalize(value, option={}):
|
||||
|
||||
option = option.__dict__ if isinstance(option, Question) else option
|
||||
|
||||
if isinstance(value, str):
|
||||
|
@ -1368,7 +1355,6 @@ class GroupQuestion(Question):
|
|||
def __init__(
|
||||
self, question, context: Mapping[str, Any] = {}, hooks: Dict[str, Callable] = {}
|
||||
):
|
||||
|
||||
from yunohost.user import user_group_list
|
||||
|
||||
super().__init__(question, context)
|
||||
|
@ -1401,7 +1387,6 @@ class NumberQuestion(Question):
|
|||
|
||||
@staticmethod
|
||||
def normalize(value, option={}):
|
||||
|
||||
if isinstance(value, int):
|
||||
return value
|
||||
|
||||
|
|
|
@ -31,19 +31,16 @@ external_resolvers_: List[str] = []
|
|||
|
||||
|
||||
def is_yunohost_dyndns_domain(domain):
|
||||
|
||||
return any(
|
||||
domain.endswith(f".{dyndns_domain}") for dyndns_domain in YNH_DYNDNS_DOMAINS
|
||||
)
|
||||
|
||||
|
||||
def is_special_use_tld(domain):
|
||||
|
||||
return any(domain.endswith(f".{tld}") for tld in SPECIAL_USE_TLDS)
|
||||
|
||||
|
||||
def external_resolvers():
|
||||
|
||||
global external_resolvers_
|
||||
|
||||
if not external_resolvers_:
|
||||
|
|
|
@ -21,7 +21,6 @@ from moulinette import m18n
|
|||
|
||||
|
||||
class YunohostError(MoulinetteError):
|
||||
|
||||
http_code = 500
|
||||
|
||||
"""
|
||||
|
@ -43,7 +42,6 @@ class YunohostError(MoulinetteError):
|
|||
super(YunohostError, self).__init__(msg, raw_msg=True)
|
||||
|
||||
def content(self):
|
||||
|
||||
if not self.log_ref:
|
||||
return super().content()
|
||||
else:
|
||||
|
@ -51,14 +49,11 @@ class YunohostError(MoulinetteError):
|
|||
|
||||
|
||||
class YunohostValidationError(YunohostError):
|
||||
|
||||
http_code = 400
|
||||
|
||||
def content(self):
|
||||
|
||||
return {"error": self.strerror, "error_key": self.key, **self.kwargs}
|
||||
|
||||
|
||||
class YunohostAuthenticationError(MoulinetteAuthenticationError):
|
||||
|
||||
pass
|
||||
|
|
|
@ -36,7 +36,6 @@ _ldap_interface = None
|
|||
|
||||
|
||||
def _get_ldap_interface():
|
||||
|
||||
global _ldap_interface
|
||||
|
||||
if _ldap_interface is None:
|
||||
|
|
|
@ -193,7 +193,6 @@ LEGACY_PHP_VERSION_REPLACEMENTS = [
|
|||
|
||||
|
||||
def _patch_legacy_php_versions(app_folder):
|
||||
|
||||
files_to_patch = []
|
||||
files_to_patch.extend(glob.glob("%s/conf/*" % app_folder))
|
||||
files_to_patch.extend(glob.glob("%s/scripts/*" % app_folder))
|
||||
|
@ -203,7 +202,6 @@ def _patch_legacy_php_versions(app_folder):
|
|||
files_to_patch.append("%s/manifest.toml" % app_folder)
|
||||
|
||||
for filename in files_to_patch:
|
||||
|
||||
# Ignore non-regular files
|
||||
if not os.path.isfile(filename):
|
||||
continue
|
||||
|
@ -217,7 +215,6 @@ def _patch_legacy_php_versions(app_folder):
|
|||
|
||||
|
||||
def _patch_legacy_php_versions_in_settings(app_folder):
|
||||
|
||||
settings = read_yaml(os.path.join(app_folder, "settings.yml"))
|
||||
|
||||
if settings.get("fpm_config_dir") in ["/etc/php/7.0/fpm", "/etc/php/7.3/fpm"]:
|
||||
|
@ -243,7 +240,6 @@ def _patch_legacy_php_versions_in_settings(app_folder):
|
|||
|
||||
|
||||
def _patch_legacy_helpers(app_folder):
|
||||
|
||||
files_to_patch = []
|
||||
files_to_patch.extend(glob.glob("%s/scripts/*" % app_folder))
|
||||
files_to_patch.extend(glob.glob("%s/scripts/.*" % app_folder))
|
||||
|
@ -291,7 +287,6 @@ def _patch_legacy_helpers(app_folder):
|
|||
infos["replace"] = infos.get("replace")
|
||||
|
||||
for filename in files_to_patch:
|
||||
|
||||
# Ignore non-regular files
|
||||
if not os.path.isfile(filename):
|
||||
continue
|
||||
|
@ -305,7 +300,6 @@ def _patch_legacy_helpers(app_folder):
|
|||
show_warning = False
|
||||
|
||||
for helper, infos in stuff_to_replace.items():
|
||||
|
||||
# Ignore if not relevant for this file
|
||||
if infos.get("only_for") and not any(
|
||||
filename.endswith(f) for f in infos["only_for"]
|
||||
|
@ -329,7 +323,6 @@ def _patch_legacy_helpers(app_folder):
|
|||
)
|
||||
|
||||
if replaced_stuff:
|
||||
|
||||
# Check the app do load the helper
|
||||
# If it doesn't, add the instruction ourselve (making sure it's after the #!/bin/bash if it's there...
|
||||
if filename.split("/")[-1] in [
|
||||
|
|
|
@ -29,7 +29,6 @@ logger = logging.getLogger("yunohost.utils.network")
|
|||
|
||||
|
||||
def get_public_ip(protocol=4):
|
||||
|
||||
assert protocol in [4, 6], (
|
||||
"Invalid protocol version for get_public_ip: %s, expected 4 or 6" % protocol
|
||||
)
|
||||
|
@ -90,7 +89,6 @@ def get_public_ip_from_remote_server(protocol=4):
|
|||
|
||||
|
||||
def get_network_interfaces():
|
||||
|
||||
# Get network devices and their addresses (raw infos from 'ip addr')
|
||||
devices_raw = {}
|
||||
output = check_output("ip addr show")
|
||||
|
@ -111,7 +109,6 @@ def get_network_interfaces():
|
|||
|
||||
|
||||
def get_gateway():
|
||||
|
||||
output = check_output("ip route show")
|
||||
m = re.search(r"default via (.*) dev ([a-z]+[0-9]?)", output)
|
||||
if not m:
|
||||
|
|
|
@ -58,7 +58,6 @@ def assert_password_is_compatible(password):
|
|||
"""
|
||||
|
||||
if len(password) >= 127:
|
||||
|
||||
# Note that those imports are made here and can't be put
|
||||
# on top (at least not the moulinette ones)
|
||||
# because the moulinette needs to be correctly initialized
|
||||
|
@ -69,7 +68,6 @@ def assert_password_is_compatible(password):
|
|||
|
||||
|
||||
def assert_password_is_strong_enough(profile, password):
|
||||
|
||||
PasswordValidator(profile).validate(password)
|
||||
|
||||
|
||||
|
@ -197,7 +195,6 @@ class PasswordValidator:
|
|||
return strength_level
|
||||
|
||||
def is_in_most_used_list(self, password):
|
||||
|
||||
# Decompress file if compressed
|
||||
if os.path.exists("%s.gz" % MOST_USED_PASSWORDS):
|
||||
os.system("gzip -fd %s.gz" % MOST_USED_PASSWORDS)
|
||||
|
|
|
@ -37,7 +37,6 @@ logger = getActionLogger("yunohost.app_resources")
|
|||
|
||||
class AppResourceManager:
|
||||
def __init__(self, app: str, current: Dict, wanted: Dict):
|
||||
|
||||
self.app = app
|
||||
self.current = current
|
||||
self.wanted = wanted
|
||||
|
@ -50,7 +49,6 @@ class AppResourceManager:
|
|||
def apply(
|
||||
self, rollback_and_raise_exception_if_failure, operation_logger=None, **context
|
||||
):
|
||||
|
||||
todos = list(self.compute_todos())
|
||||
completed = []
|
||||
rollback = False
|
||||
|
@ -121,7 +119,6 @@ class AppResourceManager:
|
|||
logger.error(exception)
|
||||
|
||||
def compute_todos(self):
|
||||
|
||||
for name, infos in reversed(self.current["resources"].items()):
|
||||
if name not in self.wanted["resources"].keys():
|
||||
resource = AppResourceClassesByType[name](infos, self.app, self)
|
||||
|
@ -140,12 +137,10 @@ class AppResourceManager:
|
|||
|
||||
|
||||
class AppResource:
|
||||
|
||||
type: str = ""
|
||||
default_properties: Dict[str, Any] = {}
|
||||
|
||||
def __init__(self, properties: Dict[str, Any], app: str, manager=None):
|
||||
|
||||
self.app = app
|
||||
self.manager = manager
|
||||
|
||||
|
@ -175,7 +170,6 @@ class AppResource:
|
|||
app_setting(self.app, key, delete=True)
|
||||
|
||||
def _run_script(self, action, script, env={}, user="root"):
|
||||
|
||||
from yunohost.app import (
|
||||
_make_tmp_workdir_for_app,
|
||||
_make_environment_for_app_script,
|
||||
|
@ -295,7 +289,6 @@ class PermissionsResource(AppResource):
|
|||
permissions: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
def __init__(self, properties: Dict[str, Any], *args, **kwargs):
|
||||
|
||||
# FIXME : if url != None, we should check that there's indeed a domain/path defined ? ie that app is a webapp
|
||||
|
||||
for perm, infos in properties.items():
|
||||
|
@ -315,7 +308,6 @@ class PermissionsResource(AppResource):
|
|||
super().__init__({"permissions": properties}, *args, **kwargs)
|
||||
|
||||
def provision_or_update(self, context: Dict = {}):
|
||||
|
||||
from yunohost.permission import (
|
||||
permission_create,
|
||||
permission_url,
|
||||
|
@ -375,7 +367,6 @@ class PermissionsResource(AppResource):
|
|||
permission_sync_to_user()
|
||||
|
||||
def deprovision(self, context: Dict = {}):
|
||||
|
||||
from yunohost.permission import (
|
||||
permission_delete,
|
||||
user_permission_list,
|
||||
|
@ -432,7 +423,6 @@ class SystemuserAppResource(AppResource):
|
|||
allow_sftp: bool = False
|
||||
|
||||
def provision_or_update(self, context: Dict = {}):
|
||||
|
||||
# FIXME : validate that no yunohost user exists with that name?
|
||||
# and/or that no system user exists during install ?
|
||||
|
||||
|
@ -462,7 +452,6 @@ class SystemuserAppResource(AppResource):
|
|||
os.system(f"usermod -G {','.join(groups)} {self.app}")
|
||||
|
||||
def deprovision(self, context: Dict = {}):
|
||||
|
||||
if check_output(f"getent passwd {self.app} &>/dev/null || true").strip():
|
||||
os.system(f"deluser {self.app} >/dev/null")
|
||||
if check_output(f"getent passwd {self.app} &>/dev/null || true").strip():
|
||||
|
@ -528,7 +517,6 @@ class InstalldirAppResource(AppResource):
|
|||
# FIXME: change default dir to /opt/stuff if app ain't a webapp ...
|
||||
|
||||
def provision_or_update(self, context: Dict = {}):
|
||||
|
||||
assert self.dir.strip() # Be paranoid about self.dir being empty...
|
||||
assert self.owner.strip()
|
||||
assert self.group.strip()
|
||||
|
@ -582,7 +570,6 @@ class InstalldirAppResource(AppResource):
|
|||
self.delete_setting("final_path") # Legacy
|
||||
|
||||
def deprovision(self, context: Dict = {}):
|
||||
|
||||
assert self.dir.strip() # Be paranoid about self.dir being empty...
|
||||
assert self.owner.strip()
|
||||
assert self.group.strip()
|
||||
|
@ -643,7 +630,6 @@ class DatadirAppResource(AppResource):
|
|||
group: str = ""
|
||||
|
||||
def provision_or_update(self, context: Dict = {}):
|
||||
|
||||
assert self.dir.strip() # Be paranoid about self.dir being empty...
|
||||
assert self.owner.strip()
|
||||
assert self.group.strip()
|
||||
|
@ -686,7 +672,6 @@ class DatadirAppResource(AppResource):
|
|||
self.delete_setting("datadir") # Legacy
|
||||
|
||||
def deprovision(self, context: Dict = {}):
|
||||
|
||||
assert self.dir.strip() # Be paranoid about self.dir being empty...
|
||||
assert self.owner.strip()
|
||||
assert self.group.strip()
|
||||
|
@ -737,7 +722,6 @@ class AptDependenciesAppResource(AppResource):
|
|||
extras: Dict[str, Dict[str, str]] = {}
|
||||
|
||||
def __init__(self, properties: Dict[str, Any], *args, **kwargs):
|
||||
|
||||
for key, values in properties.get("extras", {}).items():
|
||||
if not all(
|
||||
isinstance(values.get(k), str) for k in ["repo", "key", "packages"]
|
||||
|
@ -749,7 +733,6 @@ class AptDependenciesAppResource(AppResource):
|
|||
super().__init__(properties, *args, **kwargs)
|
||||
|
||||
def provision_or_update(self, context: Dict = {}):
|
||||
|
||||
script = [f"ynh_install_app_dependencies {self.packages}"]
|
||||
for repo, values in self.extras.items():
|
||||
script += [
|
||||
|
@ -760,7 +743,6 @@ class AptDependenciesAppResource(AppResource):
|
|||
self._run_script("provision_or_update", "\n".join(script))
|
||||
|
||||
def deprovision(self, context: Dict = {}):
|
||||
|
||||
self._run_script("deprovision", "ynh_remove_app_dependencies")
|
||||
|
||||
|
||||
|
@ -818,7 +800,6 @@ class PortsResource(AppResource):
|
|||
ports: Dict[str, Dict[str, Any]]
|
||||
|
||||
def __init__(self, properties: Dict[str, Any], *args, **kwargs):
|
||||
|
||||
if "main" not in properties:
|
||||
properties["main"] = {}
|
||||
|
||||
|
@ -832,7 +813,6 @@ class PortsResource(AppResource):
|
|||
super().__init__({"ports": properties}, *args, **kwargs)
|
||||
|
||||
def _port_is_used(self, port):
|
||||
|
||||
# FIXME : this could be less brutal than two os.system ...
|
||||
cmd1 = (
|
||||
"ss --numeric --listening --tcp --udp | awk '{print$5}' | grep --quiet --extended-regexp ':%s$'"
|
||||
|
@ -843,11 +823,9 @@ class PortsResource(AppResource):
|
|||
return os.system(cmd1) == 0 and os.system(cmd2) == 0
|
||||
|
||||
def provision_or_update(self, context: Dict = {}):
|
||||
|
||||
from yunohost.firewall import firewall_allow, firewall_disallow
|
||||
|
||||
for name, infos in self.ports.items():
|
||||
|
||||
setting_name = f"port_{name}" if name != "main" else "port"
|
||||
port_value = self.get_setting(setting_name)
|
||||
if not port_value and name != "main":
|
||||
|
@ -881,7 +859,6 @@ class PortsResource(AppResource):
|
|||
)
|
||||
|
||||
def deprovision(self, context: Dict = {}):
|
||||
|
||||
from yunohost.firewall import firewall_disallow
|
||||
|
||||
for name, infos in self.ports.items():
|
||||
|
@ -938,7 +915,6 @@ class DatabaseAppResource(AppResource):
|
|||
}
|
||||
|
||||
def __init__(self, properties: Dict[str, Any], *args, **kwargs):
|
||||
|
||||
if "type" not in properties or properties["type"] not in [
|
||||
"mysql",
|
||||
"postgresql",
|
||||
|
@ -956,7 +932,6 @@ class DatabaseAppResource(AppResource):
|
|||
super().__init__(properties, *args, **kwargs)
|
||||
|
||||
def db_exists(self, db_name):
|
||||
|
||||
if self.dbtype == "mysql":
|
||||
return os.system(f"mysqlshow '{db_name}' >/dev/null 2>/dev/null") == 0
|
||||
elif self.dbtype == "postgresql":
|
||||
|
@ -970,7 +945,6 @@ class DatabaseAppResource(AppResource):
|
|||
return False
|
||||
|
||||
def provision_or_update(self, context: Dict = {}):
|
||||
|
||||
# This is equivalent to ynh_sanitize_dbid
|
||||
db_name = self.app.replace("-", "_").replace(".", "_")
|
||||
db_user = db_name
|
||||
|
@ -997,7 +971,6 @@ class DatabaseAppResource(AppResource):
|
|||
self.set_setting("db_pwd", db_pwd)
|
||||
|
||||
if not self.db_exists(db_name):
|
||||
|
||||
if self.dbtype == "mysql":
|
||||
self._run_script(
|
||||
"provision",
|
||||
|
@ -1010,7 +983,6 @@ class DatabaseAppResource(AppResource):
|
|||
)
|
||||
|
||||
def deprovision(self, context: Dict = {}):
|
||||
|
||||
db_name = self.app.replace("-", "_").replace(".", "_")
|
||||
db_user = db_name
|
||||
|
||||
|
|
|
@ -49,7 +49,6 @@ def free_space_in_directory(dirpath):
|
|||
|
||||
|
||||
def space_used_by_directory(dirpath, follow_symlinks=True):
|
||||
|
||||
if not follow_symlinks:
|
||||
du_output = check_output(["du", "-sb", dirpath], shell=False)
|
||||
return int(du_output.split()[0])
|
||||
|
@ -61,7 +60,6 @@ def space_used_by_directory(dirpath, follow_symlinks=True):
|
|||
|
||||
|
||||
def human_to_binary(size: str) -> int:
|
||||
|
||||
symbols = ("K", "M", "G", "T", "P", "E", "Z", "Y")
|
||||
factor = {}
|
||||
for i, s in enumerate(symbols):
|
||||
|
@ -99,14 +97,12 @@ def binary_to_human(n: int) -> str:
|
|||
|
||||
|
||||
def ram_available():
|
||||
|
||||
import psutil
|
||||
|
||||
return (psutil.virtual_memory().available, psutil.swap_memory().free)
|
||||
|
||||
|
||||
def get_ynh_package_version(package):
|
||||
|
||||
# Returns the installed version and release version ('stable' or 'testing'
|
||||
# or 'unstable')
|
||||
|
||||
|
@ -152,7 +148,6 @@ def dpkg_lock_available():
|
|||
|
||||
|
||||
def _list_upgradable_apt_packages():
|
||||
|
||||
# List upgradable packages
|
||||
# LC_ALL=C is here to make sure the results are in english
|
||||
upgradable_raw = check_output("LC_ALL=C apt list --upgradable")
|
||||
|
@ -162,7 +157,6 @@ def _list_upgradable_apt_packages():
|
|||
line.strip() for line in upgradable_raw.split("\n") if line.strip()
|
||||
]
|
||||
for line in upgradable_raw:
|
||||
|
||||
# Remove stupid warning and verbose messages >.>
|
||||
if "apt does not have a stable CLI interface" in line or "Listing..." in line:
|
||||
continue
|
||||
|
@ -182,7 +176,6 @@ def _list_upgradable_apt_packages():
|
|||
|
||||
|
||||
def _dump_sources_list():
|
||||
|
||||
from glob import glob
|
||||
|
||||
filenames = glob("/etc/apt/sources.list") + glob("/etc/apt/sources.list.d/*")
|
||||
|
|
|
@ -28,7 +28,6 @@ logger = logging.getLogger("yunohost.utils.yunopaste")
|
|||
|
||||
|
||||
def yunopaste(data):
|
||||
|
||||
paste_server = "https://paste.yunohost.org"
|
||||
|
||||
try:
|
||||
|
|
2
tox.ini
2
tox.ini
|
@ -8,7 +8,7 @@ deps =
|
|||
py39-black-{run,check}: black
|
||||
py39-mypy: mypy >= 0.900
|
||||
commands =
|
||||
py39-lint: flake8 src doc maintenance tests --ignore E402,E501,E203,W503 --exclude src/vendor
|
||||
py39-lint: flake8 src doc maintenance tests --ignore E402,E501,E203,W503,E741 --exclude src/vendor
|
||||
py39-invalidcode: flake8 src bin maintenance --exclude src/tests,src/vendor --select F,E722,W605
|
||||
py39-black-check: black --check --diff bin src doc maintenance tests
|
||||
py39-black-run: black bin src doc maintenance tests
|
||||
|
|
Loading…
Add table
Reference in a new issue