diff --git a/doc/helper_doc_template.md b/doc/helper_doc_template.md
index cf88e10ac..d41c0b6e9 100644
--- a/doc/helper_doc_template.md
+++ b/doc/helper_doc_template.md
@@ -10,11 +10,10 @@ routes:
Doc auto-generated by [this script](https://github.com/YunoHost/yunohost/blob/{{ current_commit }}/doc/generate_helper_doc.py) on {{data.date}} (YunoHost version {{data.version}})
{% for category, helpers in data.helpers %}
-### {{ category.upper() }}
+## {{ category.upper() }}
{% for h in helpers %}
-**{{ h.name }}**
+#### {{ h.name }}
[details summary="{{ h.brief }}" class="helper-card-subtitle text-muted"]
-
**Usage**: `{{ h.usage }}`
{%- if h.args %}
diff --git a/src/yunohost/app.py b/src/yunohost/app.py
index 569fbdb23..14ef6e3e7 100644
--- a/src/yunohost/app.py
+++ b/src/yunohost/app.py
@@ -56,7 +56,6 @@ from yunohost.utils import packages
from yunohost.utils.config import (
ConfigPanel,
parse_args_in_yunohost_format,
- Question,
)
from yunohost.utils.i18n import _value_for_locale
from yunohost.utils.error import YunohostError, YunohostValidationError
@@ -1713,8 +1712,6 @@ def app_config_set(
config_ = AppConfigPanel(app)
- Question.operation_logger = operation_logger
-
return config_.set(key, value, args, args_file, operation_logger=operation_logger)
@@ -1769,7 +1766,7 @@ ynh_app_config_run $1
"app_id": app_id,
"app": self.app,
"app_instance_nb": str(app_instance_nb),
- "final_path": settings.get("final_path", "")
+ "final_path": settings.get("final_path", ""),
}
)
diff --git a/src/yunohost/log.py b/src/yunohost/log.py
index 3f25d7a7d..f40470063 100644
--- a/src/yunohost/log.py
+++ b/src/yunohost/log.py
@@ -75,7 +75,7 @@ def log_list(limit=None, with_details=False, with_suboperations=False):
# 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]
+ logs = logs[: limit * 5]
for log in logs:
@@ -186,12 +186,17 @@ def log_show(
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)]
+ return [
+ line
+ for line in lines
+ if not any(f.search(line.strip()) for f in filters)
+ ]
+
else:
+
def _filter(lines):
return lines
-
# Normalize log/metadata paths and filenames
abs_path = path
log_path = None
diff --git a/src/yunohost/permission.py b/src/yunohost/permission.py
index 01330ad7f..5161430de 100644
--- a/src/yunohost/permission.py
+++ b/src/yunohost/permission.py
@@ -457,22 +457,26 @@ def permission_create(
"permission_creation_failed", permission=permission, error=e
)
- permission_url(
- permission,
- url=url,
- add_url=additional_urls,
- auth_header=auth_header,
- sync_perm=False,
- )
+ try:
+ permission_url(
+ permission,
+ url=url,
+ add_url=additional_urls,
+ auth_header=auth_header,
+ sync_perm=False,
+ )
- new_permission = _update_ldap_group_permission(
- permission=permission,
- allowed=allowed,
- label=label,
- show_tile=show_tile,
- protected=protected,
- sync_perm=sync_perm,
- )
+ new_permission = _update_ldap_group_permission(
+ permission=permission,
+ allowed=allowed,
+ label=label,
+ show_tile=show_tile,
+ protected=protected,
+ sync_perm=sync_perm,
+ )
+ except:
+ permission_delete(permission, force=True)
+ raise
logger.debug(m18n.n("permission_created", permission=permission))
return new_permission
diff --git a/src/yunohost/tests/test_questions.py b/src/yunohost/tests/test_questions.py
index 67b50769b..91372dffa 100644
--- a/src/yunohost/tests/test_questions.py
+++ b/src/yunohost/tests/test_questions.py
@@ -348,9 +348,7 @@ def test_question_password():
]
answers = {"some_password": "some_value"}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
- Question.operation_logger = MagicMock()
- with patch.object(Question.operation_logger, "data_to_redact", create=True):
- assert parse_args_in_yunohost_format(answers, questions) == expected_result
+ assert parse_args_in_yunohost_format(answers, questions) == expected_result
def test_question_password_no_input():
@@ -375,13 +373,9 @@ def test_question_password_input():
}
]
answers = {}
- Question.operation_logger = {"data_to_redact": []}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
- Question.operation_logger = MagicMock()
- with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
+ with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@@ -397,10 +391,7 @@ def test_question_password_input_no_ask():
answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
- Question.operation_logger = MagicMock()
- with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
+ with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@@ -417,20 +408,14 @@ def test_question_password_no_input_optional():
answers = {}
expected_result = OrderedDict({"some_password": ("", "password")})
- Question.operation_logger = MagicMock()
- with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(os, "isatty", return_value=False):
+ with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
questions = [
{"name": "some_password", "type": "password", "optional": True, "default": ""}
]
- Question.operation_logger = MagicMock()
- with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(os, "isatty", return_value=False):
+ with patch.object(os, "isatty", return_value=False):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@@ -446,10 +431,7 @@ def test_question_password_optional_with_input():
answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
- Question.operation_logger = MagicMock()
- with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
+ with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@@ -467,10 +449,7 @@ def test_question_password_optional_with_empty_input():
answers = {}
expected_result = OrderedDict({"some_password": ("", "password")})
- Question.operation_logger = MagicMock()
- with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(Moulinette, "prompt", return_value=""), patch.object(
+ with patch.object(Moulinette, "prompt", return_value=""), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@@ -487,10 +466,7 @@ def test_question_password_optional_with_input_without_ask():
answers = {}
expected_result = OrderedDict({"some_password": ("some_value", "password")})
- Question.operation_logger = MagicMock()
- with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
+ with patch.object(Moulinette, "prompt", return_value="some_value"), patch.object(
os, "isatty", return_value=True
):
assert parse_args_in_yunohost_format(answers, questions) == expected_result
@@ -540,10 +516,7 @@ def test_question_password_input_test_ask():
]
answers = {}
- Question.operation_logger = MagicMock()
with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(
os, "isatty", return_value=True
@@ -572,10 +545,7 @@ def test_question_password_input_test_ask_with_example():
]
answers = {}
- Question.operation_logger = MagicMock()
with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(
os, "isatty", return_value=True
@@ -599,10 +569,7 @@ def test_question_password_input_test_ask_with_help():
]
answers = {}
- Question.operation_logger = MagicMock()
with patch.object(
- Question.operation_logger, "data_to_redact", create=True
- ), patch.object(
Moulinette, "prompt", return_value="some_value"
) as prompt, patch.object(
os, "isatty", return_value=True
diff --git a/src/yunohost/utils/config.py b/src/yunohost/utils/config.py
index 6b823452b..447540e13 100644
--- a/src/yunohost/utils/config.py
+++ b/src/yunohost/utils/config.py
@@ -39,6 +39,7 @@ from moulinette.utils.filesystem import (
from yunohost.utils.i18n import _value_for_locale
from yunohost.utils.error import YunohostError, YunohostValidationError
+from yunohost.log import OperationLogger
logger = getActionLogger("yunohost.config")
CONFIG_PANEL_VERSION_SUPPORTED = 1.0
@@ -441,7 +442,6 @@ class ConfigPanel:
class Question(object):
hide_user_input_in_prompt = False
- operation_logger = None
pattern = None
def __init__(self, question, user_answers):
@@ -478,7 +478,7 @@ class Question(object):
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
+ confirm=False, # We doesn't want to confirm this kind of password like in webadmin
prefill=prefill,
is_multiline=(self.type == "text"),
)
@@ -575,13 +575,9 @@ class Question(object):
for data in data_to_redact
if urllib.parse.quote(data) != data
]
- if self.operation_logger:
- self.operation_logger.data_to_redact.extend(data_to_redact)
- elif data_to_redact:
- raise YunohostError(
- f"Can't redact {self.name} because no operation logger available in the context",
- raw_msg=True,
- )
+
+ for operation_logger in OperationLogger._instances:
+ operation_logger.data_to_redact.extend(data_to_redact)
return self.value
@@ -705,11 +701,17 @@ class PasswordQuestion(Question):
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)
+ 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")
+ 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")
+ text_for_user_input_in_cli += "\n - " + m18n.n(
+ "app_argument_password_help_optional"
+ )
return text_for_user_input_in_cli
@@ -832,7 +834,7 @@ class UserQuestion(Question):
raise YunohostValidationError(
"app_argument_invalid",
name=self.name,
- error="You should create a YunoHost user first."
+ error="You should create a YunoHost user first.",
)
if self.default is None: