config: add listings of devices and flavours to config profile init
aka prompt_profile() and --no-parse flag
This is slow-ish without SRCINFO caching
This commit is contained in:
parent
7b7caf3f37
commit
f77aa4f2a2
3 changed files with 72 additions and 11 deletions
|
@ -1,7 +1,7 @@
|
||||||
import click
|
import click
|
||||||
import logging
|
import logging
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from typing import Any, Optional, Union
|
from typing import Any, Iterable, Optional, Union
|
||||||
|
|
||||||
from .scheme import Profile
|
from .scheme import Profile
|
||||||
from .profile import PROFILE_EMPTY, PROFILE_DEFAULTS
|
from .profile import PROFILE_EMPTY, PROFILE_DEFAULTS
|
||||||
|
@ -23,9 +23,10 @@ def comma_str_to_list(s: str, default=None) -> list[str]:
|
||||||
def prompt_config(
|
def prompt_config(
|
||||||
text: str,
|
text: str,
|
||||||
default: Any,
|
default: Any,
|
||||||
field_type: type = str,
|
field_type: Union[type, click.Choice] = str,
|
||||||
bold: bool = True,
|
bold: bool = True,
|
||||||
echo_changes: bool = True,
|
echo_changes: bool = True,
|
||||||
|
show_choices: bool = False,
|
||||||
) -> tuple[Any, bool]:
|
) -> tuple[Any, bool]:
|
||||||
"""
|
"""
|
||||||
prompts for a new value for a config key. returns the result and a boolean that indicates
|
prompts for a new value for a config key. returns the result and a boolean that indicates
|
||||||
|
@ -54,16 +55,28 @@ def prompt_config(
|
||||||
if bold:
|
if bold:
|
||||||
text = click.style(text, bold=True)
|
text = click.style(text, bold=True)
|
||||||
|
|
||||||
result = click.prompt(text, type=field_type, default=default, value_proc=value_conv, show_default=True) # type: ignore
|
result = click.prompt(
|
||||||
|
text,
|
||||||
|
type=field_type, # type: ignore
|
||||||
|
default=default,
|
||||||
|
value_proc=value_conv,
|
||||||
|
show_default=True,
|
||||||
|
show_choices=show_choices,
|
||||||
|
) # type: ignore
|
||||||
changed = result != (original_default if field_type == list else default) and (true_or_zero(default) or true_or_zero(result))
|
changed = result != (original_default if field_type == list else default) and (true_or_zero(default) or true_or_zero(result))
|
||||||
if changed and echo_changes:
|
if changed and echo_changes:
|
||||||
print(f'value changed: "{text}" = "{result}"')
|
print(f'value changed: "{text}" = "{result}"')
|
||||||
return result, changed
|
return result, changed
|
||||||
|
|
||||||
|
|
||||||
def prompt_profile(name: str, create: bool = True, defaults: Union[Profile, dict] = {}) -> tuple[Profile, bool]:
|
def prompt_profile(
|
||||||
|
name: str,
|
||||||
|
create: bool = True,
|
||||||
|
defaults: Union[Profile, dict] = {},
|
||||||
|
no_parse: bool = True,
|
||||||
|
) -> tuple[Profile, bool]:
|
||||||
"""Prompts the user for every field in `defaults`. Set values to None for an empty profile."""
|
"""Prompts the user for every field in `defaults`. Set values to None for an empty profile."""
|
||||||
|
PARSEABLE_FIELDS = ['device', 'flavour']
|
||||||
profile: Any = PROFILE_EMPTY | defaults
|
profile: Any = PROFILE_EMPTY | defaults
|
||||||
# don't use get_profile() here because we need the sparse profile
|
# don't use get_profile() here because we need the sparse profile
|
||||||
if name in config.file.profiles:
|
if name in config.file.profiles:
|
||||||
|
@ -77,6 +90,16 @@ def prompt_profile(name: str, create: bool = True, defaults: Union[Profile, dict
|
||||||
for key, current in profile.items():
|
for key, current in profile.items():
|
||||||
current = profile[key]
|
current = profile[key]
|
||||||
text = f'{name}.{key}'
|
text = f'{name}.{key}'
|
||||||
|
if not no_parse and key in PARSEABLE_FIELDS:
|
||||||
|
parse_prompt = None
|
||||||
|
if key == 'device':
|
||||||
|
parse_prompt = prompt_profile_device
|
||||||
|
elif key == 'flavour':
|
||||||
|
parse_prompt = prompt_profile_flavour
|
||||||
|
else:
|
||||||
|
raise Exception(f'config: Unhanled parseable field {key}, this is a bug in kupferbootstrap.')
|
||||||
|
result, _changed = parse_prompt(current, name) # type: ignore
|
||||||
|
else:
|
||||||
result, _changed = prompt_config(text=text, default=current, field_type=type(PROFILE_DEFAULTS[key])) # type: ignore
|
result, _changed = prompt_config(text=text, default=current, field_type=type(PROFILE_DEFAULTS[key])) # type: ignore
|
||||||
if _changed:
|
if _changed:
|
||||||
profile[key] = result
|
profile[key] = result
|
||||||
|
@ -84,6 +107,32 @@ def prompt_profile(name: str, create: bool = True, defaults: Union[Profile, dict
|
||||||
return profile, changed
|
return profile, changed
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_choice(current: Optional[Any], key: str, choices: Iterable[Any], allow_none: bool = True, show_choices: bool = False) -> tuple[Any, bool]:
|
||||||
|
choices = list(choices) + ([''] if allow_none else [])
|
||||||
|
res, _ = prompt_config(text=key, default=current, field_type=click.Choice(choices), show_choices=show_choices)
|
||||||
|
if allow_none and res == '':
|
||||||
|
res = None
|
||||||
|
return res, res == current
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_profile_device(current: Optional[str], profile_name: str) -> tuple[str, bool]:
|
||||||
|
from packages.device import get_devices
|
||||||
|
devices = get_devices()
|
||||||
|
print(click.style("Pick your device!\nThese are the available devices:", bold=True))
|
||||||
|
for dev in sorted(devices.keys()):
|
||||||
|
print(devices[dev])
|
||||||
|
return prompt_choice(current, f'profiles.{profile_name}.device', devices.keys())
|
||||||
|
|
||||||
|
|
||||||
|
def prompt_profile_flavour(current: Optional[str], profile_name: str) -> tuple[str, bool]:
|
||||||
|
from packages.flavour import get_flavours
|
||||||
|
flavours = get_flavours()
|
||||||
|
print(click.style("Pick your flavour!\nThese are the available flavours:", bold=True))
|
||||||
|
for f in sorted(flavours.keys()):
|
||||||
|
print(flavours[f])
|
||||||
|
return prompt_choice(current, f'profiles.{profile_name}.flavour', flavours.keys())
|
||||||
|
|
||||||
|
|
||||||
def config_dot_name_get(name: str, config: dict[str, Any], prefix: str = '') -> Any:
|
def config_dot_name_get(name: str, config: dict[str, Any], prefix: str = '') -> Any:
|
||||||
if not isinstance(config, dict):
|
if not isinstance(config, dict):
|
||||||
raise Exception(f"Couldn't resolve config name: passed config is not a dict: {repr(config)}")
|
raise Exception(f"Couldn't resolve config name: passed config is not a dict: {repr(config)}")
|
||||||
|
@ -138,11 +187,13 @@ def cmd_config():
|
||||||
|
|
||||||
noninteractive_flag = click.option('-N', '--non-interactive', is_flag=True)
|
noninteractive_flag = click.option('-N', '--non-interactive', is_flag=True)
|
||||||
noop_flag = click.option('--noop', '-n', help="Don't write changes to file", is_flag=True)
|
noop_flag = click.option('--noop', '-n', help="Don't write changes to file", is_flag=True)
|
||||||
|
noparse_flag = click.option('--no-parse', help="Don't search PKGBUILDs for devices and flavours", is_flag=True)
|
||||||
|
|
||||||
|
|
||||||
@cmd_config.command(name='init')
|
@cmd_config.command(name='init')
|
||||||
@noninteractive_flag
|
@noninteractive_flag
|
||||||
@noop_flag
|
@noop_flag
|
||||||
|
@noparse_flag
|
||||||
@click.option(
|
@click.option(
|
||||||
'--sections',
|
'--sections',
|
||||||
'-s',
|
'-s',
|
||||||
|
@ -152,7 +203,13 @@ noop_flag = click.option('--noop', '-n', help="Don't write changes to file", is_
|
||||||
show_choices=True,
|
show_choices=True,
|
||||||
)
|
)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def cmd_config_init(ctx, sections: list[str] = CONFIG_SECTIONS, non_interactive: bool = False, noop: bool = False):
|
def cmd_config_init(
|
||||||
|
ctx,
|
||||||
|
sections: list[str] = CONFIG_SECTIONS,
|
||||||
|
non_interactive: bool = False,
|
||||||
|
noop: bool = False,
|
||||||
|
no_parse: bool = False,
|
||||||
|
):
|
||||||
"""Initialize the config file"""
|
"""Initialize the config file"""
|
||||||
if not non_interactive:
|
if not non_interactive:
|
||||||
results: dict[str, dict] = {}
|
results: dict[str, dict] = {}
|
||||||
|
@ -173,7 +230,7 @@ def cmd_config_init(ctx, sections: list[str] = CONFIG_SECTIONS, non_interactive:
|
||||||
if 'profiles' in sections:
|
if 'profiles' in sections:
|
||||||
current_profile = 'default' if 'current' not in config.file.profiles else config.file.profiles.current
|
current_profile = 'default' if 'current' not in config.file.profiles else config.file.profiles.current
|
||||||
new_current, _ = prompt_config('profiles.current', default=current_profile, field_type=str)
|
new_current, _ = prompt_config('profiles.current', default=current_profile, field_type=str)
|
||||||
profile, changed = prompt_profile(new_current, create=True)
|
profile, changed = prompt_profile(new_current, create=True, no_parse=no_parse)
|
||||||
config.update_profile(new_current, profile)
|
config.update_profile(new_current, profile)
|
||||||
if not noop:
|
if not noop:
|
||||||
if not prompt_for_save(ctx):
|
if not prompt_for_save(ctx):
|
||||||
|
@ -188,9 +245,10 @@ def cmd_config_init(ctx, sections: list[str] = CONFIG_SECTIONS, non_interactive:
|
||||||
@cmd_config.command(name='set')
|
@cmd_config.command(name='set')
|
||||||
@noninteractive_flag
|
@noninteractive_flag
|
||||||
@noop_flag
|
@noop_flag
|
||||||
|
@noparse_flag
|
||||||
@click.argument('key_vals', nargs=-1)
|
@click.argument('key_vals', nargs=-1)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def cmd_config_set(ctx, key_vals: list[str], non_interactive: bool = False, noop: bool = False):
|
def cmd_config_set(ctx, key_vals: list[str], non_interactive: bool = False, noop: bool = False, no_parse: bool = False):
|
||||||
"""
|
"""
|
||||||
Set config entries. Pass entries as `key=value` pairs, with keys as dot-separated identifiers,
|
Set config entries. Pass entries as `key=value` pairs, with keys as dot-separated identifiers,
|
||||||
like `build.clean_mode=false` or alternatively just keys to get prompted if run interactively.
|
like `build.clean_mode=false` or alternatively just keys to get prompted if run interactively.
|
||||||
|
@ -245,16 +303,17 @@ def cmd_profile():
|
||||||
@cmd_profile.command(name='init')
|
@cmd_profile.command(name='init')
|
||||||
@noninteractive_flag
|
@noninteractive_flag
|
||||||
@noop_flag
|
@noop_flag
|
||||||
|
@noparse_flag
|
||||||
@click.argument('name', required=True)
|
@click.argument('name', required=True)
|
||||||
@click.pass_context
|
@click.pass_context
|
||||||
def cmd_profile_init(ctx, name: str, non_interactive: bool = False, noop: bool = False):
|
def cmd_profile_init(ctx, name: str, non_interactive: bool = False, noop: bool = False, no_parse: bool = False):
|
||||||
"""Create or edit a profile"""
|
"""Create or edit a profile"""
|
||||||
profile = deepcopy(PROFILE_EMPTY)
|
profile = deepcopy(PROFILE_EMPTY)
|
||||||
if name in config.file.profiles:
|
if name in config.file.profiles:
|
||||||
profile |= config.file.profiles[name]
|
profile |= config.file.profiles[name]
|
||||||
|
|
||||||
if not non_interactive:
|
if not non_interactive:
|
||||||
profile, _changed = prompt_profile(name, create=True)
|
profile, _changed = prompt_profile(name, create=True, no_parse=no_parse)
|
||||||
|
|
||||||
config.update_profile(name, profile)
|
config.update_profile(name, profile)
|
||||||
if not noop:
|
if not noop:
|
||||||
|
|
|
@ -99,6 +99,7 @@ def get_devices(pkgbuilds: Optional[dict[str, Pkgbuild]] = None, lazy: bool = Tr
|
||||||
global _device_cache, _device_cache_populated
|
global _device_cache, _device_cache_populated
|
||||||
use_cache = _device_cache_populated and lazy
|
use_cache = _device_cache_populated and lazy
|
||||||
if not use_cache:
|
if not use_cache:
|
||||||
|
logging.info("Searching PKGBUILDs for device packages")
|
||||||
if not pkgbuilds:
|
if not pkgbuilds:
|
||||||
pkgbuilds = discover_pkgbuilds(lazy=lazy, repositories=['device'])
|
pkgbuilds = discover_pkgbuilds(lazy=lazy, repositories=['device'])
|
||||||
_device_cache.clear()
|
_device_cache.clear()
|
||||||
|
|
|
@ -39,6 +39,7 @@ def get_flavours(lazy: bool = True):
|
||||||
global _flavours_cache, _flavours_discovered
|
global _flavours_cache, _flavours_discovered
|
||||||
if lazy and _flavours_discovered:
|
if lazy and _flavours_discovered:
|
||||||
return _flavours_cache
|
return _flavours_cache
|
||||||
|
logging.info("Searching PKGBUILDs for flavour packages")
|
||||||
flavours: dict[str, Flavour] = {}
|
flavours: dict[str, Flavour] = {}
|
||||||
pkgbuilds: dict[str, Pkgbuild] = discover_pkgbuilds(lazy=(lazy or not _flavours_discovered))
|
pkgbuilds: dict[str, Pkgbuild] = discover_pkgbuilds(lazy=(lazy or not _flavours_discovered))
|
||||||
for pkg in pkgbuilds.values():
|
for pkg in pkgbuilds.values():
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue