Remove Snap support from Bearhub

This commit is contained in:
Sebastian Palencsar
2026-05-28 06:00:37 +02:00
parent ce09e8b0d1
commit 1d3feeae63
26 changed files with 19 additions and 1584 deletions

View File

@@ -3203,15 +3203,6 @@ class ArchManager(SoftwareManager, SettingsController):
requires_root=True,
refresh=False,
manager=self,
requires_confirmation=False),
'setup_snapd': CustomSoftwareAction(i18n_label_key='arch.custom_action.setup_snapd',
i18n_status_key='arch.custom_action.setup_snapd.status',
i18n_description_key='arch.custom_action.setup_snapd.desc',
manager_method='setup_snapd',
icon_path=get_icon_path(),
requires_root=False,
refresh=False,
manager=self,
requires_confirmation=False)
}
@@ -3226,9 +3217,6 @@ class ArchManager(SoftwareManager, SettingsController):
if bool(arch_config['repositories']):
yield self._custom_actions['sys_up']
if pacman.is_snapd_installed():
yield self._custom_actions['setup_snapd']
def fill_sizes(self, pkgs: List[ArchPackage]):
installed, new, all_names, installed_names = [], [], [], []
@@ -3475,89 +3463,6 @@ class ArchManager(SoftwareManager, SettingsController):
if self._remove_from_editable_pkgbuilds(pkg.name):
pkg.pkgbuild_editable = False
def setup_snapd(self, root_password: Optional[str], watcher: ProcessWatcher) -> bool:
# checking services
missing_items = []
for serv, active in system.check_enabled_services('snapd.service', 'snapd.socket').items():
if not active:
missing_items.append(InputOption(label=self.i18n['snap.custom_action.setup_snapd.service_disabled'].format("'{}'".format(serv)),
value='enable:{}'.format(serv),
read_only=True))
for serv, active in system.check_active_services('snapd.service', 'snapd.socket').items():
if not active:
missing_items.append(InputOption(label=self.i18n['snap.custom_action.setup_snapd.service_inactive'].format("'{}'".format(serv)),
value='start:{}'.format(serv),
read_only=True))
link = '/snap'
link_dest = '/var/lib/snapd/snap'
if not os.path.exists('/snap'):
missing_items.append(InputOption(label=self.i18n['snap.custom_action.setup_snapd.missing_link'].format("'{}'".format(link), "'{}'".format(link_dest)),
value='link:{}:{}'.format(link, link_dest),
read_only=True))
if missing_items:
actions = MultipleSelectComponent(label=self.i18n['snap.custom_action.setup_snapd.required_actions'],
options=missing_items,
default_options=set(missing_items),
max_per_line=1,
spaces=False)
if watcher.request_confirmation(title=self.i18n['confirmation'].capitalize(),
body='',
components=[actions],
confirmation_label=self.i18n['proceed'].capitalize(),
deny_label=self.i18n['cancel'].capitalize()):
valid_pwd, pwd = watcher.request_root_password()
if valid_pwd:
handler = ProcessHandler(watcher)
for a in missing_items:
action = a.value.split(':')
if action[0] == 'enable':
msg = 'Enabling service {}'.format(action[1])
watcher.print(msg)
self.logger.info(msg)
proc = SimpleProcess(['systemctl', 'enable', '--now', action[1]], root_password=pwd)
elif action[0] == 'start':
msg = 'Starting service {}'.format(action[1])
watcher.print(msg)
self.logger.info(msg)
proc = SimpleProcess(['systemctl', 'start', action[1]], root_password=pwd)
elif action[0] == 'link':
msg = 'Creating symbolic link {} for {}'.format(action[1], action[2])
watcher.print(msg)
self.logger.info(msg)
proc = SimpleProcess(['ln', '-s', action[2], action[1]], root_password=pwd)
else:
msg = "Wrong action '{}'".format(action)
watcher.print(msg)
self.logger.warning(msg)
proc = None
if not proc:
return False
success, output = handler.handle_simple(proc)
if not success:
watcher.show_message(title=self.i18n['error'].capitalize(),
body=output,
type_=MessageType.ERROR)
return False
watcher.show_message(title=self.i18n['snap.custom_action.setup_snapd.ready'],
body=self.i18n['snap.custom_action.setup_snapd.ready.body'],
type_=MessageType.INFO)
return True
else:
watcher.show_message(title=self.i18n['snap.custom_action.setup_snapd.ready'],
body=self.i18n['snap.custom_action.setup_snapd.ready.body'],
type_=MessageType.INFO)
return True
def _gen_custom_pkgbuild_if_required(self, context: TransactionContext) -> Optional[str]:
build_only_chosen = context.config.get('aur_build_only_chosen')

View File

@@ -1002,10 +1002,6 @@ def get_packages_to_sync_first() -> Set[str]:
return set()
def is_snapd_installed() -> bool:
return bool(run_cmd('pacman -Qq snapd', print_error=False))
def list_hard_requirements(name: str, logger: Optional[logging.Logger] = None,
assume_installed: Optional[Set[str]] = None) -> Optional[Set[str]]:
cmd = StringIO()

View File

@@ -1,14 +0,0 @@
import os
from bauh.api.paths import CONFIG_DIR, CACHE_DIR
from bauh.commons import resource
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
SNAP_CACHE_DIR = f'{CACHE_DIR}/snap'
CONFIG_FILE = f'{CONFIG_DIR}/snap.yml'
CATEGORIES_FILE_PATH = f'{SNAP_CACHE_DIR}/categories.txt'
URL_CATEGORIES_FILE = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/snap/categories.txt'
def get_icon_path() -> str:
return resource.get_path('img/snap.svg', ROOT_DIR)

View File

@@ -1,11 +0,0 @@
from bauh.commons.config import YAMLConfigManager
from bauh.gems.snap import CONFIG_FILE
class SnapConfigManager(YAMLConfigManager):
def __init__(self):
super(SnapConfigManager, self).__init__(config_file_path=CONFIG_FILE)
def get_default_config(self) -> dict:
return {'install_channel': False, 'categories_exp': 24}

View File

