Repository: cetmix/cetmix-tower
Pull request #264: [14.0][FIX] cetmix_tower_server
Author: login: ivs-cetmix
Labels: name: needs review
name: merged 🎉
name: approved
Created: 2025-04-23T12:35:41Z | Merged: 2025-04-23T17:46:40Z

- Use named variables in command code.
- Limit access to Tower general settings to the `root` group only.
- Error when rendering command with malformed code.

Task: 4617


<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Access to Tower general settings is now restricted to users in the root group.

- **Bug Fixes**
  - Improved error handling for commands with malformed template code, ensuring users receive clear validation errors.
  - Updated command code to use named variables for greater clarity and reliability.

- **Documentation**
  - Corrected spelling of "license" to "licence" in badges and documentation for consistency.

- **Tests**
  - Added a test to verify that malformed command templates raise appropriate validation errors.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/cetmix_tower/README.rst b/cetmix_tower/README.rst
index 604a33a0b..aa9d79d35 100644
--- a/cetmix_tower/README.rst
+++ b/cetmix_tower/README.rst
@@ -13,7 +13,7 @@ Cetmix Tower
 .. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
     :target: https://odoo-community.org/page/development-status
     :alt: Production/Stable
-.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
+.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
     :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
     :alt: License: AGPL-3
 .. |badge3| image:: https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github
diff --git a/cetmix_tower/readme/newsfragments/4574.bugfix b/cetmix_tower/readme/newsfragments/4574.bugfix
new file mode 100644
index 000000000..fb1b4bc86
--- /dev/null
+++ b/cetmix_tower/readme/newsfragments/4574.bugfix
@@ -0,0 +1 @@
+Use named variables in command code.
diff --git a/cetmix_tower/readme/newsfragments/4574.feature b/cetmix_tower/readme/newsfragments/4574.feature
new file mode 100644
index 000000000..a00364e2e
--- /dev/null
+++ b/cetmix_tower/readme/newsfragments/4574.feature
@@ -0,0 +1 @@
+Limit access to Tower general settings to the `root` group only.
diff --git a/cetmix_tower/readme/newsfragments/4617.bugfix b/cetmix_tower/readme/newsfragments/4617.bugfix
new file mode 100644
index 000000000..fd46e59b6
--- /dev/null
+++ b/cetmix_tower/readme/newsfragments/4617.bugfix
@@ -0,0 +1 @@
+Error when rendering command with malformed code.
diff --git a/cetmix_tower_server/README.rst b/cetmix_tower_server/README.rst
index 468853c7e..1590e598f 100644
--- a/cetmix_tower_server/README.rst
+++ b/cetmix_tower_server/README.rst
@@ -13,7 +13,7 @@ Cetmix Tower Server
 .. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
     :target: https://odoo-community.org/page/development-status
     :alt: Production/Stable
-.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
+.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
     :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
     :alt: License: AGPL-3
 .. |badge3| image:: https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github
