From 3f75b94ea1fb75bdee2a300acadf4304f49cdfc5 Mon Sep 17 00:00:00 2001 From: MacRimi Date: Tue, 6 Oct 2026 19:42:30 +0200 Subject: [PATCH] fix(oci): call the editing of an application Modify everywhere - The option that edits an installed application is Modify for one container and for a multi-container application, in the OCI menu and on the Monitor button. Recreate is only the rebuild of a multi-container application as it is. - A stack recreation says "Recreating" and "Recreated" in its steps instead of "Updating". - The notes of the GPU, Coral, disk and share menus no longer name the option. - Spanish for the new texts; the English copies of the stack texts are removed from de, fr, pt and sv so the bot translates them. --- .../tests/test_oci_monitor_modify_contract.py | 27 +++++- AppImage/components/release-notes-modal.tsx | 2 +- AppImage/components/virtual-machines.tsx | 12 +-- AppImage/messages/de/common.json | 6 +- AppImage/messages/en/common.json | 6 +- AppImage/messages/es/common.json | 6 +- AppImage/messages/fr/common.json | 6 +- AppImage/messages/it/common.json | 6 +- AppImage/messages/pt/common.json | 6 +- AppImage/messages/sk/common.json | 6 +- AppImage/messages/sv/common.json | 6 +- lang/de.json | 12 --- lang/es.json | 91 ++++++++++--------- lang/fr.json | 12 --- lang/pt.json | 12 --- lang/sv.json | 12 --- oci/remote/oci_instance_transaction.py | 12 +-- oci/remote/oci_stack_modify.py | 4 +- oci/remote/oci_stack_transaction.py | 9 +- oci/remote/oci_update_current.py | 2 +- oci/src/proxmenux_oci/management.py | 15 +-- oci/src/proxmenux_oci/stack_recreation.py | 10 +- oci/tests/test_stack_operation_wording.py | 46 ++++++++++ scripts/global/share-common.func | 2 +- scripts/gpu_tpu/add_gpu_lxc.sh | 2 +- scripts/gpu_tpu/add_gpu_vm.sh | 4 +- scripts/gpu_tpu/install_coral_lxc.sh | 2 +- scripts/gpu_tpu/switch_gpu_mode.sh | 4 +- scripts/gpu_tpu/switch_gpu_mode_direct.sh | 4 +- scripts/share/lxc-mount-manager_minimal.sh | 2 +- scripts/storage/disk-passthrough_ct.sh | 2 +- 31 files changed, 185 insertions(+), 163 deletions(-) create mode 100644 oci/tests/test_stack_operation_wording.py diff --git a/.github/scripts/tests/test_oci_monitor_modify_contract.py b/.github/scripts/tests/test_oci_monitor_modify_contract.py index 565b5bd0..625443f1 100644 --- a/.github/scripts/tests/test_oci_monitor_modify_contract.py +++ b/.github/scripts/tests/test_oci_monitor_modify_contract.py @@ -27,10 +27,31 @@ def extracted_stack_manager(scope): class MonitorModifyContractTests(TestCase): - def test_monitor_sends_modify_for_a_stack_and_recreate_for_a_single_instance(self): + def test_monitor_sends_modify_for_every_instance(self): source = MONITOR.read_text(encoding='utf-8') - self.assertIn('action: "update" | "modify" | "recreate" | "recover"', source) - self.assertIn('action: ociInstance.stack ? "modify" : "recreate"', source) + self.assertIn('action: "update" | "modify" | "recover"', source) + self.assertIn('action: "modify",', source) + self.assertNotIn('"recreate"', source) + + def test_the_editor_has_one_name_for_one_container_and_for_several(self): + menu = MENU.read_text(encoding='utf-8') + self.assertIn("('modify', translate('Modify: edit resources, network, paths and GPU'))", menu) + self.assertIn("('modify', translate('Modify extra paths and devices'))", menu) + self.assertEqual(menu.count("('recreate', translate("), 1) + self.assertIn("('recreate', translate('Recreate every container with its saved configuration'))", menu) + + def test_recreate_on_a_single_instance_changes_nothing(self): + node = next(item for item in ast.parse(MENU.read_text(encoding='utf-8')).body + if isinstance(item, ast.FunctionDef) and item.name == 'manage_instance') + row = {'vmid': 101, 'reason': 'matched', 'stack': None, 'pending': False, 'status': 'installed'} + run_lifecycle = Mock() + scope = {'carry_records': lambda *_, **__: {}, 'check_selected': lambda _project, selected: selected, + 'translate': lambda text: text, '_run_lifecycle': run_lifecycle, '_manage_stack': Mock()} + exec(compile(ast.fix_missing_locations(ast.Module(body=[node], type_ignores=[])), str(MENU), 'exec'), scope) + ui = Mock() + self.assertFalse(scope['manage_instance'](Path('/inert'), ui, row, action='recreate')) + self.assertEqual(ui.method_calls, []) + run_lifecycle.assert_not_called() def test_cli_and_monitor_wrapper_accept_the_modify_action(self): self.assertIn('choices=("update", "modify", "recreate")', CLI.read_text(encoding='utf-8')) diff --git a/AppImage/components/release-notes-modal.tsx b/AppImage/components/release-notes-modal.tsx index 86c883e0..4cbcd68d 100644 --- a/AppImage/components/release-notes-modal.tsx +++ b/AppImage/components/release-notes-modal.tsx @@ -312,7 +312,7 @@ const CURRENT_VERSION_FEATURES = [ { icon: , key: "releaseNotes.currentFeatures.ociUpdatesTab", - text: "Updates for OCI containers — Update and Recreate open the same flow as the OCI menu, the backup taken before updating can be kept in a backup storage, and the image can be updated on a schedule.", + text: "Updates for OCI containers — Update and Modify open the same flow as the OCI menu, the backup taken before updating can be kept in a backup storage, and the image can be updated on a schedule.", }, { icon: , diff --git a/AppImage/components/virtual-machines.tsx b/AppImage/components/virtual-machines.tsx index 031e88c3..722c72ae 100644 --- a/AppImage/components/virtual-machines.tsx +++ b/AppImage/components/virtual-machines.tsx @@ -954,7 +954,7 @@ export function VirtualMachines() { // A container restored from a backup carries the mark of its installation // and has no record on this host until it is recovered from the menu. const [ociRestored, setOciRestored] = useState(false) - const [ociAction, setOciAction] = useState<{ vmid: number; action: "update" | "modify" | "recreate" | "recover" } | null>(null) + const [ociAction, setOciAction] = useState<{ vmid: number; action: "update" | "modify" | "recover" } | null>(null) // Firewall log state — fetched only when the operator opens that tab // so a CT/VM without firewall use doesn't pay the pvesh cost on every @@ -5343,11 +5343,11 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => { className={neutralBtnCls} onClick={() => setOciAction({ vmid: selectedVM.vmid, - action: ociInstance.stack ? "modify" : "recreate", + action: "modify", })} > - - {t("vmLxc.ociUpdates.recreate")} + + {t("vmLxc.ociUpdates.modify")} )}