@@ -1,573 +0,0 @@
import re
import time
import traceback
from threading import Thread
from typing import List, Set, Type, Optional, Tuple, Generator
from bauh.api.abstract.controller import SoftwareManager, SearchResult, ApplicationContext, UpgradeRequirements, \
TransactionResult, SoftwareAction, SettingsView, SettingsController
from bauh.api.abstract.disk import DiskCacheLoader
from bauh.api.abstract.handler import ProcessWatcher, TaskManager
from bauh.api.abstract.model import SoftwarePackage, PackageHistory, PackageUpdate, PackageSuggestion, \
SuggestionPriority, PackageStatus
from bauh.api.abstract.view import SingleSelectComponent, SelectViewType, InputOption, PanelComponent, \
FormComponent, TextInputComponent
from bauh.api.exception import NoInternetException
from bauh.commons import suggestions
from bauh.commons.boot import CreateConfigFile
from bauh.commons.category import CategoriesDownloader
from bauh.commons.html import bold
from bauh.commons.system import SystemProcess, ProcessHandler, new_root_subprocess
from bauh.commons.view_utils import new_select, get_human_size_str
from bauh.gems.snap import snap, URL_CATEGORIES_FILE, CATEGORIES_FILE_PATH, \
get_icon_path, snapd
from bauh.gems.snap.config import SnapConfigManager
from bauh.gems.snap.model import SnapApplication
from bauh.gems.snap.snapd import SnapdClient
RE_AVAILABLE_CHANNELS = re.compile(re.compile(r'(\w+)\s+(snap install.+)'))
class SnapManager(SoftwareManager, SettingsController):
def __init__(self, context: ApplicationContext):
super(SnapManager, self).__init__(context=context)
self.i18n = context.i18n
self.api_cache = context.cache_factory.new()
context.disk_loader_factory.map(SnapApplication, self.api_cache)
self.enabled = True
self.http_client = context.http_client
self.logger = context.logger
self.ubuntu_distro = context.distro == 'ubuntu'
self.categories = {}
self.suggestions_cache = context.cache_factory.new()
self.info_path = None
self.configman = SnapConfigManager()
self._suggestions_url: Optional[str] = None
def _fill_categories(self, app: SnapApplication):
categories = self.categories.get(app.name.lower())
if categories:
app.categories = categories
if not app.is_application():
categories = app.categories
if categories is None:
categories = []
app.categories = categories
if 'runtime' not in categories:
categories.append('runtime')
def search(self, words: str, disk_loader: DiskCacheLoader, limit: int = -1, is_url: bool = False) -> SearchResult:
if is_url or (not snap.is_installed() and not snapd.is_running()):
return SearchResult([], [], 0)
snapd_client = SnapdClient(self.logger)
apps_found = snapd_client.query(words)
res = SearchResult([], [], 0)
if apps_found:
installed = self.read_installed(disk_loader).installed
for app_json in apps_found:
already_installed = None
if installed:
already_installed = [i for i in installed if i.id == app_json.get('id')]
already_installed = already_installed[0] if already_installed else None
if already_installed:
res.installed.append(already_installed)
else:
res.new.append(self._map_to_app(app_json, installed=False))
res.total = len(res.installed) + len(res.new)
return res
def read_installed(self, disk_loader: DiskCacheLoader, limit: int = -1, only_apps: bool = False, pkg_types: Set[Type[SoftwarePackage]] = None, internet_available: bool = None) -> SearchResult:
if snap.is_installed() and snapd.is_running():
snapd_client = SnapdClient(self.logger)
app_names = {a['snap'] for a in snapd_client.list_only_apps()}
installed = [self._map_to_app(app_json=appjson,
installed=True,
disk_loader=disk_loader,
is_application=app_names and appjson['name'] in app_names) for appjson in snapd_client.list_all_snaps()]
return SearchResult(installed, None, len(installed))
else:
return SearchResult([], None, 0)
def downgrade(self, pkg: SnapApplication, root_password: Optional[str], watcher: ProcessWatcher) -> bool:
if not snap.is_installed():
watcher.print("'snap' seems not to be installed")
return False
if not snapd.is_running():
watcher.print("'snapd' seems not to be running")
return False
return ProcessHandler(watcher).handle_simple(snap.downgrade_and_stream(pkg.name, root_password))[0]
def upgrade(self, requirements: UpgradeRequirements, root_password: Optional[str], watcher: ProcessWatcher) -> SystemProcess:
raise Exception(f"'upgrade' is not supported by {SnapManager.__class__.__name__}")
def uninstall(self, pkg: SnapApplication, root_password: Optional[str], watcher: ProcessWatcher, disk_loader: DiskCacheLoader) -> TransactionResult:
if snap.is_installed() and snapd.is_running():
uninstalled = ProcessHandler(watcher).handle_simple(snap.uninstall_and_stream(pkg.name, root_password))[0]
if uninstalled:
if self.suggestions_cache:
self.suggestions_cache.delete(pkg.name)
return TransactionResult(success=True, installed=None, removed=[pkg])
return TransactionResult.fail()
def get_managed_types(self) -> Set[Type[SoftwarePackage]]:
return {SnapApplication}
def clean_cache_for(self, pkg: SnapApplication):
super(SnapManager, self).clean_cache_for(pkg)
self.api_cache.delete(pkg.id)
def get_info(self, pkg: SnapApplication) -> dict:
info = {
'description': pkg.description,
'developer': pkg.developer,
'license': pkg.license,
'contact': pkg.contact,
'snap-id': pkg.id,
'name': pkg.name,
'publisher': pkg.publisher,
'revision': pkg.rev,
'tracking': pkg.tracking,
'channel': pkg.channel,
'type': pkg.type
}
if pkg.installed:
commands = [*{c['name'] for c in SnapdClient(self.logger).list_commands(pkg.name)}]
commands.sort()
info['commands'] = commands
if pkg.installed_size:
info['installed_size']: get_human_size_str(pkg.installed_size)
elif pkg.download_size:
info['download_size'] = get_human_size_str(pkg.download_size)
return info
def get_history(self, pkg: SnapApplication) -> PackageHistory:
raise Exception(f"'get_history' is not supported by {pkg.__class__.__name__}")
def install(self, pkg: SnapApplication, root_password: Optional[str], disk_loader: DiskCacheLoader, watcher: ProcessWatcher) -> TransactionResult:
# retrieving all installed so it will be possible to know the additional installed runtimes after the operation succeeds
if not snap.is_installed():
watcher.print("'snap' seems not to be installed")
return TransactionResult.fail()
if not snapd.is_running():
watcher.print("'snapd' seems not to be running")
return TransactionResult.fail()
installed_names = {s['name'] for s in SnapdClient(self.logger).list_all_snaps()}
client = SnapdClient(self.logger)
snap_config = self.configman.get_config()
try:
channel = self._request_channel_installation(pkg=pkg, snap_config=snap_config, snapd_client=client, watcher=watcher)
pkg.channel = channel
except Exception:
watcher.print('Aborted by user')
return TransactionResult.fail()
res, output = ProcessHandler(watcher).handle_simple(snap.install_and_stream(app_name=pkg.name,
confinement=pkg.confinement,
root_password=root_password,
channel=channel))
if 'error:' in output:
res = False
if 'not available on stable' in output:
channels = RE_AVAILABLE_CHANNELS.findall(output)
if channels:
opts = [InputOption(label=c[0], value=c[1]) for c in channels]
channel_select = SingleSelectComponent(type_=SelectViewType.RADIO, label='', options=opts, default_option=opts[0])
body = f"<p>{self.i18n['snap.install.available_channels.message'].format(bold(self.i18n['stable']), bold(pkg.name))}.</p>"
body += f"<p>{self.i18n['snap.install.available_channels.help']}:</p>"
if watcher.request_confirmation(title=self.i18n['snap.install.available_channels.title'],
body=body,
components=[channel_select],
confirmation_label=self.i18n['continue'],
deny_label=self.i18n['cancel']):
self.logger.info(f"Installing '{pkg.name}' with the custom command '{channel_select.value}'")
res = ProcessHandler(watcher).handle(SystemProcess(new_root_subprocess(channel_select.value.value.split(' '), root_password=root_password)))
return self._gen_installation_response(success=res, pkg=pkg,
installed=installed_names, disk_loader=disk_loader)
else:
self.logger.error(f"Could not find available channels in the installation output: {output}")
return self._gen_installation_response(success=res, pkg=pkg, installed=installed_names, disk_loader=disk_loader)
def _gen_installation_response(self, success: bool, pkg: SnapApplication, installed: Set[str], disk_loader: DiskCacheLoader):
if success:
new_installed = []
try:
net_available = self.context.internet_checker.is_available()
current_installed = self.read_installed(disk_loader=disk_loader, internet_available=net_available).installed
except Exception:
new_installed = [pkg]
traceback.print_exc()
current_installed = None
if current_installed:
for p in current_installed:
if p.name == pkg.name or (not installed or p.name not in installed):
new_installed.append(p)
return TransactionResult(success=success, installed=new_installed, removed=[])
else:
return TransactionResult.fail()
def is_enabled(self) -> bool:
return self.enabled
def set_enabled(self, enabled: bool):
self.enabled = enabled
def can_work(self) -> Tuple[bool, Optional[str]]:
return (True, None) if snap.is_installed() else (False, self.i18n['missing_dep'].format(dep=bold('snap')))
def requires_root(self, action: SoftwareAction, pkg: SnapApplication) -> bool:
return action not in (SoftwareAction.PREPARE, SoftwareAction.SEARCH)
def refresh(self, pkg: SnapApplication, root_password: Optional[str], watcher: ProcessWatcher) -> bool:
return ProcessHandler(watcher).handle_simple(snap.refresh_and_stream(pkg.name, root_password))[0]
def change_channel(self, pkg: SnapApplication, root_password: Optional[str], watcher: ProcessWatcher) -> bool:
if not self.context.internet_checker.is_available():
raise NoInternetException()
try:
channel = self._request_channel_installation(pkg=pkg,
snap_config=None,
snapd_client=SnapdClient(self.logger),
watcher=watcher,
exclude_current=True)
if not channel:
watcher.show_message(title=self.i18n['snap.action.channel.label'],
body=self.i18n['snap.action.channel.error.no_channel'])
return False
return ProcessHandler(watcher).handle_simple(snap.refresh_and_stream(app_name=pkg.name,
root_password=root_password,
channel=channel))[0]
except Exception:
return False
def _start_category_task(self, taskman: TaskManager, create_config: CreateConfigFile, downloader: CategoriesDownloader):
if taskman:
taskman.update_progress('snap_cats', 0, self.i18n['task.waiting_task'].format(bold(create_config.task_name)))
create_config.join()
categories_exp = create_config.config['categories_exp']
downloader.expiration = categories_exp if isinstance(categories_exp, int) else None
taskman.update_progress('snap_cats', 1, None)
def _finish_category_task(self, taskman: TaskManager):
if taskman:
taskman.update_progress('snap_cats', 100, None)
taskman.finish_task('snap_cats')
def prepare(self, task_manager: TaskManager, root_password: Optional[str], internet_available: bool):
create_config = CreateConfigFile(taskman=task_manager, configman=self.configman, i18n=self.i18n,
task_icon_path=get_icon_path(), logger=self.logger)
create_config.start()
task_manager.register_task('snap_cats', self.i18n['task.download_categories'], get_icon_path())
category_downloader = CategoriesDownloader(id_='snap', manager=self, http_client=self.http_client,
logger=self.logger,
url_categories_file=URL_CATEGORIES_FILE,
categories_path=CATEGORIES_FILE_PATH,
internet_connection=internet_available,
internet_checker=self.context.internet_checker,
after=lambda: self._finish_category_task(task_manager))
category_downloader.before = lambda: self._start_category_task(task_manager, create_config, category_downloader)
category_downloader.start()
def list_updates(self, internet_available: bool) -> List[PackageUpdate]:
pass
def list_warnings(self, internet_available: bool) -> Optional[List[str]]:
if not snapd.is_running():
snap_bold = bold('Snap')
return [self.i18n['snap.notification.snapd_unavailable'].format(bold('snapd'), snap_bold),
self.i18n['snap.notification.snap.disable'].format(snap_bold,
bold(f"{self.i18n['settings'].capitalize()} > {self.i18n['core.config.tab.types']}"))]
elif internet_available:
available, output = snap.is_api_available()
if not available:
self.logger.warning(f'It seems Snap API is not available. Search output: {output}')
return [self.i18n['snap.notifications.api.unavailable'].format(bold('Snaps'), bold('Snap'))]
def _fill_suggestion(self, name: str, priority: SuggestionPriority, snapd_client: SnapdClient,
out: List[PackageSuggestion]):
res = snapd_client.find_by_name(name)
if res:
if len(res) == 1:
app_json = res[0]
else:
jsons_found = [p for p in res if p['name'] == name]
app_json = jsons_found[0] if jsons_found else None
if app_json:
sug = PackageSuggestion(self._map_to_app(app_json, False), priority)
self.suggestions_cache.add(name, sug)
out.append(sug)
return
self.logger.warning(f"Could not retrieve suggestion '{name}'")
def _map_to_app(self, app_json: dict, installed: bool, disk_loader: Optional[DiskCacheLoader] = None, is_application: bool = False) -> SnapApplication:
app = SnapApplication(id=app_json.get('id'),
name=app_json.get('name'),
license=app_json.get('license'),
version=app_json.get('version'),
latest_version=app_json.get('version'),
description=app_json.get('description', app_json.get('summary')),
installed=installed,
rev=app_json.get('revision'),
publisher=app_json['publisher'].get('display-name', app_json['publisher'].get('username')),
verified_publisher=app_json['publisher'].get('validation') == 'verified',
icon_url=app_json.get('icon'),
screenshots={m['url'] for m in app_json.get('media', []) if m['type'] == 'screenshot'},
download_size=app_json.get('download-size'),
channel=app_json.get('channel'),
confinement=app_json.get('confinement'),
app_type=app_json.get('type'),
app=is_application,
installed_size=app_json.get('installed-size'))
if disk_loader and app.installed:
disk_loader.fill(app)
self._fill_categories(app)
app.status = PackageStatus.READY
return app
def _read_local_suggestions_file(self) -> Optional[str]:
try:
with open(self.suggestions_url) as f:
suggestions_str = f.read()
return suggestions_str
except FileNotFoundError:
self.logger.error(f"Local Snap suggestions file not found: {self.suggestions_url}")
except OSError:
self.logger.error(f"Could not read local Snap suggestions file: {self.suggestions_url}")
traceback.print_exc()
def _download_remote_suggestions_file(self) -> Optional[str]:
self.logger.info(f"Downloading the Snap suggestions from {self.suggestions_url}")
file = self.http_client.get(self.suggestions_url)
if file:
return file.text
def list_suggestions(self, limit: int, filter_installed: bool) -> Optional[List[PackageSuggestion]]:
if limit == 0 or not snapd.is_running():
return
if self.is_local_suggestions_file_mapped():
suggestions_str = self._read_local_suggestions_file()
else:
suggestions_str = self._download_remote_suggestions_file()
if suggestions_str is None:
return
if not suggestions_str:
self.logger.warning(f"No Snap suggestion found in {self.suggestions_url}")
return
ids_prios = suggestions.parse(suggestions_str, self.logger, 'Snap')
if not ids_prios:
self.logger.warning(f"No Snap suggestion could be parsed from {self.suggestions_url}")
return
suggestion_by_priority = suggestions.sort_by_priority(ids_prios)
snapd_client = SnapdClient(self.logger)
if filter_installed:
installed = {s['name'].lower() for s in snapd_client.list_all_snaps()}
if installed:
suggestion_by_priority = tuple(n for n in suggestion_by_priority if n not in installed)
if suggestion_by_priority and 0 < limit < len(suggestion_by_priority):
suggestion_by_priority = suggestion_by_priority[0:limit]
self.logger.info(f'Available Snap suggestions: {len(suggestion_by_priority)}')
if not suggestion_by_priority:
return
self.logger.info("Mapping Snap suggestions")
instances, threads = [], []
res, cached_count = [], 0
for name in suggestion_by_priority:
cached_sug = self.suggestions_cache.get(name)
if cached_sug:
res.append(cached_sug)
cached_count += 1
else:
t = Thread(target=self._fill_suggestion, args=(name, ids_prios[name], snapd_client, res), daemon=True)
t.start()
threads.append(t)
time.sleep(0.001) # to avoid being blocked
for t in threads:
t.join()
if cached_count > 0:
self.logger.info(f"Returning {cached_count} cached Snap suggestions")
return res
def is_default_enabled(self) -> bool:
return True
def launch(self, pkg: SnapApplication):
commands = SnapdClient(self.logger).list_commands(pkg.name)
if commands:
if len(commands) == 1:
cmd = commands[0]['name']
else:
desktop_cmd = [c for c in commands if 'desktop-file' in c]
if desktop_cmd:
cmd = desktop_cmd[0]['name']
else:
cmd = commands[0]['name']
self.logger.info(f"Running '{pkg.name}': {cmd}")
snap.run(cmd)
def get_screenshots(self, pkg: SnapApplication) -> Generator[str, None, None]:
if pkg.screenshots:
yield from pkg.screenshots
def get_settings(self) -> Optional[Generator[SettingsView, None, None]]:
snap_config = self.configman.get_config()
install_channel = new_select(label=self.i18n['snap.config.install_channel'],
opts=[(self.i18n['yes'].capitalize(), True, None),
(self.i18n['no'].capitalize(), False, None)],
value=bool(snap_config['install_channel']),
id_='snap_install_channel',
tip=self.i18n['snap.config.install_channel.tip'])
cat_exp_val = snap_config['categories_exp'] if isinstance(snap_config['categories_exp'], int) else ''
categories_exp = TextInputComponent(id_='snap_cat_exp',
value=cat_exp_val,
only_int=True,
label=self.i18n['snap.config.categories_exp'],
tooltip=self.i18n['snap.config.categories_exp.tip'])
panel = PanelComponent([FormComponent([install_channel, categories_exp], self.i18n['installation'].capitalize())])
yield SettingsView(self, panel)
def save_settings(self, component: PanelComponent) -> Tuple[bool, Optional[List[str]]]:
config_ = self.configman.get_config()
form = component.get_component_by_idx(0, FormComponent)
config_['install_channel'] = form.get_component('snap_install_channel', SingleSelectComponent).get_selected()
config_['categories_exp'] = form.get_component('snap_cat_exp', TextInputComponent).get_int_value()
try:
self.configman.save_config(config_)
return True, None
except Exception:
return False, [traceback.format_exc()]
def _request_channel_installation(self, pkg: SnapApplication, snap_config: Optional[dict], snapd_client: SnapdClient, watcher: ProcessWatcher, exclude_current: bool = False) -> Optional[str]:
if snap_config is None or snap_config['install_channel']:
try:
data = [r for r in snapd_client.find_by_name(pkg.name) if r['name'] == pkg.name]
except Exception:
self.logger.warning(f"snapd client could not retrieve channels for '{pkg.name}'")
return
if not data:
self.logger.warning(f"snapd client could find a match for name '{pkg.name}' when retrieving its channels")
else:
if not data[0].get('channels'):
self.logger.info(f"No channel available for '{pkg.name}'. Skipping selection.")
else:
if pkg.channel:
current_channel = pkg.channel if '/' in pkg.channel else f'latest/{pkg.channel}'
else:
current_channel = f"latest/{data[0].get('channel', 'stable')}"
opts = []
def_opt = None
for channel in sorted(data[0]['channels'].keys()):
if exclude_current:
if channel != current_channel:
opts.append(InputOption(label=channel, value=channel))
else:
op = InputOption(label=channel, value=channel)
opts.append(op)
if not def_opt and channel == current_channel:
def_opt = op
if not opts:
self.logger.info(f"No different channel available for '{pkg.name}'. Skipping selection.")
return
select = SingleSelectComponent(label='',
options=opts,
default_option=def_opt if def_opt else opts[0],
type_=SelectViewType.RADIO)
if not watcher.request_confirmation(title=self.i18n['snap.install.available_channels.title'],
body=self.i18n['snap.install.channel.body'] + ':',
components=[select],
confirmation_label=self.i18n['proceed'].capitalize(),
deny_label=self.i18n['cancel'].capitalize()):
raise Exception('aborted')
else:
return select.get_selected()
@property
def suggestions_url(self) -> str:
if not self._suggestions_url:
file_url = self.context.get_suggestion_url(self.__module__)
if not file_url:
file_url = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/snap/suggestions.txt'
self._suggestions_url = file_url
if file_url.startswith('/'):
self.logger.info(f"Local Snap suggestions file mapped: {file_url}")
return self._suggestions_url
def is_local_suggestions_file_mapped(self) -> bool:
return self.suggestions_url.startswith('/')

