Skip to content

WIP: Packaging v3 #2070

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 24 commits into
base: dev
Choose a base branch
from
Draft

WIP: Packaging v3 #2070

wants to merge 24 commits into from

Conversation

alexAubin
Copy link
Member

@alexAubin alexAubin commented Mar 19, 2025

As foretold by The Prophecy

(NB: no need to try to review this, it's not nearly done, this is just the beginning, there's still a good 3 ~ 6 months of work ...)

TODO / Brain dump

  • Mechanism to identify which functions are defined in the app's scripts.sh (build, initialize, before_build_as_root etc ...)
  • Mechanism to call single bash functions as root or as $app
  • Draft the flow for install
  • Resource mechanism
    • Have nodejs / ruby / go / composer as resources (done in 12.1 for helpers 2.1)
    • Refactor such that all resources can be handled multiple time with a main one and extra resources (similar to what's already done for ports ... though uuugh that feels weird for install_dir / data_dir for example...) with if clauses and pydantic for property typing, default values, restriction to what may/must be defined in the manifests
    • How to make the code backward compatible for packaging v2 x_x
  • Find a good way to handle the permissions on install_dir after the build ($app can't chgrp to www-data)
  • (ongoing) Mechanism for declarative configurations
    • general architecture
    • possibility to merge manual changes with the new conf
    • properly garbage collect confs that ain't in the manifest anymore
    • handle if clause
    • proper restriction of the 'exposed' properties that can/must be defined in the manifest / probably used for doc
    • possibility to dry_run and get the diff of expected changes (or list manual changes with diff)
    • Implement the various confs classes:
      • nginx
      • php
      • systemd
      • fail2ban
      • cron
      • sudoers
      • app confs
      • logrotate? (or should we have a log_dir resource ? and logrotate handled in the systemd conf ? idk)
      • multimedia? (or should it be a resource? idk)
  • Iterate on the resource and configurations mechanism to also handle the backup/restore logic
  • Implement the actual remove, upgrade, backup, restore flow
  • Should the build happen in a separate dir ... how much can we move venvs ?
  • Should --keep be handled declaratively in the manifest ? Or even have a categorization of folders / files to flag them as conf / data / cache / source / dependencies / ..., idk
  • The setting / config_panel story urgh
  • Hooks directly from the /etc/yunohost/apps/*/hooks folders ?
  • ???
  • ???
  • Create the 'v3' helpers ? See how much we can get rid of ... explore having a mechanism a mechanism with "python helpers" for helpers we want to keep callable from bash but that would mean duplicated logic between bash and python
  • ???
  • ???
  • try to draft some automagic 2.1 -> 3 script
  • ???
  • ???
  • ???

Base automatically changed from new-resources to dev March 19, 2025 13:53
@alexAubin alexAubin force-pushed the packaging-v3 branch 4 times, most recently from da7ec5e to d659e58 Compare April 7, 2025 17:41
alexAubin added 13 commits May 29, 2025 14:17
…able to run app scripts snippets, in particular the future v3 app functions build/init etc
…p assets and files to keep during ynh_setup_source such that we can run it as non-root ... not super happy about it, it's a mess, we should probably find a simpler way to handle this under a common directory which becomes YNH_APP_BASEDIR..
…nd run the build() function from the app's scripts.sh instead of the install script (later we shall have a _pre_build_as_root, _post_build_as_root, the init phase, etc, gotta think about how to organize the code)
…ant as non-root because $app can't give a file to the www-data group ... we'll need to do this between build and init/migrate somehow

super().__init__(*args, **kwargs)

def reload():

Check notice

Code scanning / CodeQL

First parameter of a method is not named 'self' Note test

Normal methods should have at least one parameter (the first of which should be 'self').

Copilot Autofix

AI about 12 hours ago

To fix the issue, we need to determine the intended behavior of the reload method:

  1. If the method is meant to interact with the instance (e.g., access or modify instance attributes), the self parameter should be added as the first argument.
  2. If the method is independent of the instance, it should be decorated with @staticmethod to clarify its intent.

Given the context, it is likely that reload is intended to be an instance method, as it is defined within a class that represents configurations. Therefore, the best fix is to add the self parameter to the method definition.


Suggested changeset 1
src/tests/test_app_regenconf.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/tests/test_app_regenconf.py b/src/tests/test_app_regenconf.py
--- a/src/tests/test_app_regenconf.py
+++ b/src/tests/test_app_regenconf.py
@@ -54,3 +54,3 @@
 
-    def reload():
+    def reload(self):
         pass
EOF
@@ -54,3 +54,3 @@

def reload():
def reload(self):
pass
Copilot is powered by AI and may make mistakes. Always verify output.
ast.Eq: op.eq,
ast.NotEq: op.ne,
}
context["true"] = True

Check failure

Code scanning / CodeQL

Modification of parameter with default Error

This expression mutates a
default value
.

Copilot Autofix

AI 6 days ago

To fix the issue, replace the mutable default value ({}) of the context parameter in evaluate_simple_js_expression with None. Then, within the function, check if context is None and initialize it to an empty dictionary. This ensures that a new dictionary is created for each function call, avoiding unintended side effects caused by modifying a shared default value.

Steps to implement the fix:

  1. Change the default value of the context parameter in evaluate_simple_js_expression from {} to None.
  2. Add a check within evaluate_simple_js_expression to initialize context to an empty dictionary if it is None.

Suggested changeset 1
src/utils/eval.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/utils/eval.py b/src/utils/eval.py
--- a/src/utils/eval.py
+++ b/src/utils/eval.py
@@ -168,3 +168,5 @@
 
-def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = {}) -> bool:
+def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = None) -> bool:
+    if context is None:
+        context = {}
     if not expr.strip():
EOF
@@ -168,3 +168,5 @@

def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = {}) -> bool:
def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = None) -> bool:
if context is None:
context = {}
if not expr.strip():
Copilot is powered by AI and may make mistakes. Always verify output.
ast.NotEq: op.ne,
}
context["true"] = True
context["false"] = False

Check failure

Code scanning / CodeQL

Modification of parameter with default Error

This expression mutates a
default value
.

Copilot Autofix

AI 6 days ago

To fix the issue, we will replace the mutable default argument ({}) with None and initialize the dictionary within the function if context is None. This ensures that each call to the function gets a fresh dictionary, preventing unintended side effects from shared state.

Steps to fix:

  1. Update the evaluate_simple_js_expression function definition to set context=None as the default value.
  2. Add a check at the beginning of the function to initialize context as an empty dictionary if it is None.

Suggested changeset 1
src/utils/eval.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/utils/eval.py b/src/utils/eval.py
--- a/src/utils/eval.py
+++ b/src/utils/eval.py
@@ -168,3 +168,5 @@
 
-def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = {}) -> bool:
+def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = None) -> bool:
+    if context is None:
+        context = {}
     if not expr.strip():
EOF
@@ -168,3 +168,5 @@

def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = {}) -> bool:
def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = None) -> bool:
if context is None:
context = {}
if not expr.strip():
Copilot is powered by AI and may make mistakes. Always verify output.
}
context["true"] = True
context["false"] = False
context["null"] = None

Check failure

Code scanning / CodeQL

Modification of parameter with default Error

This expression mutates a
default value
.

Copilot Autofix

AI 6 days ago

To fix the issue, we will replace the mutable default value ({}) for the context parameter with a placeholder value (None). Inside the function, we will check if context is None and initialize it to an empty dictionary ({}) if necessary. This ensures that a new dictionary is created for each function call, avoiding the unintended sharing of state between calls.

Steps to fix:

  1. Update the evaluate_simple_js_expression function definition to set the default value of context to None instead of {}.
  2. Add a check at the beginning of the function to initialize context to an empty dictionary if it is None.

Suggested changeset 1
src/utils/eval.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/utils/eval.py b/src/utils/eval.py
--- a/src/utils/eval.py
+++ b/src/utils/eval.py
@@ -168,3 +168,5 @@
 
-def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = {}) -> bool:
+def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = None) -> bool:
+    if context is None:
+        context = {}
     if not expr.strip():
EOF
@@ -168,3 +168,5 @@

def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = {}) -> bool:
def evaluate_simple_js_expression(expr: str, context: dict[str, Any] = None) -> bool:
if context is None:
context = {}
if not expr.strip():
Copilot is powered by AI and may make mistakes. Always verify output.
src/service.py Outdated
Comment on lines 624 to 633
#if service not in services.keys():
# raise YunohostValidationError("service_unknown", service=service)

Check notice

Code scanning / CodeQL

Commented-out code Note

This comment appears to contain commented-out code.

Copilot Autofix

AI about 12 hours ago

To fix the issue, we need to either remove the commented-out code or reinstate it. Since the _run_service_command function already contains logic for handling services, reinstating the code would require validating its functionality and ensuring it integrates correctly with the existing logic. If the code is unnecessary, it should be removed entirely.

The best approach here is to remove the commented-out code, as there is no indication that it is required or functional. This will clean up the code and eliminate any confusion for future developers.


Suggested changeset 1
src/service.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/service.py b/src/service.py
--- a/src/service.py
+++ b/src/service.py
@@ -631,4 +631,3 @@
     services = _get_services()
-    # if service not in services.keys():
-    #    raise YunohostValidationError("service_unknown", service=service)
+    
 
EOF
@@ -631,4 +631,3 @@
services = _get_services()
# if service not in services.keys():
# raise YunohostValidationError("service_unknown", service=service)


Copilot is powered by AI and may make mistakes. Always verify output.
# Remove the lock if one was given
if need_lock and PID != 0:
_remove_lock(PID)

if wait_until_pattern:
try:
out, err = watch_log_proc.communicate(timeout=timeout)

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable out is not used.

Copilot Autofix

AI about 12 hours ago

To fix the issue, we need to remove the unused variable out while ensuring that the communicate() method is still called to preserve any side effects it may have. Since the variable err is also unused, we can simplify the call to communicate() by omitting the unpacking of its return values entirely. This will make the code cleaner and avoid confusion about unused variables.

The changes will be made in the function where the issue occurs, specifically on line 691. No additional imports, methods, or definitions are required.


Suggested changeset 1
src/service.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/service.py b/src/service.py
--- a/src/service.py
+++ b/src/service.py
@@ -690,3 +690,3 @@
         try:
-            out, err = watch_log_proc.communicate(timeout=timeout)
+            watch_log_proc.communicate(timeout=timeout)
         except subprocess.TimeoutExpired:
EOF
@@ -690,3 +690,3 @@
try:
out, err = watch_log_proc.communicate(timeout=timeout)
watch_log_proc.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
Copilot is powered by AI and may make mistakes. Always verify output.
# Remove the lock if one was given
if need_lock and PID != 0:
_remove_lock(PID)

if wait_until_pattern:
try:
out, err = watch_log_proc.communicate(timeout=timeout)

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable err is not used.

Copilot Autofix

AI about 12 hours ago

To fix the issue, we should remove the assignment to the unused variable err while preserving the assignment to out, as out is used later in the code. This can be achieved by modifying the communicate call to only capture the out variable. The communicate method returns a tuple, so we can directly assign the first element to out and ignore the second element (which corresponds to err). Alternatively, we can use a placeholder name like _ for the unused variable to explicitly indicate that it is not used.


Suggested changeset 1
src/service.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/service.py b/src/service.py
--- a/src/service.py
+++ b/src/service.py
@@ -690,3 +690,3 @@
         try:
-            out, err = watch_log_proc.communicate(timeout=timeout)
+            out, _ = watch_log_proc.communicate(timeout=timeout)
         except subprocess.TimeoutExpired:
EOF
@@ -690,3 +690,3 @@
try:
out, err = watch_log_proc.communicate(timeout=timeout)
out, _ = watch_log_proc.communicate(timeout=timeout)
except subprocess.TimeoutExpired:
Copilot is powered by AI and may make mistakes. Always verify output.
@@ -760,9 +846,9 @@
# such that /etc/yunohost/services.yml contains the minimal
# changes with respect to the base conf

conf_base = yaml.safe_load(open(SERVICES_CONF_BASE)) or {}
conf_base: dict[str, ServiceInfos] = yaml.safe_load(open(SERVICES_CONF_BASE)) or {}

Check warning

Code scanning / CodeQL

File is not always closed Warning

File is opened but is not closed.

Copilot Autofix

AI about 12 hours ago

To fix the issue, replace the open(SERVICES_CONF_BASE) call with a with statement. This ensures the file is closed automatically when the block is exited, even if an exception occurs. The yaml.safe_load function can be used directly within the with block to read the file's contents.


Suggested changeset 1
src/service.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/service.py b/src/service.py
--- a/src/service.py
+++ b/src/service.py
@@ -857,3 +857,4 @@
 
-    conf_base: dict[str, ServiceInfos] = yaml.safe_load(open(SERVICES_CONF_BASE)) or {}
+    with open(SERVICES_CONF_BASE, 'r') as file:
+        conf_base: dict[str, ServiceInfos] = yaml.safe_load(file) or {}
 
EOF
@@ -857,3 +857,4 @@

conf_base: dict[str, ServiceInfos] = yaml.safe_load(open(SERVICES_CONF_BASE)) or {}
with open(SERVICES_CONF_BASE, 'r') as file:
conf_base: dict[str, ServiceInfos] = yaml.safe_load(file) or {}

Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant