fix(oci): clarify the summary of imported Compose stacks

Each registry line names its image, the variables of a stack are listed
without announcing questions that are not asked, and the name given to an
imported stack names its containers.
This commit is contained in:
MacRimi
2026-10-28 22:57:15 +01:00
parent 5e89b2bdf5
commit 2ab83c0b88
3 changed files with 46 additions and 3 deletions
+10 -2
View File
@@ -809,7 +809,9 @@ def describe(template: dict[str, Any]) -> str:
for volume in volumes]
environment = contract.get('environment') or []
if environment:
lines += ['', translate('Variables the installation asks for:')]
# A stack takes its values from the Compose file; none is asked here.
lines += ['', translate('Variables the installation asks for:') if len(services) <= 1 else
f"{translate('Variables')} ({template['compose_stack']['main_service']}):"]
lines += [f" {item['name']}"
+ (f" = {item['example']}" if item.get('example') and not item['sensitive'] and len(services) <= 1 else '')
+ ('' if item.get('required', True) else f" ({translate('optional')})")
@@ -852,13 +854,17 @@ def _ask_secrets(template: dict[str, Any]) -> list[str]:
def _name(ui, template: dict[str, Any]) -> str:
default = normalize_app_id(source_text(template['catalog_ui'].get('title'))
stack = template.get('compose_stack') if len((template.get('compose_stack') or {}).get('services') or []) > 1 else None
default = normalize_app_id((stack or {}).get('project_name') or source_text(template['catalog_ui'].get('title'))
or template['container_contract'].get('container_name') or 'oci-app')
name = normalize_app_id(ui.ask(translate('Name for this application'), default))
if not name:
raise UserCancelled(translate('No name was given'))
template['catalog_ui']['title'] = {'en_US': name}
template['container_contract']['container_name'] = name
if stack:
# The containers of the stack are named after it.
stack['project_name'] = name
return name
@@ -897,6 +903,8 @@ def _explore(ui) -> None:
if offer_sign_in(ui, reference, title):
accessible, report = registry_report(reference)
available = available and accessible
if accessible and len(references) > 1:
report = f'{reference}: {report}'
reports.append(report)
summary += '\n\n' + report
if notes:
+2 -1
View File
@@ -552,7 +552,8 @@ def build_stack(template, ui, mode='advanced'):
# A default installation takes the name, the VMID and the settings from the
# recipe; it still asks the storage, the address and how the stack starts.
quiet = DefaultsUI() if mode == DEFAULT_MODE else ui
name = _hostname_default(quiet.ask(translate('Stack name'), template['compose_stack']['project_name']))
# An imported Compose file was already named before its installation.
name = _hostname_default((DefaultsUI() if custom else quiet).ask(translate('Stack name'), template['compose_stack']['project_name']))
vmid = quiet.ask(translate('Base VMID (empty = next free block)'), '', required=False)
from . import host
from . import network as access
+34
View File
@@ -109,6 +109,40 @@ class CustomComposeStacks(unittest.TestCase):
install.assert_not_called()
self.assertIn('app missing', ui.text)
def test_each_registry_line_names_its_image(self):
class UI:
def message(self, text, title):
self.text = text
ui = UI()
with patch.object(custom, 'read_definition', return_value=(COMPOSE, 'Compose')), \
patch.object(custom, 'registry_report', side_effect=[(True, 'in registry'), (False, 'app missing')]), \
patch.object(custom, '_registry_refused', return_value=False), \
patch.object(custom, 'install_template'):
custom.explore(ui)
self.assertIn('postgres:16: in registry', ui.text)
self.assertNotIn('adminer:4: app missing', ui.text)
@patch('proxmenux_oci.i18n.language', return_value='en')
def test_stack_summary_does_not_announce_questions_about_its_variables(self, _):
template = custom.template_from_compose(COMPOSE.replace(
" ports:", " environment:\n ADMINER_DEFAULT_SERVER: db\n ports:"))
summary = custom.describe(template)
self.assertNotIn('Variables the installation asks for', summary)
self.assertIn('Variables (app):\n ADMINER_DEFAULT_SERVER', summary)
single = custom.template_from_compose('services: {app: {image: nginx:1.27, environment: {MODE: x}}}')
self.assertIn('Variables the installation asks for', custom.describe(single))
def test_the_name_given_names_the_stack(self):
template = custom.template_from_compose('name: adminer-postgres\n' + COMPOSE)
class UI:
def ask(self, text, default=None, required=True):
self.default = default
return 'tools'
ui = UI()
self.assertEqual(custom._name(ui, template), 'tools')
self.assertEqual(ui.default, 'adminer-postgres')
self.assertEqual(template['compose_stack']['project_name'], 'tools')
def test_single_image_still_uses_single_container_flow(self):
template = custom.template_from_compose('services: {app: {image: nginx:1.27}}')
self.assertNotEqual(template['proxmox'].get('installer_profile', {}).get('stack_driver'), 'generic-multi-lxc-stack')