View File

@@ -1,130 +0,0 @@
from typing import Optional, Set, Iterable, Tuple
from bauh.api.abstract.model import SoftwarePackage, CustomSoftwareAction
from bauh.commons import resource
from bauh.gems.snap import ROOT_DIR
class SnapApplication(SoftwarePackage):
__actions: Optional[Tuple[CustomSoftwareAction, CustomSoftwareAction]] = None
@classmethod
def actions(cls) -> Tuple[CustomSoftwareAction, CustomSoftwareAction]:
if cls.__actions is None:
cls.__actions = (
CustomSoftwareAction(i18n_status_key='snap.action.refresh.status',
i18n_label_key='snap.action.refresh.label',
i18n_description_key='snap.action.refresh.desc',
icon_path=resource.get_path('img/refresh.svg', ROOT_DIR),
manager_method='refresh',
requires_root=True,
i18n_confirm_key='snap.action.refresh.confirm'),
CustomSoftwareAction(i18n_status_key='snap.action.channel.status',
i18n_label_key='snap.action.channel.label',
i18n_confirm_key='snap.action.channel.confirm',
i18n_description_key='snap.action.channel.desc',
icon_path=resource.get_path('img/refresh.svg', ROOT_DIR),
manager_method='change_channel',
requires_root=True,
requires_confirmation=False)
)
return cls.__actions
def __init__(self, id: str = None, name: str = None, version: str = None, latest_version: str = None,
description: str = None, publisher: str = None, rev: str = None, notes: str = None,
confinement: str = None, verified_publisher: bool = False,
screenshots: Optional[Set[str]] = None,
license: Optional[str] = None,
installed: bool = False,
icon_url: Optional[str] = None,
download_size: Optional[int] = None,
developer: Optional[str] = None,
contact: Optional[str] = None,
tracking: Optional[str] = None,
app_type: Optional[str] = None,
channel: Optional[str] = None,
app: bool = False,
installed_size: Optional[int] = None):
super(SnapApplication, self).__init__(id=id, name=name, version=version,
latest_version=latest_version, description=description,
license=license, installed=installed, icon_url=icon_url)
self.publisher = publisher
self.rev = rev
self.notes = notes
self.confinement = confinement
self.verified_publisher = verified_publisher
self.screenshots = screenshots
self.download_size = download_size
self.developer = developer
self.contact = contact
self.tracking = tracking
self.type = app_type
self.channel = channel
self.app = app
self.installed_size = installed_size
def supports_disk_cache(self):
return self.installed
def has_history(self):
return False
def has_info(self):
return True
def can_be_downgraded(self):
return self.installed
def get_type(self):
return 'snap'
def get_default_icon_path(self):
return resource.get_path('img/snap.svg', ROOT_DIR)
def get_type_icon_path(self):
return self.get_default_icon_path()
def is_application(self) -> bool:
if self.installed:
return self.app
else:
return self.type == 'app'
def get_disk_cache_path(self):
return f'{super(SnapApplication, self).get_disk_cache_path()}/installed/{self.name}'
def is_trustable(self) -> bool:
return self.verified_publisher
def get_data_to_cache(self):
return {
'categories': self.categories
}
def fill_cached_data(self, data: dict):
if data:
for base_attr in self.get_data_to_cache().keys():
if data.get(base_attr):
setattr(self, base_attr, data[base_attr])
def can_be_run(self) -> bool:
return bool(self.installed and self.is_application())
def get_publisher(self):
return self.publisher
def get_custom_actions(self) -> Optional[Iterable[CustomSoftwareAction]]:
if self.installed:
return self.actions()
def supports_backup(self) -> bool:
return True
def has_screenshots(self) -> bool:
return not self.installed and self.screenshots
def __eq__(self, other):
if isinstance(other, SnapApplication):
return self.name == other.name

