From 33132827f3e2274028e8057cc0c105aa4c69abe8 Mon Sep 17 00:00:00 2001 From: Goffi Date: Sun, 19 Jul 2026 15:08:28 +0200 Subject: [PATCH] cli (application): new `update` command: Update an application, or all of them. --- doc/libervia-cli/appication.rst | 17 ++++++++ libervia/cli/cmd_application.py | 75 ++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/doc/libervia-cli/appication.rst b/doc/libervia-cli/appication.rst index 75f34321..d05ab31e 100644 --- a/doc/libervia-cli/appication.rst +++ b/doc/libervia-cli/appication.rst @@ -51,6 +51,23 @@ Stop Weblate:: $ li app stop weblate +update +====== + +Update an application. This command pulls the latest images and recreates containers if +needed. Use ``-a, --all`` to update all available applications. + +example +------- + +Update Weblate:: + + $ li app update weblate + +Update all applications:: + + $ li app update --all + exposed ======= diff --git a/libervia/cli/cmd_application.py b/libervia/cli/cmd_application.py index 1482e063..1a8c124c 100644 --- a/libervia/cli/cmd_application.py +++ b/libervia/cli/cmd_application.py @@ -18,6 +18,7 @@ from . import base from libervia.backend.core.i18n import _ +from libervia.backend.plugins.plugin_misc_app_manager.models import AppUpdateResult from libervia.backend.tools.common import data_format from libervia.cli.constants import Const as C @@ -189,8 +190,80 @@ class Exposed(base.CommandBase): self.host.quit() +class Update(base.CommandBase): + """Update an application""" + + def __init__(self, host): + extra_outputs = {"default": self.default_output} + super(Update, self).__init__( + host, + "update", + use_profile=False, + use_output=C.OUTPUT_MODEL, + extra_outputs=extra_outputs, + help=_("update an application"), + ) + + def add_parser_options(self): + self.parser.add_argument( + "name", + nargs="?", + help=_("name of the application to update"), + ) + self.parser.add_argument( + "-a", + "--all", + action="store_true", + help=_("update all applications"), + ) + + def default_output(self, result: AppUpdateResult) -> None: + for info in result.updated: + self.console.print( + f" [bold]{info.service}[/bold]: {info.old_version} → {info.new_version}" + ) + + async def _update_one(self, name: str, index: int, total: int) -> None: + self.console.print(f"[dim]updating {name}[/dim] [bold]{index}/{total}[/bold]") + try: + result_s = await self.host.bridge.application_update(name) + except Exception as e: + self.disp(f"can't update {name}: {e}", error=True) + return + + if result_s == "": + self.disp(f"{name} is already up to date") + return + + result = AppUpdateResult.model_validate_json(result_s) + await self.output(result) + + async def start(self): + if self.args.all: + if self.args.name: + self.disp("--all and name are mutually exclusive", error=True) + self.host.quit(C.EXIT_BRIDGE_ERRBACK) + return + try: + apps = await self.host.bridge.applications_list(["available"]) + except Exception as e: + self.disp(f"can't get applications list: {e}", error=True) + self.host.quit(C.EXIT_BRIDGE_ERRBACK) + return + app_names = [app["name"] for app in apps] + for i, name in enumerate(app_names, 1): + await self._update_one(name, i, len(app_names)) + else: + if not self.args.name: + self.disp("name is required (or use --all)", error=True) + self.host.quit(C.EXIT_BRIDGE_ERRBACK) + return + await self._update_one(self.args.name, 1, 1) + self.host.quit() + + class Application(base.CommandBase): - subcommands = (List, Start, Stop, Exposed) + subcommands = (List, Start, Stop, Exposed, Update) def __init__(self, host): super(Application, self).__init__(