Skip to content

[18.0][IMP] cetmix_tower_server Jets#484

Merged
CetmixGitDrone merged 4 commits into18.0from
18.0-t5337-cetmix_tower_server-mig-jets
Apr 7, 2026
Merged

[18.0][IMP] cetmix_tower_server Jets#484
CetmixGitDrone merged 4 commits into18.0from
18.0-t5337-cetmix_tower_server-mig-jets

Conversation

@ivs-cetmix
Copy link
Copy Markdown
Contributor

@ivs-cetmix ivs-cetmix commented Mar 30, 2026

Jets are coming to 18.0 🎉

Task 5337

Summary by CodeRabbit

Release Notes

  • New Features

    • Introduced Jets—a new system for creating and managing containerized applications with templates, lifecycle states, and dependency tracking.
    • Added support for jet installations, state transitions, and waypoint-based workflows.
    • Demo data includes pre-configured templates for Odoo, WordPress, WooCommerce, Docker, and other services.
    • Enhanced command and flight plan execution to support jet context.
  • Improvements

    • Added comprehensive access control and security rules for Jets.
    • Extended variable resolution system to support jet-scoped values.

Initial implementation of the Jet and Jet Template models.

Task: 4700
Do not queue commands with actions "Jet Action" and "Create Waypoint".
These commands either launch other queued commands or run instantly.
Export/import jet templates and dependencies.
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 30, 2026

Walkthrough

This pull request introduces a comprehensive "Jets" feature to the cetmix_tower_server module. Jets are managed computational entities that can be provisioned from templates onto servers. The feature includes new models for jet templates, states, actions, dependencies, and waypoints; extends existing models (servers, commands, plans, variables) to support jet context; adds security rules for role-based access control; includes data migrations for schema changes; and provides extensive test coverage for the new functionality.

Changes

Cohort / File(s) Summary
Manifest & Core Configuration
__manifest__.py, models/__init__.py, migrations/18.0.3.0.0/pre-migration.py
Added module dependency cx_web_refresh_from_backend, referenced new security/data/view/demo XML files, and imported new model modules. Pre-migration drops a legacy database constraint on cx_tower_variable_value.
Jet State & Action Models
models/cx_tower_jet_state.py, models/cx_tower_jet_action.py, models/cx_tower_jet_request.py
Introduced jet state lifecycle management with access-level controls, jet action definitions for state transitions, and jet request orchestration for state provisioning and dependency satisfaction.
Jet Core Model
models/cx_tower_jet.py
Added primary cx.tower.jet model with 1700+ lines managing jet provisioning, state/action orchestration, cloning, waypoint creation, dependency control, and operational methods (run commands/plans, state transitions).
Jet Template & Dependencies
models/cx_tower_jet_template.py, models/cx_tower_jet_template_dependency.py, models/cx_tower_jet_dependency.py
Implemented jet template definitions with dependency graph algorithms (cycle detection, SVG generation), template-level dependencies with state requirements, and concrete jet-to-jet dependency relationships.
Jet Installation & Waypoints
models/cx_tower_jet_template_install.py, models/cx_tower_jet_template_install_line.py, models/cx_tower_jet_waypoint.py, models/cx_tower_jet_waypoint_template.py
Added template installation tracking/orchestration with multi-step plans and line items, and waypoint management for jet lifecycle checkpoints with state transitions and plan execution.
Extended Existing Models
models/cetmix_tower.py, models/cx_tower_server.py, models/cx_tower_command.py, models/cx_tower_plan.py
Extended public APIs with jet context parameters, added jet-specific command runners and action/waypoint creation support, included jet fields in command/plan logs, and updated variable resolution.
Variable & Metadata Systems
models/cx_tower_variable.py, models/cx_tower_variable_mixin.py, models/cx_tower_variable_value.py, models/cx_tower_metadata_mixin.py
Refactored variable resolution to support jet/template context with new system variables (tower dict with tools/server/jet metadata), removed legacy global fallback logic, and added metadata mixin for record annotations.
Extended Model Support
models/cx_tower_file.py, models/cx_tower_file_template.py, models/cx_tower_server_log.py, models/cx_tower_command_log.py, models/cx_tower_plan_log.py, models/cx_tower_scheduled_task.py
Added jet/template context to files, logs, and scheduled tasks; updated variable rendering pipelines to include jet context; and enabled scheduled tasks to target jets.
Utilities & Constants
models/cx_tower_key.py, models/cx_tower_template_mixin.py, models/constants.py, models/tools.py, models/res_users.py
Added 8 jet-specific error constants, URL validation utility, metadata mixin, user access-level hierarchy, and docstring clarifications.
Data & Demo Files
data/cx_tower_jet_state.xml, demo/demo_data.xml, demo/demo_jets.xml
Defined 11 initial jet states, created demo commands/plans for install/uninstall/clone workflows, and populated 2100+ lines of demo data with templates, dependencies, actions, scheduled tasks, and sample jets.
Security Rules
security/cx_tower_jet*_security.xml, security/cx_tower_scheduled_task*_security.xml, security/cx_tower_server_log_security.xml, security/cx_tower_variable_value_security.xml, security/cx_tower_server_wizard_access_rules.xml
Introduced 20+ record-level security rules for role-based access (user/manager/root) across jet models, waypoints, and dependencies, with domain constraints based on user/manager membership. Consolidated and expanded variable value rule domains.
Comprehensive Test Suite
tests/__init__.py, tests/common.py, tests/common_jets.py, tests/test_jet.py, tests/test_jet_access.py, tests/test_jet_action_access.py, tests/test_jet_dependency_access.py, tests/test_jet_state.py, tests/test_jet_create_wizard.py, tests/test_cetmix_tower.py, tests/test_command.py
Added 1500+ lines of jet test utilities and 2800+ lines of comprehensive tests covering jet state transitions, access control, dependencies, waypoint creation, command/plan integration, and various user role scenarios.
Documentation
readme/diagrams/jets.puml, readme/newsfragments/4700.feature
Added PlantUML workflow diagrams for new jet flows and feature announcement.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 27

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cetmix_tower_server/models/cx_tower_server.py (1)

1158-1194: ⚠️ Potential issue | 🟠 Major

Initialize the rendered values before the variable-resolution branch.

If the command/path contains placeholders but _get_variable_values_by_references() returns an empty dict, the if variable_values: block is skipped and rendered_code / rendered_path are never assigned. run_command() then crashes with UnboundLocalError.

🔧 Suggested change
         # Get variables from path
         path = path if path else command.path
+        rendered_code = command.code
+        rendered_path = path
         if path:
             variables_extracted = command.get_variables_from_code(path)
             for ve in variables_extracted:
                 if ve not in variable_references:
                     variable_references.append(ve)
@@
             # Render command code and path using variables
             if variable_values:
                 if command.action == "python_code":
                     variable_values["pythonic_mode"] = True

                 rendered_code = (
                     command.render_code_custom(command.code, **variable_values)
                     if command.code
                     else False
                 )
                 rendered_path = (
                     command.render_code_custom(path, **variable_values)
                     if path
                     else False
                 )
-
-        else:
-            rendered_code = command.code
-            rendered_path = path

         return {"rendered_code": rendered_code, "rendered_path": rendered_path}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cetmix_tower_server/models/cx_tower_server.py` around lines 1158 - 1194,
Initialize rendered_code and rendered_path before the variable_references branch
so they are always defined; specifically, at the start of the block that checks
variable_references set rendered_code = command.code and rendered_path = path
(or False if you prefer previous falsy behavior), then proceed to fetch
variable_values via _get_variable_values_by_references and apply
custom_variable_values and rendering (via command.render_code_custom) only to
override those defaults; reference symbols: variable_references,
_get_variable_values_by_references, custom_variable_values,
_have_access_to_server, command.render_code_custom, rendered_code,
rendered_path, run_command.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cetmix_tower_server/demo/demo_data.xml`:
- Around line 1392-1426: The three demo scheduled task records
(cx_tower_scheduled_task_odoo, cx_tower_scheduled_task_wordpress,
cx_tower_scheduled_task_woocommerce) currently set only cadence and action but
lack concrete execution targets; add explicit server_ids and/or jet_ids fields
to each record (not just jet_template_ids/server_template_ids) so the scheduler
has actual targets to run against. Update each record to include <field
name="server_ids"> and/or <field name="jet_ids"> referencing the appropriate
demo server/jet records used elsewhere in the demo data (match the live demo
server/jet resource record IDs for Odoo, WordPress and WooCommerce). Ensure the
new fields use many2many/one2many references consistent with existing scheduled
task model relationships.

In `@cetmix_tower_server/models/cx_tower_command_log.py`:
- Around line 145-154: The computed method _compute_jet_id in
cx_tower_command_log currently only sets jet_id and jet_template_id when
plan_log_id exists, leaving stale stored values when plan_log_id is cleared;
update _compute_jet_id to explicitly clear those fields when plan_log_id is
falsy (e.g., add an else branch on the for-loop that calls
command_log.update({"jet_id": False, "jet_template_id": False}) or assigns False
to command_log.jet_id and command_log.jet_template_id) so the stored computed
fields are reset when plan_log_id is removed.

In `@cetmix_tower_server/models/cx_tower_command.py`:
- Around line 401-411: The python_libraries mapping currently exposes the
module-level _logger into the eval context; remove direct exposure of _logger
and instead create and expose a constrained logging wrapper (e.g., SafeLogger)
used by cx_tower_command evaluation routines: implement SafeLogger with only a
minimal, whitelisted API (e.g., info/warn/error) that sanitizes or formats
messages and does not allow attaching handlers or reading internal state, then
replace the "_logger": {"import": _logger, ...} entry in python_libraries with
the SafeLogger instance; alternatively, if logging from eval is not required,
drop the _logger entry entirely from python_libraries to eliminate the risk.

In `@cetmix_tower_server/models/cx_tower_jet_action.py`:
- Line 63: The TODO indicates a missing data-integrity constraint: implement an
Odoo `@api.constrains` on the CxTowerJetAction model to verify that all linked
action records share the same jet template; inside the constraint method (e.g.,
_check_actions_same_jet_template) iterate the record's action relation (likely
action_ids or actions) and compare each action.jet_template_id (or the correct
field name on the related action model) against the parent record's
jet_template_id, and raise a ValidationError if any mismatch is found; add unit
tests exercising mismatched and matched cases.

In `@cetmix_tower_server/models/cx_tower_jet_request.py`:
- Around line 87-135: The _create_request method uses the server parameter as a
single record (server.id and server.name) but is decorated with `@api.model` so
server may be a multi-recordset; add a singleton check by calling
server.ensure_one() near the top of _create_request (before any use of server.id
or server.name) and, if appropriate, raise a ValidationError when server is
falsy to make the requirement explicit; update the code paths that reference
server.id and server.name to rely on the ensured single record.

In `@cetmix_tower_server/models/cx_tower_jet_template_dependency.py`:
- Around line 109-135: _build_dependency_graph currently does a full table scan
via self.search([]), which is inefficient; implement a cached dependency graph
(e.g., a model-level attribute like _dependency_graph_cache) and change
_build_dependency_graph to return the cached graph if present, otherwise build
it, store it in _dependency_graph_cache, and return it; add cache invalidation
by clearing _dependency_graph_cache in the model methods that modify
dependencies (create, write, unlink on the CxTowerJetTemplateDependency model)
so updates to template_id/template_required_id are reflected; alternatively, for
cycle detection replace the Python graph build with a PostgreSQL recursive CTE
query using template_id and template_required_id to detect cycles without
loading the full table.

In `@cetmix_tower_server/models/cx_tower_jet_template_install.py`:
- Around line 324-337: The notification and view-reload calls inside
_flight_plan_finished() (and the similar block around lines 444-459) currently
call notify_success/notify_error and reload_views on self.env.user, which
notifies the callback executor; change these to use the job requester instead by
calling notify_success/notify_error and reload_views on self.requested_by_id
(falling back to self.env.user if requested_by_id is falsy) so the user who
started the install receives completion notifications and backend refreshes;
update all occurrences (e.g., the notify_success call shown, any notify_* and
reload_views invocations) to reference self.requested_by_id accordingly.
- Around line 229-251: The code writes current_line_id
(self.write({"current_line_id": installation_task.id})) before calling
server_id.run_flight_plan, so any exception during run_flight_plan leaves the
job stuck; change the flow to only set current_line_id after run_flight_plan
succeeds (move the write to after the successful call), or wrap
server_id.run_flight_plan in a try/except and in the except clear/reset
current_line_id and set the install state appropriately (e.g., mark failed or
remove current_line_id) so _process_install won't remain stuck; reference
current_line_id, installation_task.id, server_id.run_flight_plan and
_process_install when making the change.