View File

@@ -1,128 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
width="512"
height="512"
viewBox="0 0 511.99998 512"
xml:space="preserve"
sodipodi:docname="refresh.svg"
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><metadata
id="metadata45"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs43" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview41"
showgrid="false"
showborder="true"
inkscape:zoom="0.48437083"
inkscape:cx="133.71431"
inkscape:cy="289.92474"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:document-rotation="0" />
<g
id="g8"
transform="matrix(1.0938971,0,0,1.0217723,-10.489433,7.0809411)"
style="fill:#3991e9;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none">
<g
id="g6"
style="fill:#3991e9;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none">
<path
d="m 55.323,203.641 c 15.664,0 29.813,-9.405 35.872,-23.854 25.017,-59.604 83.842,-101.61 152.42,-101.61 37.797,0 72.449,12.955 100.23,34.442 l -21.775,3.371 c -7.438,1.153 -13.224,7.054 -14.232,14.512 -1.01,7.454 3.008,14.686 9.867,17.768 l 119.746,53.872 c 5.249,2.357 11.33,1.904 16.168,-1.205 4.83,-3.114 7.764,-8.458 7.796,-14.208 l 0.621,-131.943 c 0.042,-7.506 -4.851,-14.144 -12.024,-16.332 -7.185,-2.188 -14.947,0.589 -19.104,6.837 L 414.403,70.096 C 370.398,26.778 310.1,0 243.615,0 142.806,0 56.133,61.562 19.167,149.06 c -5.134,12.128 -3.84,26.015 3.429,36.987 7.269,10.976 19.556,17.594 32.727,17.594 z"
id="path2"
inkscape:connector-curvature="0"
style="fill:#3991e9;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none" />
<path
d="m 464.635,301.184 c -7.27,-10.977 -19.558,-17.594 -32.728,-17.594 -15.664,0 -29.813,9.405 -35.872,23.854 -25.018,59.604 -83.843,101.61 -152.42,101.61 -37.798,0 -72.45,-12.955 -100.232,-34.442 l 21.776,-3.369 c 7.437,-1.153 13.223,-7.055 14.233,-14.514 1.009,-7.453 -3.008,-14.686 -9.867,-17.768 L 49.779,285.089 c -5.25,-2.356 -11.33,-1.905 -16.169,1.205 -4.829,3.114 -7.764,8.458 -7.795,14.207 l -0.622,131.943 c -0.042,7.506 4.85,14.144 12.024,16.332 7.185,2.188 14.948,-0.59 19.104,-6.839 l 16.505,-24.805 c 44.004,43.32 104.303,70.098 170.788,70.098 100.811,0 187.481,-61.561 224.446,-149.059 5.137,-12.128 3.843,-26.014 -3.425,-36.987 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#3991e9;fill-opacity:1;stroke:none;stroke-width:39.2413;stroke-miterlimit:4;stroke-dasharray:none" />
</g>
</g>
<g
id="g10"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g12"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g14"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g16"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g18"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g20"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g22"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g24"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g26"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g28"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g30"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g32"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g34"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g36"
transform="translate(-15.027876,-464.19647)">
</g>
<g
id="g38"
transform="translate(-15.027876,-464.19647)">
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -1,70 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg3957"
height="6.3499999mm"
width="6.3499999mm"
version="1.1"
viewBox="0 0 24.000124 24.000125"
sodipodi:docname="snap.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<defs
id="defs83" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1360"
inkscape:window-height="703"
id="namedview81"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.6803366"
inkscape:cx="192.32571"
inkscape:cy="-49.135216"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg3957" />
<metadata
id="metadata3963">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
style="fill:none"
id="snapcraft-logo--web"
transform="matrix(0.76433516,0,0,0.80000413,0,-0.40000206)">
<path
id="Combined-Shape"
d="m 18.06,7.28 6.92,3.08 -6.92,6.92 z M 4.84,30.5 l 8.49,-15.92 3.73,3.7 z M 0,0.5 17.47,6.82 v 11.05 z"
inkscape:connector-curvature="0"
style="fill:#82bfa1" />
<polygon
id="Shape"
points="18.46,6.82 31.4,12.57 28.53,6.82 "
style="fill:#fa6340" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -1,34 +0,0 @@
gem.snap.info=Aplicacions publicades a https://snapcraft.io/store
snap.action.channel.confirm=Change channel of {} ?
snap.action.channel.error.no_channel=No available channel for {}
snap.action.channel.label=Change channel
snap.action.channel.desc=Allows to change the application channel (if available)
snap.action.channel.status=Changing channel of
snap.action.refresh.confirm=Actualitza {} ?
snap.action.refresh.desc=Updates the application for the latest version
snap.action.refresh.label=Actualitza
snap.action.refresh.status=Sestà actualitzant
snap.config.categories_exp=Categories expiration
snap.config.categories_exp.tip=It defines the expiration time (in HOURS) of the Snaps categories mapping file stored in disc. Use 0 so that it is always updated during initialization.
snap.config.install_channel=Display channels
snap.config.install_channel.tip=It allows to select one of the available channels for the given application to be installed
snap.info.channel=channel
snap.info.commands=ordres
snap.info.contact=contacte
snap.info.description=descripció
snap.info.download_size=Size (Download)
snap.info.installed=instal·lada
snap.info.installed_size=Size (Instalattion)
snap.info.license=llicència
snap.info.publisher=publicador
snap.info.revision=revisió
snap.info.tracking=seguiment
snap.info.type=type
snap.info.version=versió
snap.install.available_channels.help=Seleccioneu-ne un si voleu continuar
snap.install.available_channels.message=No hi ha un canal {} (stable) disponible per a {}. Però més avall nhi ha els següents
snap.install.available_channels.title=Canals disponibles
snap.install.channel.body=Select a channel to proceed
snap.notification.snap.disable=Si no voleu utilitzar aplicacions snap, desmarqueu {} a {}
snap.notification.snapd_unavailable=Sembla que no sha iniciat o activat {}. Els paquets {} no estaran disponibles.
snap.notifications.api.unavailable=Sembla que lAPI de {} no està disponible en aquests moments. No podreu cercar aplicacions {} noves.

