fix(oci): show progress between selected updates

- Show which selected update is starting while its container is checked
- Keep the single-application flow unchanged
- Translate the new progress text
This commit is contained in:
MacRimi
2026-10-20 00:19:02 +02:00
parent 29b4913047
commit 58e16c686e
10 changed files with 21 additions and 8 deletions
+7 -5
View File
@@ -151,7 +151,7 @@ def _run_lifecycle(command, title):
return completed.returncode == 0
def carry_records(project, mount_stopped=True, vmids=None, verify=False, shown=False, heading=None):
def carry_records(project, mount_stopped=True, vmids=None, verify=False, shown=False, heading=None, message=None):
"""Leave inside each container the copy of its record that a restore on
another host needs. It is refreshed after every operation. Returns what
was found for each container. Every installed application is looked at,
@@ -162,7 +162,7 @@ def carry_records(project, mount_stopped=True, vmids=None, verify=False, shown=F
if heading:
console.show_logo()
console.msg_title(heading)
console.msg_info(translate('Updating the copy of the record...'))
console.msg_info(message or translate('Updating the copy of the record...'))
try:
import oci_carried_record
return oci_carried_record.sync(oci_carried_record.instances.ROOT, vmids, mount_stopped, verify)
@@ -391,13 +391,15 @@ def _toggle_watchdog(project, ui, vmid, enabled):
return True
def manage_instance(project, ui, row, action=None, lifecycle_args=()):
def manage_instance(project, ui, row, action=None, lifecycle_args=(), heading=None):
"""What the menu does with one instance once it is selected. `action`
skips the choice of operation, as ProxMenux Monitor does; the extra
`lifecycle_args` are passed to the program that performs it."""
`lifecycle_args` are passed to the program that performs it. A `heading`
shows the check of the container on screen, as one of several updates."""
shown = {'shown': True, 'heading': heading, 'message': translate('Checking the container...')} if heading else {}
# A container that came back from another node or from an older backup
# carries the record that describes it; the one of this host is not used.
if carry_records(project, vmids=[row['vmid']], verify=True).get(row['vmid']) == 'stale':
if carry_records(project, vmids=[row['vmid']], verify=True, **shown).get(row['vmid']) == 'stale':
ui.message(translate('This container was restored or came back from another host after its record on this host was written. Open this menu again to recover it; the container was not modified.'),
translate('OCI management'))
return False
+3 -2
View File
@@ -188,9 +188,10 @@ def select_updates(project, ui, available):
ui.message(translate('An application changed since the update check. Check for updates again.'),
translate('Updates available'))
return False
for row in selected:
for index, row in enumerate(selected, 1):
# Keep the existing identity checks, backup/storage prompts, update
# confirmation, coordinated stack update and recovery path.
if not manage_instance(project, ui, current[row['vmid']], action='update'):
heading = f"{translate('Update OCI')} ({index}/{len(selected)}) · {row['title']}"
if not manage_instance(project, ui, current[row['vmid']], action='update', heading=heading):
break # A cancellation or failure never updates later selections.
return False
+4 -1
View File
@@ -42,13 +42,16 @@ class SelectionTests(unittest.TestCase):
_, ui, run = self.select(['111'])
self.assertEqual(run.call_count, 1)
self.assertEqual(run.call_args.args[2]['vmid'], 111)
self.assertEqual(run.call_args.kwargs, {'action': 'update'})
self.assertEqual(run.call_args.kwargs, {'action': 'update', 'heading': 'Update OCI (1/1) · Second app'})
self.assertIn('Second app', ui.review.call_args.args[0])
self.assertNotIn('First app', ui.review.call_args.args[0])
def test_select_all_includes_every_app_once(self):
_, ui, run = self.select(['all', '111'])
self.assertEqual([c.args[2]['vmid'] for c in run.call_args_list], [110, 111])
# Each update shows its place in the batch while its container is checked.
self.assertEqual([c.kwargs['heading'] for c in run.call_args_list],
['Update OCI (1/2) · First app', 'Update OCI (2/2) · Second app'])
self.assertFalse(ui.review.call_args.kwargs['default'])
def test_cancel_does_nothing(self):