diff --git a/.gitlab/ci/translation.gitlab-ci.yml b/.gitlab/ci/translation.gitlab-ci.yml index e6365adbc..41e8c82d2 100644 --- a/.gitlab/ci/translation.gitlab-ci.yml +++ b/.gitlab/ci/translation.gitlab-ci.yml @@ -20,7 +20,7 @@ autofix-translated-strings: - python3 reformat_locales.py - '[ $(git diff -w | wc -l) != 0 ] || exit 0' # stop if there is nothing to commit - git commit -am "[CI] Reformat / remove stale translated strings" || true - - git push -f origin "ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}":"ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}" + - git push -f origin "HEAD":"ci-remove-stale-translated-strings-${CI_COMMIT_REF_NAME}" - hub pull-request -m "[CI] Reformat / remove stale translated strings" -b Yunohost:dev -p || true # GITHUB_USER and GITHUB_TOKEN registered here https://gitlab.com/yunohost/yunohost/-/settings/ci_cd only: variables: diff --git a/data/helpers.d/string b/data/helpers.d/string index 7036b3b3c..a96157f78 100644 --- a/data/helpers.d/string +++ b/data/helpers.d/string @@ -43,12 +43,14 @@ ynh_replace_string () { local target_file # Manage arguments with getopts ynh_handle_getopts_args "$@" + set +o xtrace # set +x local delimit=@ # Escape the delimiter if it's in the string. match_string=${match_string//${delimit}/"\\${delimit}"} replace_string=${replace_string//${delimit}/"\\${delimit}"} + set -o xtrace # set -x sed --in-place "s${delimit}${match_string}${delimit}${replace_string}${delimit}g" "$target_file" } diff --git a/data/helpers.d/utils b/data/helpers.d/utils index 511fa52fb..34a089eb1 100644 --- a/data/helpers.d/utils +++ b/data/helpers.d/utils @@ -1,6 +1,6 @@ #!/bin/bash -YNH_APP_BASEDIR=$(realpath $([[ "$(basename $0)" =~ ^backup|restore$ ]] && echo '../settings' || echo '..')) +YNH_APP_BASEDIR=$(realpath $([[ "$(basename $0)" =~ ^backup|restore$ ]] && echo '../settings' || [[ -n "${YNH_ACTION:-}" ]] && echo '.' || echo '..' )) # Handle script crashes / failures # @@ -519,6 +519,8 @@ ynh_read_var_in_file() { [[ -f $file ]] || ynh_die --message="File $file does not exists" + set +o xtrace # set +x + # Get the line number after which we search for the variable local line_number=1 if [[ -n "$after" ]]; @@ -526,6 +528,7 @@ ynh_read_var_in_file() { line_number=$(grep -n $after $file | cut -d: -f1) if [[ -z "$line_number" ]]; then + set -o xtrace # set -x return 1 fi fi @@ -555,6 +558,7 @@ ynh_read_var_in_file() { # Extract the part after assignation sign local expression_with_comment="$(tail +$line_number ${file} | grep -i -o -P $var_part'\K.*$' || echo YNH_NULL | head -n1)" if [[ "$expression_with_comment" == "YNH_NULL" ]]; then + set -o xtrace # set -x echo YNH_NULL return 0 fi @@ -570,6 +574,7 @@ ynh_read_var_in_file() { else echo "$expression" fi + set -o xtrace # set -x } # Set a value into heterogeneous file (yaml, json, php, python...) @@ -594,6 +599,8 @@ ynh_write_var_in_file() { [[ -f $file ]] || ynh_die --message="File $file does not exists" + set +o xtrace # set +x + # Get the line number after which we search for the variable local line_number=1 if [[ -n "$after" ]]; @@ -601,6 +608,7 @@ ynh_write_var_in_file() { line_number=$(grep -n $after $file | cut -d: -f1) if [[ -z "$line_number" ]]; then + set -o xtrace # set -x return 1 fi fi @@ -631,9 +639,10 @@ ynh_write_var_in_file() { # Extract the part after assignation sign local expression_with_comment="$(tail +$line_number ${file} | grep -i -o -P $var_part'\K.*$' || echo YNH_NULL | head -n1)" if [[ "$expression_with_comment" == "YNH_NULL" ]]; then + set -o xtrace # set -x return 1 fi - + # Remove comments if needed local expression="$(echo "$expression_with_comment" | sed "s@$comments[^$string]*\$@@g" | sed "s@\s*[$endline]*\s*]*\$@@")" endline=${expression_with_comment#"$expression"} @@ -661,6 +670,7 @@ ynh_write_var_in_file() { fi sed -ri "${range}s$delimiter(^${var_part}).*\$$delimiter\1${value}${endline}${delimiter}i" ${file} fi + set -o xtrace # set -x } @@ -727,6 +737,7 @@ ynh_secure_remove () { local file # Manage arguments with getopts ynh_handle_getopts_args "$@" + set +o xtrace # set +x local forbidden_path=" \ /var/www \ @@ -754,6 +765,8 @@ ynh_secure_remove () { else ynh_print_info --message="'$file' wasn't deleted because it doesn't exist." fi + + set -o xtrace # set -x } # Extract a key from a plain command output diff --git a/locales/ca.json b/locales/ca.json index 5a128ebb8..0e8d446d5 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -177,7 +177,7 @@ "iptables_unavailable": "No podeu modificar les iptables aquí. O bé sou en un contenidor o bé el vostre nucli no és compatible amb aquesta opció", "log_corrupted_md_file": "El fitxer de metadades YAML associat amb els registres està malmès: « {md_file} »\nError: {error}", "log_link_to_log": "El registre complet d'aquesta operació: «{desc}»", - "log_help_to_get_log": "Per veure el registre de l'operació « {desc} », utilitzeu l'ordre « yunohost log show {name}{name} »", + "log_help_to_get_log": "Per veure el registre de l'operació « {desc} », utilitzeu l'ordre « yunohost log show {name} »", "log_link_to_failed_log": "No s'ha pogut completar l'operació « {desc} ». Per obtenir ajuda, proveïu el registre complete de l'operació clicant aquí", "log_help_to_get_failed_log": "No s'ha pogut completar l'operació « {desc} ». Per obtenir ajuda, compartiu el registre complete de l'operació utilitzant l'ordre « yunohost log share {name} »", "log_does_exists": "No hi ha cap registre per l'operació amb el nom« {log} », utilitzeu « yunohost log list » per veure tots els registre d'operació disponibles", diff --git a/locales/de.json b/locales/de.json index dca2b034d..6ddc1284d 100644 --- a/locales/de.json +++ b/locales/de.json @@ -244,7 +244,7 @@ "dpkg_is_broken": "Du kannst das gerade nicht tun, weil dpkg/APT (der Systempaketmanager) in einem defekten Zustand zu sein scheint.... Du kannst versuchen, dieses Problem zu lösen, indem du dich über SSH verbindest und `sudo apt install --fix-broken` sowie/oder `sudo dpkg --configure -a` ausführst.", "global_settings_unknown_setting_from_settings_file": "Unbekannter Schlüssel in den Einstellungen: '{setting_key}', verwerfen und speichern in /etc/yunohost/settings-unknown.json", "log_link_to_log": "Vollständiges Log dieser Operation: '{desc}'", - "log_help_to_get_log": "Um das Protokoll der Operation '{desc}' anzuzeigen, verwenden Sie den Befehl 'yunohost log show {name}{name}'", + "log_help_to_get_log": "Um das Protokoll der Operation '{desc}' anzuzeigen, verwenden Sie den Befehl 'yunohost log show {name}'", "global_settings_setting_security_nginx_compatibility": "Kompatibilitäts- vs. Sicherheits-Kompromiss für den Webserver NGINX. Betrifft die Ciphers (und andere sicherheitsrelevante Aspekte)", "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Erlaubt die Verwendung eines (veralteten) DSA-Hostkeys für die SSH-Daemon-Konfiguration", "log_app_remove": "Entferne die Applikation '{}'", diff --git a/locales/en.json b/locales/en.json index c8417e1e6..282576ee8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -15,6 +15,8 @@ "app_already_up_to_date": "{app} is already up-to-date", "app_argument_choice_invalid": "Use one of these choices '{choices}' for the argument '{name}' instead of '{value}'", "app_argument_invalid": "Pick a valid value for the argument '{name}': {error}", + "app_argument_password_help_keep": "Press Enter to keep the current value", + "app_argument_password_help_optional": "Type one space to empty the password", "app_argument_password_no_default": "Error while parsing password argument '{name}': password argument can't have a default value for security reason", "app_argument_required": "Argument '{name}' is required", "app_change_url_identical_domains": "The old and new domain/url_path are identical ('{domain}{path}'), nothing to do.", @@ -424,7 +426,7 @@ "log_dyndns_subscribe": "Subscribe to a YunoHost subdomain '{}'", "log_dyndns_update": "Update the IP associated with your YunoHost subdomain '{}'", "log_help_to_get_failed_log": "The operation '{desc}' could not be completed. Please share the full log of this operation using the command 'yunohost log share {name}' to get help", - "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log show {name}{name}'", + "log_help_to_get_log": "To view the log of the operation '{desc}', use the command 'yunohost log show {name}'", "log_letsencrypt_cert_install": "Install a Let's Encrypt certificate on '{}' domain", "log_letsencrypt_cert_renew": "Renew '{}' Let's Encrypt certificate", "log_link_to_failed_log": "Could not complete the operation '{desc}'. Please provide the full log of this operation by clicking here to get help", diff --git a/locales/eo.json b/locales/eo.json index f40111f04..1904cfd9a 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -326,7 +326,7 @@ "log_dyndns_subscribe": "Aboni al YunoHost-subdominio '{}'", "password_too_simple_4": "La pasvorto bezonas almenaŭ 12 signojn kaj enhavas ciferon, majuskle, pli malaltan kaj specialajn signojn", "regenconf_file_updated": "Agordodosiero '{conf}' ĝisdatigita", - "log_help_to_get_log": "Por vidi la protokolon de la operacio '{desc}', uzu la komandon 'yunohost log show {name}{name}'", + "log_help_to_get_log": "Por vidi la protokolon de la operacio '{desc}', uzu la komandon 'yunohost log show {name}'", "global_settings_setting_security_nginx_compatibility": "Kongruo vs sekureca kompromiso por la TTT-servilo NGINX. Afektas la ĉifradojn (kaj aliajn aspektojn pri sekureco)", "restore_complete": "Restarigita", "hook_exec_failed": "Ne povis funkcii skripto: {path}", diff --git a/locales/es.json b/locales/es.json index 9af875898..ecb12d912 100644 --- a/locales/es.json +++ b/locales/es.json @@ -325,7 +325,7 @@ "log_does_exists": "No existe ningún registro de actividades con el nombre '{log}', ejecute 'yunohost log list' para ver todos los registros de actividades disponibles", "log_help_to_get_failed_log": "No se pudo completar la operación «{desc}». Para obtener ayuda, comparta el registro completo de esta operación ejecutando la orden «yunohost log share {name}»", "log_link_to_failed_log": "No se pudo completar la operación «{desc}». Para obtener ayuda, proporcione el registro completo de esta operación pulsando aquí", - "log_help_to_get_log": "Para ver el registro de la operación «{desc}», ejecute la orden «yunohost log show {name}{name}»", + "log_help_to_get_log": "Para ver el registro de la operación «{desc}», ejecute la orden «yunohost log show {name}»", "log_link_to_log": "Registro completo de esta operación: «{desc}»", "log_corrupted_md_file": "El archivo de metadatos YAML asociado con el registro está dañado: «{md_file}\nError: {error}»", "hook_json_return_error": "No se pudo leer la respuesta del gancho {path}. Error: {msg}. Contenido sin procesar: {raw_content}", diff --git a/locales/fa.json b/locales/fa.json index 3e78c5de0..d9d3cb175 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -500,7 +500,7 @@ "log_does_exists": "هیچ گزارش عملیاتی با نام '{log}' وجود ندارد ، برای مشاهده همه گزارش عملیّات های موجود در خط فرمان از دستور 'yunohost log list' استفاده کنید", "log_help_to_get_failed_log": "عملیات '{desc}' کامل نشد. لطفاً برای دریافت راهنمایی و کمک ، گزارش کامل این عملیات را با استفاده از دستور 'yunohost log share {name}' به اشتراک بگذارید", "log_link_to_failed_log": "عملیّات '{desc}' کامل نشد. لطفاً گزارش کامل این عملیات را ارائه دهید بواسطه اینجا را کلیک کنید برای دریافت کمک", - "log_help_to_get_log": "برای مشاهده گزارش عملیات '{desc}'، از دستور 'yunohost log show {name}{name}' استفاده کنید", + "log_help_to_get_log": "برای مشاهده گزارش عملیات '{desc}'، از دستور 'yunohost log show {name}' استفاده کنید", "log_link_to_log": "گزارش کامل این عملیات: {desc}'", "log_corrupted_md_file": "فایل فوق داده YAML مربوط به گزارش ها آسیب دیده است: '{md_file}\nخطا: {error} '", "ldap_server_is_down_restart_it": "سرویس LDAP خاموش است ، سعی کنید آن را دوباره راه اندازی کنید...", diff --git a/locales/fr.json b/locales/fr.json index a11ef3b43..55eb4ecd4 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -251,7 +251,7 @@ "experimental_feature": "Attention : cette fonctionnalité est expérimentale et ne doit pas être considérée comme stable, vous ne devriez pas l'utiliser à moins que vous ne sachiez ce que vous faites.", "log_corrupted_md_file": "Le fichier YAML de métadonnées associé aux logs est corrompu : '{md_file}'\nErreur : {error}", "log_link_to_log": "Journal complet de cette opération : ' {desc} '", - "log_help_to_get_log": "Pour voir le journal de cette opération '{desc}', utilisez la commande 'yunohost log show {name}{name}'", + "log_help_to_get_log": "Pour voir le journal de cette opération '{desc}', utilisez la commande 'yunohost log show {name}'", "log_link_to_failed_log": "L'opération '{desc}' a échoué ! Pour obtenir de l'aide, merci de partager le journal de l'opération en cliquant ici", "log_help_to_get_failed_log": "L'opération '{desc}' a échoué ! Pour obtenir de l'aide, merci de partager le journal de l'opération en utilisant la commande 'yunohost log share {name}'", "log_does_exists": "Il n'y a pas de journal des opérations avec le nom '{log}', utilisez 'yunohost log list' pour voir tous les journaux d'opérations disponibles", diff --git a/locales/gl.json b/locales/gl.json index 1a3c570c2..7976c11e9 100644 --- a/locales/gl.json +++ b/locales/gl.json @@ -358,7 +358,7 @@ "global_settings_setting_security_webadmin_allowlist_enabled": "Permitir que só algúns IPs accedan á webadmin.", "disk_space_not_sufficient_update": "Non hai espazo suficiente no disco para actualizar esta aplicación", "disk_space_not_sufficient_install": "Non queda espazo suficiente no disco para instalar esta aplicación", - "log_help_to_get_log": "Para ver o rexistro completo da operación '{desc}', usa o comando 'yunohost log show {name}{name}'", + "log_help_to_get_log": "Para ver o rexistro completo da operación '{desc}', usa o comando 'yunohost log show {name}'", "log_link_to_log": "Rexistro completo desta operación: '{desc}'", "log_corrupted_md_file": "O ficheiro YAML con metadatos asociado aos rexistros está danado: '{md_file}\nErro: {error}'", "iptables_unavailable": "Non podes andar remexendo en iptables aquí. Ou ben estás nun contedor ou o teu kernel non ten soporte para isto", diff --git a/locales/it.json b/locales/it.json index dc998d8d4..7bd048ac7 100644 --- a/locales/it.json +++ b/locales/it.json @@ -249,7 +249,7 @@ "good_practices_about_admin_password": "Stai per impostare una nuova password di amministratore. La password deve essere almeno di 8 caratteri - anche se è buona pratica utilizzare password più lunghe (es. una frase, una serie di parole) e/o utilizzare vari tipi di caratteri (maiuscole, minuscole, numeri e simboli).", "log_corrupted_md_file": "Il file dei metadati YAML associato con i registri è danneggiato: '{md_file}'\nErrore: {error}", "log_link_to_log": "Registro completo di questa operazione: '{desc}'", - "log_help_to_get_log": "Per vedere il registro dell'operazione '{desc}', usa il comando 'yunohost log show {name}{name}'", + "log_help_to_get_log": "Per vedere il registro dell'operazione '{desc}', usa il comando 'yunohost log show {name}'", "global_settings_setting_security_postfix_compatibility": "Bilanciamento tra compatibilità e sicurezza per il server Postfix. Riguarda gli algoritmi di cifratura (e altri aspetti legati alla sicurezza)", "log_link_to_failed_log": "Impossibile completare l'operazione '{desc}'! Per ricevere aiuto, per favore fornisci il registro completo dell'operazione cliccando qui", "log_help_to_get_failed_log": "L'operazione '{desc}' non può essere completata. Per ottenere aiuto, per favore condividi il registro completo dell'operazione utilizzando il comando 'yunohost log share {name}'", diff --git a/locales/nb_NO.json b/locales/nb_NO.json index 037e09cb6..221f974ab 100644 --- a/locales/nb_NO.json +++ b/locales/nb_NO.json @@ -115,7 +115,7 @@ "domain_deletion_failed": "Kunne ikke slette domene", "domain_dyndns_already_subscribed": "Du har allerede abonnement på et DynDNS-domene", "log_link_to_log": "Full logg for denne operasjonen: '{desc}'", - "log_help_to_get_log": "For å vise loggen for operasjonen '{desc}', bruk kommandoen 'yunohost log show {name}{name}'", + "log_help_to_get_log": "For å vise loggen for operasjonen '{desc}', bruk kommandoen 'yunohost log show {name}'", "log_user_create": "Legg til '{}' bruker", "app_change_url_success": "{app} nettadressen er nå {domain}{path}", "app_install_failed": "Kunne ikke installere {app}: {error}" diff --git a/locales/oc.json b/locales/oc.json index 995c61b16..27d4aeca9 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -248,7 +248,7 @@ "experimental_feature": "Atencion : aquesta foncionalitat es experimentala e deu pas èsser considerada coma establa, deuriatz pas l’utilizar levat que sapiatz çò que fasètz.", "log_corrupted_md_file": "Lo fichièr YAML de metadonadas ligat als jornals d’audit es damatjat : « {md_file} »\nError : {error}", "log_link_to_log": "Jornal complèt d’aquesta operacion : {desc}", - "log_help_to_get_log": "Per veire lo jornal d’aquesta operacion « {desc} », utilizatz la comanda « yunohost log show {name}{name} »", + "log_help_to_get_log": "Per veire lo jornal d’aquesta operacion « {desc} », utilizatz la comanda « yunohost log show {name} »", "log_link_to_failed_log": "L’operacion « {desc} » a pas capitat ! Per obténer d’ajuda, mercés de fornir lo jornal complèt de l’operacion", "log_help_to_get_failed_log": "L’operacion « {desc} » a pas reüssit ! Per obténer d’ajuda, mercés de partejar lo jornal d’audit complèt d’aquesta operacion en utilizant la comanda « yunohost log share {name} »", "log_does_exists": "I a pas cap de jornal d’audit per l’operacion amb lo nom « {log} », utilizatz « yunohost log list » per veire totes los jornals d’operacion disponibles", diff --git a/locales/uk.json b/locales/uk.json index 0de15a830..150e8c240 100644 --- a/locales/uk.json +++ b/locales/uk.json @@ -248,7 +248,7 @@ "log_does_exists": "Немає журналу операцій з назвою '{log}', використовуйте 'yunohost log list', щоб подивитися всі доступні журнали операцій", "log_help_to_get_failed_log": "Операція '{desc}' не може бути завершена. Будь ласка, поділіться повним журналом цієї операції, використовуючи команду 'yunohost log share {name}', щоб отримати допомогу", "log_link_to_failed_log": "Не вдалося завершити операцію '{desc}'. Будь ласка, надайте повний журнал цієї операції, натиснувши тут, щоб отримати допомогу", - "log_help_to_get_log": "Щоб переглянути журнал операції '{desc}', використовуйте команду 'yunohost log show {name}{name}'", + "log_help_to_get_log": "Щоб переглянути журнал операції '{desc}', використовуйте команду 'yunohost log show {name}'", "log_link_to_log": "Повний журнал цієї операції: '{desc}'", "log_corrupted_md_file": "Файл метаданих YAML, пов'язаний з журналами, пошкоджено: '{md_file}\nПомилка: {error}'", "iptables_unavailable": "Ви не можете грати з iptables тут. Ви перебуваєте або в контейнері, або ваше ядро не підтримує його", diff --git a/locales/zh_Hans.json b/locales/zh_Hans.json index 560ee0db0..e6b4d1cc8 100644 --- a/locales/zh_Hans.json +++ b/locales/zh_Hans.json @@ -511,7 +511,7 @@ "log_does_exists": "没有名称为'{log}'的操作日志,请使用 'yunohost log list' 查看所有可用的操作日志", "log_help_to_get_failed_log": "操作'{desc}'无法完成。请使用命令'yunohost log share {name}' 共享此操作的完整日志以获取帮助", "log_link_to_failed_log": "无法完成操作 '{desc}'。请通过单击此处提供此操作的完整日志以获取帮助", - "log_help_to_get_log": "要查看操作'{desc}'的日志,请使用命令'yunohost log show {name}{name}'", + "log_help_to_get_log": "要查看操作'{desc}'的日志,请使用命令'yunohost log show {name}'", "log_link_to_log": "此操作的完整日志: '{desc}'", "log_corrupted_md_file": "与日志关联的YAML元数据文件已损坏: '{md_file}\n错误: {error}'", "iptables_unavailable": "你不能在这里使用iptables。你要么在一个容器中,要么你的内核不支持它", diff --git a/src/yunohost/app.py b/src/yunohost/app.py index cf58af597..871e3dc00 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -1652,25 +1652,34 @@ def app_action_run(operation_logger, app, action, args=None): ) env_dict["YNH_ACTION"] = action - _, path = tempfile.mkstemp() + tmp_workdir_for_app = _make_tmp_workdir_for_app(app=app) + _, action_script = tempfile.mkstemp(dir=tmp_workdir_for_app) - with open(path, "w") as script: + with open(action_script, "w") as script: script.write(action_declaration["command"]) - os.chmod(path, 700) - if action_declaration.get("cwd"): cwd = action_declaration["cwd"].replace("$app", app) else: - cwd = os.path.join(APPS_SETTING_PATH, app) + cwd = tmp_workdir_for_app - # FIXME: this should probably be ran in a tmp workdir... - retcode = hook_exec( - path, - env=env_dict, - chdir=cwd, - user=action_declaration.get("user", "root"), - )[0] + try: + retcode = hook_exec( + action_script, + env=env_dict, + chdir=cwd, + user=action_declaration.get("user", "root"), + )[0] + # Calling hook_exec could fail miserably, or get + # manually interrupted (by mistake or because script was stuck) + # In that case we still want to delete the tmp work dir + except (KeyboardInterrupt, EOFError, Exception): + retcode = -1 + import traceback + + logger.error(m18n.n("unexpected_error", error="\n" + traceback.format_exc())) + finally: + shutil.rmtree(tmp_workdir_for_app) if retcode not in action_declaration.get("accepted_return_codes", [0]): msg = "Error while executing action '%s' of app '%s': return code %s" % ( @@ -1681,8 +1690,6 @@ def app_action_run(operation_logger, app, action, args=None): operation_logger.error(msg) raise YunohostError(msg, raw_msg=True) - os.remove(path) - operation_logger.success() return logger.success("Action successed!") @@ -1760,7 +1767,6 @@ class AppConfigPanel(ConfigPanel): default_script = """#!/bin/bash source /usr/share/yunohost/helpers ynh_abort_if_errors -final_path=$(ynh_app_setting_get $app final_path) ynh_app_config_run $1 """ write_to_file(config_script, default_script) @@ -1768,11 +1774,13 @@ ynh_app_config_run $1 # Call config script to extract current values logger.debug(f"Calling '{action}' action from config script") app_id, app_instance_nb = _parse_app_instance_name(self.app) + settings = _get_app_settings(app_id) env.update( { "app_id": app_id, "app": self.app, "app_instance_nb": str(app_instance_nb), + "final_path": settings.get("final_path", "") } ) @@ -2173,6 +2181,13 @@ def _set_default_ask_questions(arguments): key = "app_manifest_%s_ask_%s" % (script_name, arg["name"]) arg["ask"] = m18n.n(key) + # Also it in fact doesn't make sense for any of those questions to have an example value nor a default value... + if arg.get("type") in ["domain", "user", "password"]: + if "example" in arg: + del arg["example"] + if "default" in arg: + del arg["domain"] + return arguments diff --git a/src/yunohost/log.py b/src/yunohost/log.py index b7b585c94..3f25d7a7d 100644 --- a/src/yunohost/log.py +++ b/src/yunohost/log.py @@ -69,7 +69,13 @@ def log_list(limit=None, with_details=False, with_suboperations=False): logs = list(reversed(sorted(logs))) if limit is not None: - logs = logs[:limit] + if with_suboperations: + logs = logs[:limit] + else: + # If we displaying only parent, we are still gonna load up to limit * 5 logs + # because many of them are suboperations which are not gonna be kept + # Yet we still want to obtain ~limit number of logs + logs = logs[:limit * 5] for log in logs: @@ -122,6 +128,9 @@ def log_list(limit=None, with_details=False, with_suboperations=False): else: operations = [o for o in operations.values()] + if limit: + operations = operations[:limit] + operations = list(reversed(sorted(operations, key=lambda o: o["name"]))) # Reverse the order of log when in cli, more comfortable to read (avoid # unecessary scrolling) @@ -151,26 +160,37 @@ def log_show( filter_irrelevant = True if filter_irrelevant: - filters = [ - r"set [+-]x$", - r"set [+-]o xtrace$", - r"local \w+$", - r"local legacy_args=.*$", - r".*Helper used in legacy mode.*", - r"args_array=.*$", - r"local -A args_array$", - r"ynh_handle_getopts_args", - r"ynh_script_progression", - ] + + def _filter(lines): + filters = [ + r"set [+-]x$", + r"set [+-]o xtrace$", + r"set [+-]o errexit$", + r"set [+-]o nounset$", + r"trap '' EXIT", + r"local \w+$", + r"local exit_code=(1|0)$", + r"local legacy_args=.*$", + r"local -A args_array$", + r"args_array=.*$", + r"ret_code=1", + r".*Helper used in legacy mode.*", + r"ynh_handle_getopts_args", + r"ynh_script_progression", + r"sleep 0.5", + r"'\[' (1|0) -eq (1|0) '\]'$", + r"\[?\['? -n '' '?\]\]?$", + r"rm -rf /var/cache/yunohost/download/$", + r"type -t ynh_clean_setup$", + r"DEBUG - \+ echo '", + r"DEBUG - \+ exit (1|0)$", + ] + filters = [re.compile(f) for f in filters] + return [line for line in lines if not any(f.search(line.strip()) for f in filters)] else: - filters = [] + def _filter(lines): + return lines - def _filter_lines(lines, filters=[]): - - filters = [re.compile(f) for f in filters] - return [ - line for line in lines if not any(f.search(line.strip()) for f in filters) - ] # Normalize log/metadata paths and filenames abs_path = path @@ -209,7 +229,7 @@ def log_show( content += "\n============\n\n" if os.path.exists(log_path): actual_log = read_file(log_path) - content += "\n".join(_filter_lines(actual_log.split("\n"), filters)) + content += "\n".join(_filter(actual_log.split("\n"))) url = yunopaste(content) @@ -282,13 +302,13 @@ def log_show( if os.path.exists(log_path): from yunohost.service import _tail - if number and filters: + if number and filter_irrelevant: logs = _tail(log_path, int(number * 4)) elif number: logs = _tail(log_path, int(number)) else: logs = read_file(log_path) - logs = _filter_lines(logs, filters) + logs = list(_filter(logs)) if number: logs = logs[-number:] infos["log_path"] = log_path diff --git a/src/yunohost/tests/test_questions.py b/src/yunohost/tests/test_questions.py index 93149b272..67b50769b 100644 --- a/src/yunohost/tests/test_questions.py +++ b/src/yunohost/tests/test_questions.py @@ -552,7 +552,7 @@ def test_question_password_input_test_ask(): prompt.assert_called_with( message=ask_text, is_password=True, - confirm=True, + confirm=False, prefill="", is_multiline=False, ) diff --git a/src/yunohost/utils/config.py b/src/yunohost/utils/config.py index abb901e84..0b6b472cd 100644 --- a/src/yunohost/utils/config.py +++ b/src/yunohost/utils/config.py @@ -203,7 +203,6 @@ class ConfigPanel: "panels": { "properties": ["name", "services", "actions", "help"], "default": { - "name": "", "services": [], "actions": {"apply": {"en": "Apply"}}, }, @@ -277,7 +276,9 @@ class ConfigPanel: continue subnode = convert(value, subnode_type) subnode["id"] = key - if node_type == "sections": + if node_type == "toml": + subnode.setdefault("name", {"en": key.capitalize()}) + elif node_type == "sections": subnode["name"] = key # legacy subnode.setdefault("optional", toml_node.get("optional", True)) node.setdefault(subnode_type, []).append(subnode) @@ -364,7 +365,8 @@ class ConfigPanel: display_header(f"\n{'='*40}\n>>>> {name}\n{'='*40}") continue name = _value_for_locale(section["name"]) - display_header(f"\n# {name}") + if name: + display_header(f"\n# {name}") # Check and ask unanswered questions self.new_values.update( @@ -473,6 +475,20 @@ class Question(object): def normalize(value, option={}): return value + def _prompt(self, text): + prefill = "" + if self.current_value is not None: + prefill = self.humanize(self.current_value, self) + elif self.default is not None: + prefill = self.humanize(self.default, self) + self.value = Moulinette.prompt( + message=text, + is_password=self.hide_user_input_in_prompt, + confirm=False, # We doesn't want to confirm this kind of password like in webadmin + prefill=prefill, + is_multiline=(self.type == "text"), + ) + def ask_if_needed(self): for i in range(5): # Display question if no value filled or if it's a readonly message @@ -480,20 +496,8 @@ class Question(object): text_for_user_input_in_cli = self._format_text_for_user_input_in_cli() if getattr(self, "readonly", False): Moulinette.display(text_for_user_input_in_cli) - elif self.value is None: - prefill = "" - if self.current_value is not None: - prefill = self.humanize(self.current_value, self) - elif self.default is not None: - prefill = self.humanize(self.default, self) - self.value = Moulinette.prompt( - message=text_for_user_input_in_cli, - is_password=self.hide_user_input_in_prompt, - confirm=self.hide_user_input_in_prompt, - prefill=prefill, - is_multiline=(self.type == "text"), - ) + self._prompt(text_for_user_input_in_cli) # Apply default value class_default = getattr(self, "default_value", None) @@ -529,7 +533,7 @@ class Question(object): raise YunohostValidationError("app_argument_required", name=self.name) # we have an answer, do some post checks - if self.value is not None: + if self.value not in [None, ""]: if self.choices and self.value not in self.choices: self._raise_invalid_answer() if self.pattern and not re.match(self.pattern["regexp"], str(self.value)): @@ -547,13 +551,13 @@ class Question(object): choices=", ".join(self.choices), ) - def _format_text_for_user_input_in_cli(self): + def _format_text_for_user_input_in_cli(self, column=False): text_for_user_input_in_cli = _value_for_locale(self.ask) if self.choices: text_for_user_input_in_cli += " [{0}]".format(" | ".join(self.choices)) - if self.help: + if self.help or column: text_for_user_input_in_cli += ":\033[m" if self.help: text_for_user_input_in_cli += "\n - " @@ -694,6 +698,23 @@ class PasswordQuestion(Question): assert_password_is_strong_enough("user", self.value) + def _format_text_for_user_input_in_cli(self): + need_column = self.current_value or self.optional + text_for_user_input_in_cli = super()._format_text_for_user_input_in_cli(need_column) + if self.current_value: + text_for_user_input_in_cli += "\n - " + m18n.n("app_argument_password_help_keep") + if self.optional: + text_for_user_input_in_cli += "\n - " + m18n.n("app_argument_password_help_optional") + + return text_for_user_input_in_cli + + def _prompt(self, text): + super()._prompt(text) + if self.current_value and self.value == "": + self.value = self.current_value + elif self.value == " ": + self.value = "" + class PathQuestion(Question): argument_type = "path" @@ -802,6 +823,14 @@ class UserQuestion(Question): super().__init__(question, user_answers) self.choices = user_list()["users"] + + if not self.choices: + raise YunohostValidationError( + "app_argument_invalid", + name=self.name, + error="You should create a YunoHost user first." + ) + if self.default is None: root_mail = "root@%s" % _get_maindomain() for user in self.choices.keys():