In `@cetmix_tower_server/models/cx_tower_jet_template.py`:
- Around line 911-925: The helper _allow_jet_creation currently always returns
True and must enforce the "template installed on server" invariant used by
create_jet() and clone(); update _allow_jet_creation(self, server) (after
self.ensure_one()) to check the template's installed-server relation (e.g.,
self.installed_on, self.installed_server_ids, or whatever field holds servers
where the template is installed) and return True only if the given server is
present there (return False otherwise); ensure callers create_jet() and clone()
continue to use this boolean result (or if you prefer stricter behavior, have
_allow_jet_creation raise a clear ValidationError/AccessError when the server is
not in the installed list).
- Around line 820-843: The current logic in the cx.tower.jet name allocation
loop treats caller-supplied names the same as auto-generated ones and will
silently replace a requested name if it already exists; modify the routine that
sets name (referencing jet_obj, existing_names, _generate_jet_name, and
MAX_JET_NAME_RETRIES) so that if name was provided by the caller and already in
existing_names you immediately raise a ValidationError (or return a clear
failure) instead of entering the retry/generation loop; only perform the retry
loop when name was not supplied (i.e., it was generated by _generate_jet_name),
keeping the existing unique-name retry behavior for autogenerated names.

In `@cetmix_tower_server/models/cx_tower_jet_waypoint_template.py`:
- Around line 17-21: Add a DB index to the Many2one field by setting index=True
on jet_template_id in the cx_tower_jet_waypoint_template model (the
fields.Many2one definition named jet_template_id); after changing the field
definition, update/upgrade the module (or run the appropriate migration) to
apply the new index to the database so filtering/grouping by jet_template_id
benefits from the performance improvement.

In `@cetmix_tower_server/models/cx_tower_jet_waypoint.py`:
- Around line 337-344: Before calling run_flight_plan you must guard against
synchronous failures: capture the current stable state, then wrap the
run_flight_plan(...) call in a try/except; on exception revert the waypoint
state via waypoint.write(...) back to the captured stable state (or a defined
safe state), ensure any associated command/log is finalized or marked failed,
then re-raise the exception so the caller sees the error. Apply this pattern
around the run_flight_plan invocation shown (referencing run_flight_plan,
waypoint.write, waypoint._get_custom_variable_values,
waypoint.waypoint_template_id.plan_delete_id) and mirror the same
try/except+revert logic in the other launch sites noted (prepare() and the
blocks at the other ranges).
- Around line 243-255: The guard that prevents changing waypoint_template_id
uses vals.get("state") so a caller can bypass it by submitting state='draft' in
the payload; update the logic in the write/update handler (the block iterating
self in cx_tower_jet_waypoint) to check the current record state (use
waypoint.state) instead of the pending vals state when deciding if a non-draft
waypoint may change its waypoint_template_id, i.e., remove reliance on
vals.get("state") in the conditional and evaluate the existing waypoint.state
for the permission check and ValidationError raising.

In `@cetmix_tower_server/models/cx_tower_jet.py`:
- Around line 1044-1052: The write that sets state_id to transit_state and
current_action_id/current_command_log_id must be rolled back if
run_flight_plan() raises synchronously: wrap the call to run_flight_plan in a
try/except, and in the except handler call sudo().write to restore the previous
state and clear current_action_id, target_state_id and current_command_log_id
(or set them to False) so the jet is not left stuck busy; apply the same pattern
for the similar block around lines 1066-1075. Ensure you reference the same
fields written earlier (state_id, current_action_id, current_command_log_id,
target_state_id), and call _flight_plan_finished semantics only on success.
- Around line 1663-1673: The current helper only returns direct neighbors by
filtering jet_requires_ids and jet_required_by_ids and should instead traverse
the full dependency graph; update the method that computes dependencies to
perform a recursive or iterative graph traversal (DFS/BFS) starting from self to
collect all transitive jet_depends_on_id and jet_id nodes (using the existing
relations jet_requires_ids -> jet_depends_on_id and jet_required_by_ids ->
jet_id) while avoiding cycles via a visited set, then return the union of all
discovered L1+ deeper jets instead of just l1_jets | l2_jets.
- Around line 987-990: Currently _trigger_action() can be re-entered and
overwrite in-flight transition metadata (target_state_id) — instead of TODO
queueing, implement an early rejection: in the _trigger_action() path (the
method on the cx_tower_jet model / Jet class where target_state_id is set, near
the action.sudo() call) check whether self.target_state_id (or equivalent
in-flight marker) is already non-null and, if so, raise a clear exception or
return an error indicating "transition in progress" (do not overwrite); ensure
the check is done under the same sudoed security context and within the same
transactional/ORM lock (e.g., use a SELECT FOR UPDATE/record lock or Odoo
browse().with_context() transaction semantics) so concurrent callers cannot race
past the guard, and leave the existing in-flight metadata intact.
- Around line 291-304: The current construction of state_available_ids exposes
all template state_to_id options regardless of whether they are reachable from
the jet's current state; to fix it, compute the reachable states from
jet.state_id using the transition edges in actions.state_transit_id (treat
transitions where t.state_from_id is a source and t.state_to_id is a target),
perform a BFS/DFS to build the reachable state id set, then set
state_available_ids to the intersection of actions.state_to_id, the reachable
set, and the access-level filter using effective_user_access_level (keep using
jet._get_user_effective_access_level(), jet.state_id, actions.state_to_id,
actions.state_transit_id), so unreachable targets are filtered out before calls
to _bring_to_state().