View File

@@ -1,34 +0,0 @@
gem.snap.info=Auf https://snapcraft.io/store veröffentlichte Anwendungen
snap.action.channel.confirm=Kanal von {} ändern?
snap.action.channel.error.no_channel=Kein verfügbarer Kanal für {}
snap.action.channel.label=Kanal ändern
snap.action.channel.desc=Ermöglicht das Ändern des Anwendungskanals (falls verfügbar)
snap.action.channel.status=Ändern des Kanals von
snap.action.refresh.confirm=Aktualisieren {} ?
snap.action.refresh.desc=Aktualisiert die Anwendung auf die neueste Version
snap.action.refresh.label=Aktualisieren
snap.action.refresh.status=Aktualisieren
snap.config.categories_exp=Gültigkeitszeitraum der Kategorien
snap.config.categories_exp.tip=Legt den Gültigkeitszeitraum (in Stunden) der auf dem Datenträger gespeicherten Kategorien-Zuordnungsdatei der Snaps fest. Verwenden Sie 0, damit die Datei während der Initialisierung immer aktualisiert wird.
snap.config.install_channel=Kanäle anzeigen
snap.config.install_channel.tip=Hier können Sie einen der verfügbaren Kanäle für die zu installierende Anwendung auswählen
snap.info.channel=Kanal
snap.info.commands=Befehle
snap.info.contact=Kontakt
snap.info.description=Beschreibung
snap.info.download_size=Größe (Download)
snap.info.installed=installiert
snap.info.installed_size=Größe (Installation)
snap.info.license=Lizenz
snap.info.publisher=Herausgeber
snap.info.revision=Revision
snap.info.tracking=Verfolgung
snap.info.type=Typ
snap.info.version=Version
snap.install.available_channels.help=Wählen Sie eine aus, wenn Sie fortfahren möchten
snap.install.available_channels.message=Es ist kein {} Kanal für {} verfügbar. Aber es gibt diese untenstehenden
snap.install.available_channels.title=Verfügbare Kanäle
snap.install.channel.body=Wählen Sie einen Kanal, um fortzufahren
snap.notification.snap.disable=Wenn Sie keine Snap-Anwendungen verwenden möchten, deaktivieren Sie {} in {}
snap.notification.snapd_unavailable={} scheint nicht gestartet oder aktiviert zu sein. {} Pakete werden nicht verfügbar sein.
snap.notifications.api.unavailable=Es scheint, dass die {} API im Moment nicht verfügbar ist. Es wird nicht möglich sein, nach neuen {} Anwendungen zu suchen.