diff --git a/cetmix_tower_server/models/cetmix_tower.py b/cetmix_tower_server/models/cetmix_tower.py
index 97526b28d..66d62eb88 100644
--- a/cetmix_tower_server/models/cetmix_tower.py
+++ b/cetmix_tower_server/models/cetmix_tower.py
@@ -163,7 +163,9 @@ def server_get_variable_value(
         if not server:
             return None
         result = self.env["cx.tower.variable.value"].get_by_variable_reference(
-            variable_reference, server.id, check_global
+            variable_reference=variable_reference,
+            server_id=server.id,
+            check_global=check_global,
         )
 
         # Get server defined value first
diff --git a/cetmix_tower_server/models/cx_tower_template_mixin.py b/cetmix_tower_server/models/cx_tower_template_mixin.py
index d06453b04..0b3686f44 100644
--- a/cetmix_tower_server/models/cx_tower_template_mixin.py
+++ b/cetmix_tower_server/models/cx_tower_template_mixin.py
@@ -1,9 +1,10 @@
 # Copyright (C) 2024 Cetmix OÜ
 # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
 from jinja2 import Environment, Template, meta
+from jinja2.exceptions import TemplateSyntaxError
 
-from odoo import api, fields, models
-from odoo.exceptions import UserError
+from odoo import _, api, fields, models
+from odoo.exceptions import UserError, ValidationError
 
 
 class CxTowerTemplateMixin(models.AbstractModel):
@@ -131,10 +132,9 @@ def get_variables(self):
             dict {'record_id': {variables}...}
                 NB: 'record_id' is String
         """
-        Environment()
         res = {}
         for rec in self:
-            res.update({str(rec.id): self.get_variables_from_code(rec.code)})
+            res[str(rec.id)] = self.get_variables_from_code(rec.code)
         return res
 
     def get_variables_from_code(self, code):
@@ -147,9 +147,12 @@ def get_variables_from_code(self, code):
             variables (List) variables (eg ['var','var2',..])
         """
         env = Environment()
-        ast = env.parse(code)
-        undeclared_variables = meta.find_undeclared_variables(ast)
-        return list(undeclared_variables) if undeclared_variables else []
+        try:
+            ast = env.parse(code)
+            undeclared_variables = meta.find_undeclared_variables(ast)
+            return list(undeclared_variables) if undeclared_variables else []
+        except TemplateSyntaxError as e:
+            raise ValidationError(_("Variable syntax error: %s", e)) from e
 
     def _prepare_variable_commands(self, field_names, force_record=None):
         """
diff --git a/cetmix_tower_server/readme/newsfragments/4574.bugfix b/cetmix_tower_server/readme/newsfragments/4574.bugfix
new file mode 100644
index 000000000..fb1b4bc86
--- /dev/null
+++ b/cetmix_tower_server/readme/newsfragments/4574.bugfix
@@ -0,0 +1 @@
+Use named variables in command code.
diff --git a/cetmix_tower_server/readme/newsfragments/4574.feature b/cetmix_tower_server/readme/newsfragments/4574.feature
new file mode 100644
index 000000000..a00364e2e
--- /dev/null
+++ b/cetmix_tower_server/readme/newsfragments/4574.feature
@@ -0,0 +1 @@
+Limit access to Tower general settings to the `root` group only.
diff --git a/cetmix_tower_server/readme/newsfragments/4617.bugfix b/cetmix_tower_server/readme/newsfragments/4617.bugfix
new file mode 100644
index 000000000..fd46e59b6
--- /dev/null
+++ b/cetmix_tower_server/readme/newsfragments/4617.bugfix
@@ -0,0 +1 @@
+Error when rendering command with malformed code.
diff --git a/cetmix_tower_server/static/description/index.html b/cetmix_tower_server/static/description/index.html
index 14b724ced..efd645170 100644
--- a/cetmix_tower_server/static/description/index.html
+++ b/cetmix_tower_server/static/description/index.html
@@ -369,7 +369,7 @@ <h1 class="title">Cetmix Tower Server</h1>
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 !! source digest: sha256:7e932fbefecc1c8902ef2db0ee5f72c42c665b2bda492006b1a5eec34cbc202c
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/cetmix/cetmix-tower/tree/14.0/cetmix_tower_server"><img alt="cetmix/cetmix-tower" src="https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github" /></a></p>
+<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/cetmix/cetmix-tower/tree/14.0/cetmix_tower_server"><img alt="cetmix/cetmix-tower" src="https://img.shields.io/badge/github-cetmix%2Fcetmix--tower-lightgray.png?logo=github" /></a></p>
 <p><a class="reference external" href="https://cetmix.com/tower">Cetmix Tower</a> offers a streamlined
 solution for managing remote servers and applications via SSH or API
 calls directly from <a class="reference external" href="https://odoo.com">Odoo</a>. It is designed for
diff --git a/cetmix_tower_server/tests/test_command.py b/cetmix_tower_server/tests/test_command.py
index 86d44f9e6..624175947 100644
--- a/cetmix_tower_server/tests/test_command.py
+++ b/cetmix_tower_server/tests/test_command.py
@@ -1,7 +1,7 @@
 from datetime import timedelta
 from unittest.mock import patch
 
-from odoo.exceptions import AccessError
+from odoo.exceptions import AccessError, ValidationError
 from odoo.fields import Datetime
 from odoo.tests.common import Form
 from odoo.tools import mute_logger
@@ -1712,3 +1712,17 @@ def test_check_zombie_commands(self):
             COMMAND_TIMED_OUT,
             "Commands should not be marked as timed out when timeout is disabled",
         )
+
+    def test_command_with_malformed_code(self):
+        """Test rendering command using `_render_command` method
+        of cx.tower.server with malformed code
+        """
+
+        with self.assertRaises(ValidationError):
+            self.Command.create(
+                {
+                    "name": "Test Malformed Command",
+                    "code": "cd {{ !@238203 }} && mkdir #!cxtower.secret.FOLDER!#",
+                    "action": "ssh_command",
+                }
+            )
diff --git a/cetmix_tower_server/views/res_config_settings.xml b/cetmix_tower_server/views/res_config_settings.xml
index 750af8cb5..b43832995 100644
--- a/cetmix_tower_server/views/res_config_settings.xml
+++ b/cetmix_tower_server/views/res_config_settings.xml
@@ -13,6 +13,7 @@
                     data-string="cetmix_tower_server"
                     string="Cetmix Tower"
                     data-key="cetmix_tower_server"
+                    groups="cetmix_tower_server.group_root"
                 >
                     <h2>Cetmix Tower Server</h2>
                     <div


Reviews:
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>🔭 Outside diff range comments (1)</summary><blockquote>

<details>
<summary>cetmix_tower_server/tests/test_command.py (1)</summary><blockquote>

`1716-1729`: _🧹 Nitpick (assertive)_

**Well-structured test that validates error handling for malformed templates.**

This test appropriately verifies that commands with malformed Jinja template syntax (like `{{ !@238203 }}`) trigger validation errors during creation, rather than causing runtime errors during rendering. This directly addresses the PR objective of improving error handling for malformed command templates.



Consider enhancing this test to also verify the specific error message content, ensuring it's user-friendly and provides clear guidance on how to fix the issue:

```diff
with self.assertRaises(ValidationError) as cm:
    self.Command.create(
        {
            "name": "Test Malformed Command",
            "code": "cd {{ !@238203 }} && mkdir #!cxtower.secret.FOLDER!#",
            "action": "ssh_command",
        }
    )
+# Verify the error message contains helpful information
+self.assertIn("malformed", str(cm.exception).lower())
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: ASSERTIVE**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 8749bad1bf59d3d7c9e4f1f5f62b280996312c00 and fec2cb43b82dae3799ceac54c7b364f589156d61.

</details>

<details>
<summary>📒 Files selected for processing (13)</summary>

* `cetmix_tower/README.rst` (1 hunks)
* `cetmix_tower/readme/newsfragments/4574.bugfix` (1 hunks)
* `cetmix_tower/readme/newsfragments/4574.feature` (1 hunks)
* `cetmix_tower/readme/newsfragments/4617.bugfix` (1 hunks)
* `cetmix_tower_server/README.rst` (1 hunks)
* `cetmix_tower_server/models/cetmix_tower.py` (1 hunks)
* `cetmix_tower_server/models/cx_tower_template_mixin.py` (3 hunks)
* `cetmix_tower_server/readme/newsfragments/4574.bugfix` (1 hunks)
* `cetmix_tower_server/readme/newsfragments/4574.feature` (1 hunks)
* `cetmix_tower_server/readme/newsfragments/4617.bugfix` (1 hunks)
* `cetmix_tower_server/static/description/index.html` (1 hunks)
* `cetmix_tower_server/tests/test_command.py` (2 hunks)
* `cetmix_tower_server/views/res_config_settings.xml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🧠 Learnings (1)</summary>

<details>
<summary>cetmix_tower_server/README.rst (1)</summary>

```
Learnt from: ivs-cetmix
PR: cetmix/cetmix-tower#237
File: cetmix_tower_server/README.rst:1005-1006
Timestamp: 2025-03-31T20:04:24.444Z
Learning: The README.rst file in cetmix_tower_server is automatically generated from a repository template and should not be manually edited or have its issues flagged in reviews.
```

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (3)</summary>

* GitHub Check: Detect unreleased dependencies
* GitHub Check: test with OCB
* GitHub Check: pre-commit

</details>

<details>
<summary>🔇 Additional comments (13)</summary><blockquote>

<details>
<summary>cetmix_tower/README.rst (1)</summary>

`16-16`: **Approve British English spelling correction**  
The badge URL has been updated from “license” to “licence” to align with the project’s use of British English. This change is consistent with other documentation files.

</details>
<details>
<summary>cetmix_tower_server/static/description/index.html (1)</summary>

`372-372`: **Approve spelling update in badge URL**  
The AGPL badge `src` attribute now uses “licence” instead of “license,” matching the British English convention adopted across the project. No further changes needed.

</details>
<details>
<summary>cetmix_tower_server/README.rst (1)</summary>

`16-16`: **Skip review for autogenerated file**  
This README.rst is generated by oca‑gen‑addon‑readme and will be overwritten on regeneration.

</details>
<details>
<summary>cetmix_tower/readme/newsfragments/4617.bugfix (1)</summary>

`1-1`: **Clear documentation for the bugfix.**

The bugfix is clearly documented, identifying the specific issue that has been resolved - an error when rendering commands with malformed code. This aligns with the changes implemented in the code.

</details>
<details>
<summary>cetmix_tower/readme/newsfragments/4574.bugfix (1)</summary>

`1-1`: **Concise documentation for the bugfix.**

The bugfix is well documented, stating the implementation of named variables in command code. This improves clarity and maintainability.

</details>
<details>
<summary>cetmix_tower/readme/newsfragments/4574.feature (1)</summary>

`1-1`: **Important security enhancement documented.**

This feature note clearly documents an important security improvement - restricting Tower general settings access to the root group only. This aligns with the PR objectives of enhancing security by limiting permissions.

</details>
<details>
<summary>cetmix_tower_server/models/cx_tower_template_mixin.py (3)</summary>

`4-4`: **Appropriate imports added for error handling.**

The imports of `TemplateSyntaxError`, `_` for translation, and `ValidationError` are necessary for the error handling improvements implemented in this file.



Also applies to: 6-7

---

`137-137`: **Simplified dictionary update.**

The direct assignment for dictionary update is cleaner than the previous implementation. This uses named variables for clarity, matching the bugfix description.

---

`150-155`: **Proper exception handling for template syntax errors.**

Excellent implementation of error handling for malformed template code. The code now:
1. Catches specific `TemplateSyntaxError` exceptions
2. Raises a user-friendly `ValidationError` with a translated message
3. Includes the original error details for context

This directly addresses the bugfix mentioned in the news fragment about handling errors when rendering commands with malformed code.

</details>
<details>
<summary>cetmix_tower_server/readme/newsfragments/4574.bugfix (1)</summary>

`1-1`: **Approve named variable usage update.**

The entry “Use named variables in command code.” succinctly captures the improvement. It aligns with the code changes making calls more readable and less error‑prone.

</details>
<details>
<summary>cetmix_tower_server/models/cetmix_tower.py (1)</summary>

`165-169`: <details>
<summary>✅ Verification successful</summary>

**Approve switching to keyword arguments for clarity.**

Using explicit parameter names (`variable_reference`, `server_id`, `check_global`) makes the invocation of `get_by_variable_reference` more self‑documenting and safer against argument reordering. 



Please confirm that the method signature of `get_by_variable_reference` in `cx_tower_variable_value` accepts these exact keyword parameters. You can verify with:

---

🏁 Script executed:

```shell
rg -n "def get_by_variable_reference" -n cetmix_tower_server/models
```

Length of output: 162

---

To confirm the available parameters on `get_by_variable_reference`, let’s pull the method signature and its immediate parameters:

---

🏁 Script executed:

```shell
#!/bin/bash
rg -n "def get_by_variable_reference" -n cetmix_tower_server/models/cx_tower_variable_value.py -A 5
```

Length of output: 286

---



</details>

**Confirmed `get_by_variable_reference` signature and keyword args**

The method in `cetmix_tower_server/models/cx_tower_variable_value.py` is declared as:

```python
def get_by_variable_reference(
    self,
    variable_reference,
    server_id=None,
    server_template_id=None,
    check_global=True,
    …
)
```

– It accepts `variable_reference`, `server_id`, and `check_global` as keyword arguments (with `server_template_id` available if needed).  
– The invocation matches the signature exactly and is safe to merge.

</details>
<details>
<summary>cetmix_tower_server/views/res_config_settings.xml (1)</summary>

`16-16`: <details>
<summary>✅ Verification successful</summary>

**Restrict settings view to root group.**

Adding `groups="cetmix_tower_server.group_root"` correctly gates the settings block to root users only. Verify that the `group_root` XML ID is defined and that the module’s security files grant it to the intended users to avoid view loading errors.



You can check the group’s definition with:

---

🏁 Script executed:

```shell
rg -n "group_root" -n cetmix_tower_server/security
```

Length of output: 6949

---



</details>

**Confirmed: `group_root` is defined and access is granted**  
The `group_root` XML ID exists in `cetmix_tower_server/security/cetmix_tower_server_groups.xml` and is referenced by multiple `ir.model.access.csv` entries, so the view restriction will load without errors.

</details>
<details>
<summary>cetmix_tower_server/tests/test_command.py (1)</summary>

`4-4`: **Import added to support the new test case.**

Adding `ValidationError` to imports is necessary for the new test that verifies validation behavior for malformed command templates.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T12:38:17Z
user: login: ivs-cetmix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T12:39:41Z
user: login: ivs-cetmix
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T12:39:59Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T12:40:00Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T12:40:21Z
user: login: Aldeigja
body: Functional LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T15:07:53Z
user: login: GabbasovDinar
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T17:39:23Z

Review comments:
diff_hunk: @@ -0,0 +1 @@
+Error when rendering command with malformed code.
path: cetmix_tower_server/readme/newsfragments/4617.bugfix
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Enhance bugfix description for clarity.**

The current note “Error when rendering command with malformed code.” is concise but could benefit from more context. Consider mentioning the raised `ValidationError` and providing an example of malformed template syntax so that users reading the release notes understand the scope and resolution of the fix.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-23T12:38:16Z
author_association: NONE
diff_hunk: @@ -0,0 +1 @@
+Limit access to Tower general settings to the `root` group only.
path: cetmix_tower_server/readme/newsfragments/4574.feature
user: login: coderabbitai[bot]
body: _🧹 Nitpick (assertive)_

**Document access restriction feature.**

The fragment clearly states that Tower general settings are limited to the `root` group. For completeness, you might add a brief rationale (e.g., enhanced security for sensitive settings) or point users to the corresponding view change to improve traceability.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-23T12:38:16Z
author_association: NONE
diff_hunk: @@ -0,0 +1 @@
+Limit access to Tower general settings to the `root` group only.
path: cetmix_tower_server/readme/newsfragments/4574.feature
user: login: ivs-cetmix
body: Yes, this will be added in the documentation.
created_at: 2025-04-23T12:39:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1 @@
+Error when rendering command with malformed code.
path: cetmix_tower_server/readme/newsfragments/4617.bugfix
user: login: ivs-cetmix
body: This will be added in the documentation.
created_at: 2025-04-23T12:39:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1 @@
+Limit access to Tower general settings to the `root` group only.
path: cetmix_tower_server/readme/newsfragments/4574.feature
user: login: coderabbitai[bot]
body: @ivs-cetmix Great! Thank you for confirming. Adding the rationale in the documentation will help users understand the security benefits of this access restriction.

🐰 👍

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-23T12:40:00Z
author_association: NONE
diff_hunk: @@ -0,0 +1 @@
+Error when rendering command with malformed code.
path: cetmix_tower_server/readme/newsfragments/4617.bugfix
user: login: coderabbitai[bot]
body: @ivs-cetmix Sounds good! Adding those details to the documentation will be helpful for users. Thank you for considering my suggestion.

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-04-23T12:40:21Z
author_association: NONE

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-04-23T12:35:48Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces several documentation corrections, bugfix notes, feature descriptions, code improvements, and access control enhancements across the `cetmix_tower` and `cetmix_tower_server` modules. The spelling of "license" was standardized to "licence" in multiple documentation files. News fragments were added to document bugfixes and a new feature restricting Tower general settings access to the `root` group. Code changes include improved error handling for malformed template code in the command model, explicit use of named arguments in a method call, and a new test verifying validation on malformed command templates. Access control in the settings view was updated to limit visibility to root group users.

## Changes

| Files/Paths                                                                                   | Change Summary                                                                                                                                                                                                                          |
|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| cetmix_tower/README.rst<br>cetmix_tower_server/README.rst<br>cetmix_tower_server/static/description/index.html | Corrected the spelling of "license" to "licence" in badge image URLs, labels, and alt text.                                                                                                      |
| cetmix_tower/readme/newsfragments/4574.bugfix<br>cetmix_tower_server/readme/newsfragments/4574.bugfix | Added news fragments noting the use of named variables in command code.                                                                                                                          |
| cetmix_tower/readme/newsfragments/4574.feature<br>cetmix_tower_server/readme/newsfragments/4574.feature | Added news fragments describing a new feature restricting Tower general settings access to users in the `root` group.                                                                            |
| cetmix_tower/readme/newsfragments/4617.bugfix<br>cetmix_tower_server/readme/newsfragments/4617.bugfix | Added news fragments documenting a bugfix for errors when rendering commands with malformed code.                                                                                                |
| cetmix_tower_server/models/cetmix_tower.py                                                    | Changed the call to `get_by_variable_reference` in `server_get_variable_value` to use named keyword arguments instead of positional arguments.                                                    |
| cetmix_tower_server/models/cx_tower_template_mixin.py                                         | Added error handling for malformed Jinja2 template code in `get_variables_from_code`, raising a translated `ValidationError` when a syntax error is detected.                                    |
| cetmix_tower_server/tests/test_command.py                                                     | Added a test (`test_command_with_malformed_code`) to verify that creating a command with malformed template code raises a `ValidationError`.                                                     |
| cetmix_tower_server/views/res_config_settings.xml                                             | Restricted visibility of the Cetmix Tower settings section to users in the `cetmix_tower_server.group_root` group by adding a `groups` attribute to the main settings container in the view.     |

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=cetmix/cetmix-tower&utm_content=264):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: ivs-cetmix
created_at: 2025-04-23T17:40:33Z
body: /ocabot merge patch
author_association: CONTRIBUTOR
user: login: CetmixGitDrone
created_at: 2025-04-23T17:40:40Z
body: This PR looks fantastic, let's merge it!
Prepared branch [14.0-ocabot-merge-pr-264-by-ivs-cetmix-bump-patch](https://github.com/cetmix/cetmix-tower/commits/14.0-ocabot-merge-pr-264-by-ivs-cetmix-bump-patch), awaiting test results.
author_association: NONE
user: login: CetmixGitDrone
created_at: 2025-04-23T17:46:41Z
body: Congratulations, your PR was merged at 0aa3916fa7669e388bd4ea348f91ff4ccb47f331. Thanks a lot for contributing to cetmix. ❤️
author_association: NONE