In `@cetmix_tower_server/models/cx_tower_metadata_mixin.py`:
- Around line 31-45: The update_metadata method does no type-checking on the
metadata arg before using dict-unpacking and will raise a cryptic TypeError for
non-mapping inputs; add an explicit validation at the start of update_metadata
(after ensure_one()) to assert metadata is a dict or collections.abc.Mapping and
raise a clear TypeError/ValueError with a descriptive message if not; then
perform the existing merge using self.metadata and self.write (unchanged) so
callers get a predictable, explanatory failure for bad input.

In `@cetmix_tower_server/models/cx_tower_server_log.py`:
- Around line 91-96: The compute method _compute_server_id assigns an integer ID
to the Many2one field server_id (record.jet_id.server_id.id); change it to
assign the recordset instead (record.jet_id.server_id) so the field gets a
proper record rather than a raw integer; update _compute_server_id to set
record.server_id = record.jet_id.server_id when jet_id is present and server_id
is empty.

In `@cetmix_tower_server/models/cx_tower_server.py`:
- Around line 2377-2393: The get_variable_value helper ignores no_fallback and
risks KeyError; update get_variable_value to pass the no_fallback flag into
cx.tower.variable._get_variable_values_by_references (call it with
variable_references=[variable_reference], server=self, no_fallback=no_fallback)
and then return the value via values.get(variable_reference) so it returns None
when no resolved value exists instead of raising.

In `@cetmix_tower_server/models/cx_tower_variable.py`:
- Around line 668-699: The recursive call to _get_variable_values_by_references
in the method (cx_tower_variable.py) drops server_template and plan_line_action
from kwargs so nested template rendering loses its scope; update the recursive
invocation in that loop to forward server_template and plan_line_action (e.g.,
pass server_template=kwargs.get("server_template") and
plan_line_action=kwargs.get("plan_line_action")) along with server,
jet_template, jet and _depth so indirect references keep the same
template/action context during rendering.
- Around line 515-552: The plan-line-action value is checked last so it never
wins when broader contexts exist, and the server_template branch uses `or` which
treats empty-string overrides as falsy; change the precedence so
`plan_line_action` is evaluated before the broader contexts (check
`plan_line_action` and return `plan_line_action_value_char` if present), and
replace the `server_template` branch's `return server_template_value_char or
global_value_char` with an explicit None check (e.g. `if
server_template_value_char is not None: return server_template_value_char else:
return global_value_char`) so empty-string overrides are preserved; update the
logic for `server_template`, `jet`, `jet_template`, and `server` branches to use
`... is not None` checks (referencing variables server_template, jet,
jet_template, server and their *_value_char counterparts).

In `@cetmix_tower_server/readme/newsfragments/4700.feature`:
- Line 1: Replace the vague single-word fragment "Jets!" in the newsfragment
4700.feature with a concise, user-facing release note sentence describing what
changed (for example: support for Jet templates, provisioning, and import/export
of Jet configurations); update the sentence to be present-tense, focused on
user-visible scope, and keep it short so it’s changelog-friendly.

In `@cetmix_tower_server/security/cx_tower_jet_action_security.xml`:
- Around line 67-72: The ir.rule record rule_cx_tower_jet_action_root_full for
model_cx_tower_jet_action currently relies on Odoo defaults and omits explicit
perm_* flags; update that record to include explicit permission fields
(perm_read, perm_write, perm_create, perm_unlink) set to True to match the style
used in cx_tower_jet_template_install_security.xml so the root rule is
consistent and unambiguous.

In `@cetmix_tower_server/security/cx_tower_jet_template_install_security.xml`:
- Around line 26-31: The ir.rule record
rule_cx_tower_jet_template_install_root_full grants root full access via a
domain but omits explicit permission flags; update the record for
model_cx_tower_jet_template_install (record id
rule_cx_tower_jet_template_install_root_full, group
cetmix_tower_server.group_root) to add explicit fields perm_read="1",
perm_write="1", perm_create="1", and perm_unlink="1" so permissions match other
security files and improve consistency and maintainability.

In `@cetmix_tower_server/tests/common_jets.py`:
- Around line 505-516: The helper reuses cls.server_test_1 and then mutates it
when server_user_ids/server_manager_ids are provided, which causes later jets to
see overwritten ACLs; change the logic in _create_jet so that if server_user_ids
or server_manager_ids is not None you first instantiate a fresh server object
(do not reuse cls.server_test_1) and assign that new instance to the local
server variable before calling server.write(...); ensure the created server is
the one stored in jet_server_* and depends_on_server_* so each jet with per-jet
ACLs has its own distinct server record.

In `@cetmix_tower_server/tests/test_jet_access.py`:
- Around line 406-441: The test test_root_full_access currently performs
Jet.create, Jet.search, write, and unlink with the default test user instead of
exercising root; update each CRUD call to use the root user by invoking
with_user(self.root) on the recordset/Model (e.g.,
self.Jet.with_user(self.root).create(...),
self.Jet.with_user(self.root).search([...]),
manager_jet.with_user(self.root).write({...}), and
manager_jet.with_user(self.root).unlink()) so every assertion runs under the
root role and validates root record rules.

---

Outside diff comments:
In `@cetmix_tower_server/models/cx_tower_server.py`:
- Around line 1158-1194: Initialize rendered_code and rendered_path before the
variable_references branch so they are always defined; specifically, at the
start of the block that checks variable_references set rendered_code =
command.code and rendered_path = path (or False if you prefer previous falsy
behavior), then proceed to fetch variable_values via
_get_variable_values_by_references and apply custom_variable_values and
rendering (via command.render_code_custom) only to override those defaults;
reference symbols: variable_references, _get_variable_values_by_references,
custom_variable_values, _have_access_to_server, command.render_code_custom,
rendered_code, rendered_path, run_command.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f027bb15-40a0-441a-956f-b82431da92cd

📥 Commits

Reviewing files that changed from the base of the PR and between 4423f1b and 04e8f90.

⛔ Files ignored due to path filters (17)
  • cetmix_tower_server/security/ir.model.access.csv is excluded by !**/*.csv
  • cetmix_tower_server/static/demo/img/backup.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/clean.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/docker.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/kubernetes.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/mariadb.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/monitoring.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/nginx.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/odoo.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/owncloud.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/postgres.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/proxmox.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/test.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/tower.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/traefik.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/woocommerce.png is excluded by !**/*.png
  • cetmix_tower_server/static/demo/img/wordpress.png is excluded by !**/*.png
📒 Files selected for processing (135)
  • cetmix_tower_server/__manifest__.py
  • cetmix_tower_server/data/cx_tower_jet_state.xml
  • cetmix_tower_server/demo/demo_data.xml
  • cetmix_tower_server/demo/demo_jets.xml
  • cetmix_tower_server/migrations/18.0.3.0.0/pre-migration.py
  • cetmix_tower_server/models/__init__.py
  • cetmix_tower_server/models/cetmix_tower.py
  • cetmix_tower_server/models/constants.py
  • cetmix_tower_server/models/cx_tower_command.py
  • cetmix_tower_server/models/cx_tower_command_log.py
  • cetmix_tower_server/models/cx_tower_file.py
  • cetmix_tower_server/models/cx_tower_file_template.py
  • cetmix_tower_server/models/cx_tower_jet.py
  • cetmix_tower_server/models/cx_tower_jet_action.py
  • cetmix_tower_server/models/cx_tower_jet_dependency.py
  • cetmix_tower_server/models/cx_tower_jet_request.py
  • cetmix_tower_server/models/cx_tower_jet_state.py
  • cetmix_tower_server/models/cx_tower_jet_template.py
  • cetmix_tower_server/models/cx_tower_jet_template_dependency.py
  • cetmix_tower_server/models/cx_tower_jet_template_install.py
  • cetmix_tower_server/models/cx_tower_jet_template_install_line.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint.py
  • cetmix_tower_server/models/cx_tower_jet_waypoint_template.py
  • cetmix_tower_server/models/cx_tower_key.py
  • cetmix_tower_server/models/cx_tower_metadata_mixin.py
  • cetmix_tower_server/models/cx_tower_plan.py
  • cetmix_tower_server/models/cx_tower_plan_line.py
  • cetmix_tower_server/models/cx_tower_plan_log.py
  • cetmix_tower_server/models/cx_tower_scheduled_task.py
  • cetmix_tower_server/models/cx_tower_server.py
  • cetmix_tower_server/models/cx_tower_server_log.py
  • cetmix_tower_server/models/cx_tower_template_mixin.py
  • cetmix_tower_server/models/cx_tower_variable.py
  • cetmix_tower_server/models/cx_tower_variable_mixin.py
  • cetmix_tower_server/models/cx_tower_variable_value.py
  • cetmix_tower_server/models/res_users.py
  • cetmix_tower_server/models/tools.py
  • cetmix_tower_server/readme/diagrams/jets.puml
  • cetmix_tower_server/readme/newsfragments/4700.feature
  • cetmix_tower_server/security/cx_tower_jet_action_security.xml
  • cetmix_tower_server/security/cx_tower_jet_dependency_security.xml
  • cetmix_tower_server/security/cx_tower_jet_security.xml
  • cetmix_tower_server/security/cx_tower_jet_template_dependency_security.xml
  • cetmix_tower_server/security/cx_tower_jet_template_install_line_security.xml
  • cetmix_tower_server/security/cx_tower_jet_template_install_security.xml
  • cetmix_tower_server/security/cx_tower_jet_template_security.xml
  • cetmix_tower_server/security/cx_tower_jet_waypoint_security.xml
  • cetmix_tower_server/security/cx_tower_jet_waypoint_template_security.xml
  • cetmix_tower_server/security/cx_tower_scheduled_task_cv_security.xml
  • cetmix_tower_server/security/cx_tower_scheduled_task_security.xml
  • cetmix_tower_server/security/cx_tower_server_log_security.xml
  • cetmix_tower_server/security/cx_tower_server_wizard_access_rules.xml
  • cetmix_tower_server/security/cx_tower_variable_value_security.xml
  • cetmix_tower_server/tests/__init__.py
  • cetmix_tower_server/tests/common.py
  • cetmix_tower_server/tests/common_jets.py
  • cetmix_tower_server/tests/test_cetmix_tower.py
  • cetmix_tower_server/tests/test_command.py
  • cetmix_tower_server/tests/test_jet.py
  • cetmix_tower_server/tests/test_jet_access.py
  • cetmix_tower_server/tests/test_jet_action_access.py
  • cetmix_tower_server/tests/test_jet_create_wizard.py
  • cetmix_tower_server/tests/test_jet_dependency_access.py
  • cetmix_tower_server/tests/test_jet_state.py
  • cetmix_tower_server/tests/test_jet_template.py
  • cetmix_tower_server/tests/test_jet_template_access.py
  • cetmix_tower_server/tests/test_jet_template_dependency_access.py
  • cetmix_tower_server/tests/test_jet_template_install.py
  • cetmix_tower_server/tests/test_jet_template_install_access.py
  • cetmix_tower_server/tests/test_jet_template_install_line_access.py
  • cetmix_tower_server/tests/test_jet_waypoint.py
  • cetmix_tower_server/tests/test_jet_waypoint_access.py
  • cetmix_tower_server/tests/test_jet_waypoint_template_access.py
  • cetmix_tower_server/tests/test_plan.py
  • cetmix_tower_server/tests/test_scheduled_task.py
  • cetmix_tower_server/tests/test_server_jet_action_command.py
  • cetmix_tower_server/tests/test_server_log.py
  • cetmix_tower_server/tests/test_variable.py
  • cetmix_tower_server/tests/test_variable_value.py
  • cetmix_tower_server/views/cx_tower_command_log_view.xml
  • cetmix_tower_server/views/cx_tower_command_view.xml
  • cetmix_tower_server/views/cx_tower_file_view.xml
  • cetmix_tower_server/views/cx_tower_jet_action_view.xml
  • cetmix_tower_server/views/cx_tower_jet_request_view.xml
  • cetmix_tower_server/views/cx_tower_jet_state_view.xml
  • cetmix_tower_server/views/cx_tower_jet_template_install_view.xml
  • cetmix_tower_server/views/cx_tower_jet_template_view.xml
  • cetmix_tower_server/views/cx_tower_jet_view.xml
  • cetmix_tower_server/views/cx_tower_jet_waypoint_template_view.xml
  • cetmix_tower_server/views/cx_tower_jet_waypoint_view.xml
  • cetmix_tower_server/views/cx_tower_plan_line_view.xml
  • cetmix_tower_server/views/cx_tower_plan_log_view.xml
  • cetmix_tower_server/views/cx_tower_scheduled_task_view.xml
  • cetmix_tower_server/views/cx_tower_server_log_view.xml
  • cetmix_tower_server/views/cx_tower_server_template_view.xml
  • cetmix_tower_server/views/cx_tower_server_view.xml
  • cetmix_tower_server/views/cx_tower_variable_value_view.xml
  • cetmix_tower_server/views/cx_tower_variable_view.xml
  • cetmix_tower_server/views/menuitems.xml
  • cetmix_tower_server/wizards/__init__.py
  • cetmix_tower_server/wizards/cx_tower_command_run_wizard.py
  • cetmix_tower_server/wizards/cx_tower_command_run_wizard_view.xml
  • cetmix_tower_server/wizards/cx_tower_jet_action_wizard.py
  • cetmix_tower_server/wizards/cx_tower_jet_action_wizard_view.xml
  • cetmix_tower_server/wizards/cx_tower_jet_clone_wizard.py
  • cetmix_tower_server/wizards/cx_tower_jet_clone_wizard_view.xml
  • cetmix_tower_server/wizards/cx_tower_jet_create_wizard.py
  • cetmix_tower_server/wizards/cx_tower_jet_create_wizard_view.xml
  • cetmix_tower_server/wizards/cx_tower_jet_state_wizard.py
  • cetmix_tower_server/wizards/cx_tower_jet_state_wizard_view.xml
  • cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard.py
  • cetmix_tower_server/wizards/cx_tower_jet_template_install_wizard_view.xml
  • cetmix_tower_server/wizards/cx_tower_plan_run_wizard.py
  • cetmix_tower_server/wizards/cx_tower_plan_run_wizard_view.xml
  • cetmix_tower_server_queue/models/cx_tower_server.py
  • cetmix_tower_server_queue/readme/newsfragments/4700.feature
  • cetmix_tower_yaml/__manifest__.py
  • cetmix_tower_yaml/models/__init__.py
  • cetmix_tower_yaml/models/cx_tower_command.py
  • cetmix_tower_yaml/models/cx_tower_jet_action.py
  • cetmix_tower_yaml/models/cx_tower_jet_state.py
  • cetmix_tower_yaml/models/cx_tower_jet_template.py
  • cetmix_tower_yaml/models/cx_tower_jet_template_dependency.py
  • cetmix_tower_yaml/models/cx_tower_jet_waypoint_template.py
  • cetmix_tower_yaml/models/cx_tower_plan.py
  • cetmix_tower_yaml/models/cx_tower_scheduled_task.py
  • cetmix_tower_yaml/models/cx_tower_scheduled_task_cv.py
  • cetmix_tower_yaml/models/cx_tower_yaml_mixin.py
  • cetmix_tower_yaml/readme/newsfragments/4700.feature
  • cetmix_tower_yaml/tests/test_command.py
  • cetmix_tower_yaml/tests/test_tower_yaml_mixin.py
  • cetmix_tower_yaml/tests/test_yaml_export_wizard.py
  • cetmix_tower_yaml/tests/test_yaml_import_wizard.py
  • cetmix_tower_yaml/views/cx_tower_jet_template_view.xml
  • cetmix_tower_yaml/wizards/cx_tower_yaml_import_wiz.py

Comment thread cetmix_tower_server/demo/demo_data.xml
Comment thread cetmix_tower_server/models/cx_tower_command_log.py
Comment thread cetmix_tower_server/models/cx_tower_command.py
Comment thread cetmix_tower_server/models/cx_tower_jet_action.py
Comment thread cetmix_tower_server/models/cx_tower_jet_request.py
Comment thread cetmix_tower_server/readme/newsfragments/4700.feature
Comment thread cetmix_tower_server/security/cx_tower_jet_action_security.xml
Comment thread cetmix_tower_server/tests/common_jets.py
Comment thread cetmix_tower_server/tests/test_jet_access.py
Copy link
Copy Markdown
Collaborator

@tendil tendil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review: LGTM
But I think it would be better to call the PR [MIG] rather than [IMP]

@ivs-cetmix
Copy link
Copy Markdown
Contributor Author

/ocabot merge major

@CetmixGitDrone
Copy link
Copy Markdown

On my way to merge this fine PR!
Prepared branch 18.0-ocabot-merge-pr-484-by-ivs-cetmix-bump-major, awaiting test results.

@CetmixGitDrone CetmixGitDrone merged commit 60d808f into 18.0 Apr 7, 2026
10 checks passed
@CetmixGitDrone
Copy link
Copy Markdown

Congratulations, your PR was merged at fe2dd5a. Thanks a lot for contributing to cetmix. ❤️

@CetmixGitDrone CetmixGitDrone deleted the 18.0-t5337-cetmix_tower_server-mig-jets branch April 7, 2026 15:02
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.

3 participants