View File

@@ -1,34 +0,0 @@
gem.snap.info=Applications published at https://snapcraft.io/store
snap.action.channel.confirm=Change channel of {}?
snap.action.channel.error.no_channel=No available channel for {}
snap.action.channel.label=Change channel
snap.action.channel.desc=Allows to change the application channel (if available)
snap.action.channel.status=Changing channel of
snap.action.refresh.confirm=Refresh {}?
snap.action.refresh.desc=Updates the application for the latest version
snap.action.refresh.label=Refresh
snap.action.refresh.status=Refreshing
snap.config.categories_exp=Categories expiration
snap.config.categories_exp.tip=It defines the expiration time (in HOURS) of the Snaps categories mapping file stored in disc. Use 0 so that it is always updated during initialization.
snap.config.install_channel=Display channels
snap.config.install_channel.tip=It allows to select one of the available channels for the given application to be installed
snap.info.channel=channel
snap.info.commands=commands
snap.info.contact=contact
snap.info.description=description
snap.info.download_size=Size (Download)
snap.info.installed=installed
snap.info.installed_size=Size (Installation)
snap.info.license=license
snap.info.publisher=publisher
snap.info.revision=revision
snap.info.tracking=tracking
snap.info.type=type
snap.info.version=version
snap.install.available_channels.help=Select one if you want to continue
snap.install.available_channels.message=There is no {} channel available for {}. But there are these below
snap.install.available_channels.title=Available channels
snap.install.channel.body=Select a channel to proceed
snap.notification.snap.disable=If you do not want to use Snap applications, uncheck {} in {}
snap.notification.snapd_unavailable={} seems not to be started or enabled. {} packages will not be available.
snap.notifications.api.unavailable=It seems the {} API is unavailable at the moment. It will not be possible to search for new {} applications.

View File

@@ -1,34 +0,0 @@
gem.snap.info=Aplicativos publicados en https://snapcraft.io/store
snap.action.channel.confirm=Cambiar canal de {}?
snap.action.channel.desc=Permite cambiar el canal de la aplicación (si disponible)
snap.action.channel.error.no_channel=No hay canal disponible para {}
snap.action.channel.status=Cambiando canal de
snap.action.channel.label=Cambiar canal
snap.action.refresh.confirm=Actualizar {} ?
snap.action.refresh.desc=Actualiza la aplicación para la versión más reciente
snap.action.refresh.label=Actualizar
snap.action.refresh.status=Actualizando
snap.config.categories_exp=Expiración de categorías
snap.config.categories_exp.tip=Define el tiempo de vencimiento (en HORAS) del archivo de mapeo de categorías de Snaps almacenado en el disco. Utilice 0 para que siempre se actualice durante la inicialización.
snap.config.install_channel=Mostrar canales
snap.config.install_channel.tip=Permite seleccionar uno de los canales disponibles para la aplicación a instalar
snap.info.channel=canal
snap.info.commands=comandos
snap.info.contact=contacto
snap.info.description=descripción
snap.info.download_size=Tamaño (Descarga)
snap.info.installed=instalado
snap.info.installed_size=Tamaño (Instalación)
snap.info.license=licencia
snap.info.publisher=publicador
snap.info.revision=revisión
snap.info.tracking=tracking
snap.info.type=tipo
snap.info.version=versión
snap.install.available_channels.help=Seleccione uno si desea continuar
snap.install.available_channels.message=No hay un canal {} ( stable ) disponible para {}. Pero hay estos otros abajo
snap.install.available_channels.title=Canales disponibles
snap.install.channel.body=Seleccione un canal para continuar
snap.notification.snap.disable=Si no desea usar aplicativos Snap, desmarque {} en {}
snap.notification.snapd_unavailable={} parece no estar inicializado o habilitado. Los paquetes {} estarán indisponibles.
snap.notifications.api.unavailable=Parece que la API de {} no está disponible en este momento. No será posible buscar nuevos aplicativos {}.

View File

@@ -1,34 +0,0 @@
gem.snap.info=Applications publiées à https://snapcraft.io/store
snap.action.channel.confirm=Changer le canal de {} ?
snap.action.channel.desc=Allows to change the application channel (if available)
snap.action.channel.error.no_channel=Aucun canal disponible pour {}
snap.action.channel.label=Changer de canal
snap.action.channel.status=Changement de canal de
snap.action.refresh.confirm=Actualiser {} ?
snap.action.refresh.desc=Updates the application for the latest version
snap.action.refresh.label=Actualiser
snap.action.refresh.status=Actualisation
snap.config.categories_exp=Categories expiration
snap.config.categories_exp.tip=It defines the expiration time (in HOURS) of the Snaps categories mapping file stored in disc. Use 0 so that it is always updated during initialization.
snap.config.install_channel=Afficher les canaux
snap.config.install_channel.tip=Permet de sélectionner un des canaux disponibles pour l'application à installer
snap.info.channel=canal
snap.info.commands=commandes
snap.info.contact=contact
snap.info.description=description
snap.info.download_size=Taille (Téléchargement)
snap.info.installed=installé
snap.info.installed_size=Size (Installation)
snap.info.license=licence
snap.info.publisher=éditeur
snap.info.revision=révision
snap.info.tracking=pisteur
snap.info.type=type
snap.info.version=version
snap.install.available_channels.help=Selectionnez pour continuer
snap.install.available_channels.message=Pas de canal {} disponible pour {}. Mais les suivants existent
snap.install.available_channels.title=Canaux disponibles
snap.install.channel.body=Choisissez un canal pour continuer
snap.notification.snap.disable=Si vous ne voulez pas utiliser des applications Snap, décochez {} dans {}
snap.notification.snapd_unavailable={} a l'air de n'être ni démarré ni activé. Les paquets {} ne seront pas disponibles.
snap.notifications.api.unavailable=L'API {} a l'air d'être momentanément indisponible. Impossible de chercher de nouvelles applications {}.

View File

@@ -1,34 +0,0 @@
gem.snap.info=Applicazioni pubblicate su https://snapcraft.io/store
snap.action.channel.confirm=Cambia canale di {} ?
snap.action.channel.desc=Allows to change the application channel (if available)
snap.action.channel.error.no_channel=Nessun canale disponibile per {}
snap.action.channel.label=Cambia canale
snap.action.channel.status=Cambiare canale di
snap.action.refresh.confirm=Ripristina {} ?
snap.action.refresh.desc=Updates the application for the latest version
snap.action.refresh.label=Ripristina
snap.action.refresh.status=Ripristinare
snap.config.categories_exp=Scadenza delle categorie
snap.config.categories_exp.tip=Definisce il tempo di scadenza (in ORE) del file di mappatura delle categorie di Snap archiviato nel disco. Utilizzare 0 in modo che venga sempre aggiornato durante l'inizializzazione.
snap.config.install_channel=Visualizza i canali
snap.config.install_channel.tip=Permette di selezionare uno dei canali disponibili per la data applicazione da installare
snap.info.channel=canale
snap.info.commands=comandi
snap.info.contact=contatto
snap.info.description=descrizione
snap.info.download_size=dimensione (Download)
snap.info.installed=installato
snap.info.installed_size=Dimensione (Installazione)
snap.info.license=licenza
snap.info.publisher=editore
snap.info.revision=revisione
snap.info.tracking=tracking
snap.info.type=tipo
snap.info.version=versione
snap.install.available_channels.help=Selezionane uno se vuoi continuare
snap.install.available_channels.message=Non esiste un {} canale disponibile per {}. Ma ci sono questi sotto
snap.install.available_channels.title=Canali disponibili
snap.install.channel.body=Seleziona un canale per procedere
snap.notification.snap.disable=Se non si desidera utilizzare le applicazioni Snap, deselezionare {} in {}
snap.notification.snapd_unavailable={} sembra non essere avviato o abilitato. {} i pacchetti non saranno disponibili.
snap.notifications.api.unavailable=Sembra che l'API {} non sia al momento disponibile. Non sarà possibile cercare nuove {} applicazioni.

View File

@@ -1,34 +0,0 @@
gem.snap.info=Aplicativos publicados em https://snapcraft.io/store
snap.action.channel.confirm=Alterar canal de {} ?
snap.action.channel.desc=Permite trocar o canal do aplicativo (se disponível)
snap.action.channel.error.no_channel=Nenhum canal disponível para {}
snap.action.channel.label=Alterar canal
snap.action.channel.status=Alterando canal de
snap.action.refresh.confirm=Atualizar {} ?
snap.action.refresh.desc=Atualiza o aplicativo para a versão mais recente
snap.action.refresh.label=Atualizar
snap.action.refresh.status=Atualizando
snap.config.categories_exp=Expiração de categorias
snap.config.categories_exp.tip=Define o tempo de expiração (em HORAS) do arquivo de mapeamento de categorias Snap armazenado em disco. Utilize 0 para que ele seja sempre atualizado durante a inicialização.
snap.config.install_channel=Exibir canais
snap.config.install_channel.tip=Permite-se selecionar um dos canais disponíveis para o aplicativo a ser instalado
snap.info.channel=canal
snap.info.commands=comandos
snap.info.contact=contato
snap.info.description=descrição
snap.info.download_size=Tamanho (Download)
snap.info.installed=instalado
snap.info.installed_size=Tamanho (Instalação)
snap.info.license=licença
snap.info.publisher=publicador
snap.info.revision=revisão
snap.info.tracking=tracking
snap.info.type=tipo
snap.info.version=versão
snap.install.available_channels.help=Selecione algum se quiser continuar
snap.install.available_channels.message=Não há um canal {} ( stable ) disponível para {}. Porém existem estes outros abaixo
snap.install.available_channels.title=Canais disponíveis
snap.install.channel.body=Selecione um canal para continuar
snap.notification.snap.disable=Se não deseja usar aplicativos Snap, desmarque {} em {}
snap.notification.snapd_unavailable={} parece não estar inicializado ou habilitado. Os pacotes {} estarão indisponíveis.
snap.notifications.api.unavailable=Parece que a API de {} está indisponível no momento. Não será possível buscar novos aplicativos {}.

View File

@@ -1,34 +0,0 @@
gem.snap.info=Приложения, опубликованные на https://snapcraft.io/store
snap.action.channel.confirm=Изменить канал на {} ?
snap.action.channel.desc=Позволяет изменить канал приложения (если он доступен)
snap.action.channel.error.no_channel=Нет доступного канала для {}
snap.action.channel.label=Изменить канал
snap.action.channel.status=Изменение канала на
snap.action.refresh.confirm=Обновить {} ?
snap.action.refresh.desc=Обновление приложения до последней версии
snap.action.refresh.label=Обновить
snap.action.refresh.status=Обновляется
snap.config.categories_exp=Срок действия категорий
snap.config.categories_exp.tip=Определяет время действия (в часах) файла отображения категорий Snaps, хранящегося на диске. Используйте 0, чтобы он всегда обновлялся при инициализации.
snap.config.install_channel=Отображение каналов
snap.config.install_channel.tip=Позволяет выбрать один из доступных каналов для устанавливаемого приложения
snap.info.channel=Канал
snap.info.commands=Команды
snap.info.contact=Контакт
snap.info.description=Описание
snap.info.download_size=Размер (Загрузки)
snap.info.installed=Размер установки
snap.info.installed_size=Размер (Установки)
snap.info.license=Лицензия
snap.info.publisher=Издатель
snap.info.revision=Ревизия
snap.info.tracking=Отслеживание
snap.info.type=тип
snap.info.version=Версия
snap.install.available_channels.help=Выберите один, если вы хотите продолжить
snap.install.available_channels.message=Нет одного канала {}, доступного для {}. Но есть такие ниже
snap.install.available_channels.title=Доступные каналы
snap.install.channel.body=Выберите канал для продолжения операции
snap.notification.snap.disable=Если вы не хотите использовать приложения Snap, снимите флажок {} в {}
snap.notification.snapd_unavailable={} кажется, не запускается и не включается. {} пакеты будут недоступны.
snap.notifications.api.unavailable=Похоже, что API {} в данный момент недоступен. Невозможно будет искать новые приложения {}.

View File

@@ -1,34 +0,0 @@
gem.snap.info=https://snapcraft.io/store adresinde yayınlanan uygulamalar
snap.action.channel.confirm=Change channel of {} ?
snap.action.channel.desc=Allows to change the application channel (if available)
snap.action.channel.error.no_channel=No available channel for {}
snap.action.channel.label=Change channel
snap.action.channel.status=Changing channel of
snap.action.refresh.confirm=Yenile {} ?
snap.action.refresh.desc=Updates the application for the latest version
snap.action.refresh.label=Yenile
snap.action.refresh.status=Yenileniyor
snap.config.categories_exp=Categories expiration
snap.config.categories_exp.tip=It defines the expiration time (in HOURS) of the Snaps categories mapping file stored in disc. Use 0 so that it is always updated during initialization.
snap.config.install_channel=Display channels
snap.config.install_channel.tip=It allows to select one of the available channels for the given application to be installed
snap.info.channel=channel
snap.info.commands=komutlar
snap.info.contact=iletişim
snap.info.description=açıklama
snap.info.download_size=Size (Download)
snap.info.installed=yüklü
snap.info.installed_size=Size (Instalattion)
snap.info.license=lisans
snap.info.publisher=yayıncı
snap.info.revision=revizyon
snap.info.tracking=izleme
snap.info.type=type
snap.info.version=sürüm
snap.install.available_channels.help=Devam etmek istiyorsanız birini seçin
snap.install.available_channels.message={} için hiçbir {} kanalı yok. Ama aşağıda bunlar var
snap.install.available_channels.title=Kullanılabilir kanallar
snap.install.channel.body=Select a channel to proceed
snap.notification.snap.disable=Snap uygulamalarını kullanmak istemiyorsanız, {} içindeki {} işaretini kaldırın
snap.notification.snapd_unavailable={} başlatılmamış veya etkinleştirilmemiş gibi görünüyor. {} paketler mevcut olmayacak.
snap.notifications.api.unavailable=Görünüşe göre {} API şu anda kullanılamıyor. Yeni {} uygulamaları aramak mümkün olmayacak.

View File

@@ -1,34 +0,0 @@
gem.snap.info=在 https://snapcraft.io/store 上发布的应用程序
snap.action.channel.confirm=更改 {} 的频道?
snap.action.channel.error.no_channel=没有可用于 {} 的频道
snap.action.channel.label=更改频道
snap.action.channel.desc=允许更改应用程序的频道(如果可用)
snap.action.channel.status=更改
snap.action.refresh.confirm=刷新 {}
snap.action.refresh.desc=将应用程序更新到最新版本
snap.action.refresh.label=刷新
snap.action.refresh.status=刷新
snap.config.categories_exp=类别过期时间
snap.config.categories_exp.tip=它定义了 Snap 分类映射文件在磁盘上存储的过期时间(以小时为单位)。在初始化期间始终更新,使用 0。
snap.config.install_channel=显示频道
snap.config.install_channel.tip=允许为给定应用程序选择可用频道进行安装
snap.info.channel=频道
snap.info.commands=命令
snap.info.contact=联系
snap.info.description=描述
snap.info.download_size=大小(下载)
snap.info.installed=已安装
snap.info.installed_size=大小(安装)
snap.info.license=许可证
snap.info.publisher=发布者
snap.info.revision=修订版
snap.info.tracking=追踪
snap.info.type=类型
snap.info.version=版本
snap.install.available_channels.help=如果要继续,请选择一个
snap.install.available_channels.message=没有 {} 的可用频道供 {} 使用。但下面有这些
snap.install.available_channels.title=可用频道
snap.install.channel.body=选择一个频道以继续
snap.notification.snap.disable=如果您不想使用 Snap 应用程序,请取消选中 {}
snap.notification.snapd_unavailable={} 似乎没有启动或启用。{} 包将不可用。
snap.notifications.api.unavailable=目前似乎 {} API 不可用。将无法搜索新的 {} 应用程序。

View File

@@ -1,65 +0,0 @@
import os
import shutil
import subprocess
from io import StringIO
from typing import Tuple, Optional
from bauh.commons.system import SimpleProcess
def is_installed() -> bool:
return bool(shutil.which('snap'))
def uninstall_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess:
return SimpleProcess(cmd=('snap', 'remove', app_name),
root_password=root_password,
lang=None,
shell=True)
def install_and_stream(app_name: str, confinement: str, root_password: Optional[str], channel: Optional[str] = None) -> SimpleProcess:
install_cmd = ['snap', 'install', app_name] # default
if confinement == 'classic':
install_cmd.append('--classic')
if channel:
install_cmd.append(f'--channel={channel}')
return SimpleProcess(install_cmd, root_password=root_password, shell=True, lang=None)
def downgrade_and_stream(app_name: str, root_password: Optional[str]) -> SimpleProcess:
return SimpleProcess(cmd=('snap', 'revert', app_name),
root_password=root_password,
shell=True,
lang=None)
def refresh_and_stream(app_name: str, root_password: Optional[str], channel: Optional[str] = None) -> SimpleProcess:
cmd = ['snap', 'refresh', app_name]
if channel:
cmd.append(f'--channel={channel}')
return SimpleProcess(cmd=cmd,
root_password=root_password,
lang=None,
shell=True)
def run(cmd: str):
subprocess.Popen((f'snap run {cmd}',), shell=True, env={**os.environ})
def is_api_available() -> Tuple[bool, str]:
output = StringIO()
for o in SimpleProcess(('snap', 'search'), lang=None).instance.stdout:
if o:
output.write(o.decode())
output.seek(0)
output = output.read()
return 'error:' not in output, output

View File

@@ -1,120 +0,0 @@
import socket
import traceback
from logging import Logger
from typing import Optional, List
from requests import Session
from requests.adapters import HTTPAdapter
from urllib3.connection import HTTPConnection
from urllib3.connectionpool import HTTPConnectionPool
from bauh.commons.system import run_cmd
URL_BASE = 'http://snapd/v2'
class SnapdConnection(HTTPConnection):
def __init__(self):
super(SnapdConnection, self).__init__('localhost')
def connect(self):
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self.sock.connect("/run/snapd.socket")
class SnapdConnectionPool(HTTPConnectionPool):
def __init__(self):
super(SnapdConnectionPool, self).__init__('localhost')
def _new_conn(self):
return SnapdConnection()
class SnapdAdapter(HTTPAdapter):
def get_connection(self, url, proxies=None):
return SnapdConnectionPool()
class SnapdClient:
def __init__(self, logger: Logger):
self.logger = logger
self.session = self._new_session()
def _new_session(self) -> Optional[Session]:
try:
session = Session()
session.mount("http://snapd/", SnapdAdapter())
return session
except Exception:
self.logger.error("Could not establish a connection to 'snapd.socker'")
traceback.print_exc()
def query(self, query: str) -> Optional[List[dict]]:
final_query = query.strip()
if final_query and self.session:
res = self.session.get(url=f'{URL_BASE}/find', params={'q': final_query})
if res.status_code == 200:
json_res = res.json()
if json_res['status-code'] == 200:
return json_res['result']
def find_by_name(self, name: str) -> Optional[List[dict]]:
if name and self.session:
res = self.session.get(f'{URL_BASE}/find?name={name}')
if res.status_code == 200:
json_res = res.json()
if json_res['status-code'] == 200:
return json_res['result']
def list_all_snaps(self) -> List[dict]:
if self.session:
res = self.session.get(f'{URL_BASE}/snaps')
if res.status_code == 200:
json_res = res.json()
if json_res['status-code'] == 200:
return json_res['result']
return []
def list_only_apps(self) -> List[dict]:
if self.session:
res = self.session.get(f'{URL_BASE}/apps')
if res.status_code == 200:
json_res = res.json()
if json_res['status-code'] == 200:
return json_res['result']
return []
def list_commands(self, name: str) -> List[dict]:
if self.session:
res = self.session.get(f'{URL_BASE}/apps?names={name}')
if res.status_code == 200:
json_res = res.json()
if json_res['status-code'] == 200:
return [r for r in json_res['result'] if r['snap'] == name]
return []
def is_running() -> bool:
status = run_cmd('systemctl is-active snapd.service snapd.socket', print_error=False)
if not status:
return False
else:
for status in status.split('\n'):
if status.strip().lower() == 'active':
return True
return False