mirror of
https://github.com/spalencsar/bearhub.git
synced 2026-07-06 22:54:16 +02:00
Modernize runtime compatibility and initialize Bearhub fork
This commit is contained in:
10
README.md
10
README.md
@@ -1,4 +1,12 @@
|
||||
[](https://github.com/vinifmor/bauh/releases/) [](https://pypi.org/project/bauh) [](https://aur.archlinux.org/packages/bauh) [](https://aur.archlinux.org/packages/bauh-staging) [](https://github.com/vinifmor/bauh/blob/master/LICENSE) [](https://ko-fi.com/vinifmor)
|
||||
[](https://github.com/spalencsar/bearhub/blob/master/LICENSE)
|
||||
|
||||
# Bearhub
|
||||
|
||||
**Bearhub** is a community-maintained fork of [bauh](https://github.com/vinifmor/bauh), focused on keeping the application working on current Linux systems, especially modern Arch Linux and Python versions.
|
||||
|
||||
The original project was created by Vinicius Moreira. This fork continues from that codebase under the original license and keeps the history and authorship intact.
|
||||
|
||||
At the moment, parts of the documentation below still refer to the original `bauh` command name, paths, package names, and screenshots. Those references are historical and will be updated incrementally as the fork evolves.
|
||||
|
||||
**bauh** (baoo), formerly known as [fpakman](https://github.com/vinifmor/fpakman), is a graphical interface for managing your Linux software (packages/applications). It currently supports the following formats: AppImage, Debian and Arch Linux packages (including AUR), Flatpak, Snap and Web applications.
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import logging
|
||||
import os
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
from typing import Dict, List, Optional
|
||||
@@ -93,7 +93,7 @@ class CategoriesDownloader(Thread):
|
||||
self.logger.info(self._msg('Downloading category definitions from {}'.format(self.url_categories_file)))
|
||||
|
||||
try:
|
||||
timestamp = datetime.utcnow().timestamp()
|
||||
timestamp = datetime.now(timezone.utc).timestamp()
|
||||
res = self.http_client.get(self.url_categories_file)
|
||||
except requests.exceptions.ConnectionError:
|
||||
self.logger.error(self._msg('[{}] Could not download categories. The internet connection seems to be off.'.format(self.id_)))
|
||||
@@ -139,13 +139,13 @@ class CategoriesDownloader(Thread):
|
||||
timestamp_str = f.read()
|
||||
|
||||
try:
|
||||
categories_timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
categories_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self.logger.error(self._msg("An exception occurred when trying to parse the categories file timestamp from '{}'. The categories file should be re-downloaded.".format(categories_ts_path)))
|
||||
traceback.print_exc()
|
||||
return True
|
||||
|
||||
should_download = (categories_timestamp + timedelta(hours=self.expiration) <= datetime.utcnow())
|
||||
should_download = (categories_timestamp + timedelta(hours=self.expiration) <= datetime.now(timezone.utc))
|
||||
|
||||
if should_download:
|
||||
self.logger.info(self._msg("Cached categories file '{}' has expired. A new one should be downloaded.".format(self.categories_path)))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import logging
|
||||
import re
|
||||
from abc import ABC
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from logging import Logger
|
||||
from typing import Optional, Union
|
||||
|
||||
@@ -66,7 +66,10 @@ def size_to_byte(size: Union[float, int, str], unit: str, logger: Optional[Logge
|
||||
return final_size * (base ** 5)
|
||||
|
||||
|
||||
def datetime_as_milis(date: datetime = datetime.utcnow()) -> int:
|
||||
def datetime_as_milis(date: Optional[datetime] = None) -> int:
|
||||
if date is None:
|
||||
date = datetime.now(timezone.utc)
|
||||
|
||||
return int(round(date.timestamp() * 1000))
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Applications (bauh)
|
||||
Name=Applications (Bearhub)
|
||||
Name[pt]=Aplicativos (bauh)
|
||||
Name[es]=Aplicaciones (bauh)
|
||||
Name[ca]=Aplicacions (bauh)
|
||||
@@ -17,5 +17,5 @@ Comment[de]=Anwendungen installieren und entfernen (AppImage, Arch, Flatpak, Sna
|
||||
Comment[ca]=Instal·lar i eliminar aplicacions (AppImage, Arch, Flatpak, Snap, Web)
|
||||
Comment[ru]=Установка и удаление приложений (AppImage, Arch, Flatpak, Snap, Web)
|
||||
Comment[tr]=Uygulama yükle/kaldır (AppImage, Arch, Flatpak, Snap, Web)
|
||||
Exec=/usr/bin/bauh
|
||||
Exec=/usr/bin/bearhub
|
||||
Icon=bauh
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Applications (bauh-tray)
|
||||
Name=Applications (Bearhub tray)
|
||||
Name[pt]=Aplicativos (bauh-bandeja)
|
||||
Name[es]=Aplicaciones (bauh-bandeja)
|
||||
Name[ca]=Aplicacions (bauh-safata)
|
||||
@@ -17,5 +17,5 @@ Comment[de]=Anwendungen installieren und entfernen (AppImage, Arch, Flatpak, Sna
|
||||
Comment[ca]=Instal·lar i eliminar aplicacions (AppImage, Arch, Flatpak, Snap, Web)
|
||||
Comment[ru]=Установка и удаление приложений (AppImage, Arch, Flatpak, Snap, Web)
|
||||
Comment[tr]=Uygulama yükle/kaldır (AppImage, Arch, Flatpak, Snap, Web)
|
||||
Exec=/usr/bin/bauh-tray
|
||||
Exec=/usr/bin/bearhub-tray
|
||||
Icon=bauh
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
import tarfile
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
from typing import Optional, Generator
|
||||
@@ -73,13 +73,13 @@ class DatabaseUpdater(Thread):
|
||||
dbs_ts_str = f.read()
|
||||
|
||||
try:
|
||||
dbs_timestamp = datetime.fromtimestamp(float(dbs_ts_str))
|
||||
dbs_timestamp = datetime.fromtimestamp(float(dbs_ts_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self.logger.error('Could not parse the databases timestamp: {}'.format(dbs_ts_str))
|
||||
traceback.print_exc()
|
||||
return True
|
||||
|
||||
update = dbs_timestamp + timedelta(minutes=db_exp) <= datetime.utcnow()
|
||||
update = dbs_timestamp + timedelta(minutes=db_exp) <= datetime.now(timezone.utc)
|
||||
self.logger.info('Finished. Took {0:.2f} seconds'.format(time.time() - ti))
|
||||
return update
|
||||
|
||||
@@ -93,7 +93,7 @@ class DatabaseUpdater(Thread):
|
||||
self._update_task_progress(10, self.i18n['appimage.update_database.downloading'])
|
||||
self.logger.info('Retrieving AppImage databases')
|
||||
|
||||
database_timestamp = datetime.utcnow().timestamp()
|
||||
database_timestamp = datetime.now(timezone.utc).timestamp()
|
||||
try:
|
||||
res = self.http_client.get(URL_COMPRESSED_DATABASES, session=False)
|
||||
except Exception as e:
|
||||
@@ -356,13 +356,13 @@ class AppImageSuggestionsDownloader(Thread):
|
||||
timestamp_str = f.read()
|
||||
|
||||
try:
|
||||
suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self.logger.error(f'Could not parse the cached AppImage suggestions timestamp: {timestamp_str}')
|
||||
traceback.print_exc()
|
||||
return True
|
||||
|
||||
update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.utcnow()
|
||||
update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.now(timezone.utc)
|
||||
return update
|
||||
|
||||
def read(self) -> Generator[str, None, None]:
|
||||
@@ -372,7 +372,7 @@ class AppImageSuggestionsDownloader(Thread):
|
||||
|
||||
self.logger.info("Checking if AppImage suggestions should be downloaded")
|
||||
if self.should_download(self.config):
|
||||
suggestions_timestamp = datetime.utcnow().timestamp()
|
||||
suggestions_timestamp = datetime.now(timezone.utc).timestamp()
|
||||
suggestions_str = self.download()
|
||||
|
||||
Thread(target=self.cache_suggestions, args=(suggestions_str, suggestions_timestamp), daemon=True).start()
|
||||
@@ -466,7 +466,7 @@ class AppImageSuggestionsDownloader(Thread):
|
||||
|
||||
try:
|
||||
if should_download:
|
||||
suggestions_timestamp = datetime.utcnow().timestamp()
|
||||
suggestions_timestamp = datetime.now(timezone.utc).timestamp()
|
||||
suggestions_str = self.download()
|
||||
self.taskman.update_progress(self.task_id, 70, None)
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ def read_repository_from_info(name: str) -> Optional[str]:
|
||||
|
||||
repository = None
|
||||
|
||||
for o in new_subprocess(['grep', '-Po', "Repository\s+:\s+\K.+"], stdin=info.stdout).stdout:
|
||||
for o in new_subprocess(['grep', '-Po', r"Repository\s+:\s+\K.+"], stdin=info.stdout).stdout:
|
||||
if o:
|
||||
line = o.decode().strip()
|
||||
|
||||
@@ -343,7 +343,7 @@ def read_provides(name: str) -> Set[str]:
|
||||
|
||||
provides = None
|
||||
|
||||
for out in new_subprocess(['grep', '-Po', 'Provides\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
|
||||
for out in new_subprocess(['grep', '-Po', r'Provides\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
|
||||
if out:
|
||||
provided_names = [p.strip() for p in out.decode().strip().split(' ') if p]
|
||||
|
||||
@@ -372,7 +372,7 @@ def read_dependencies(name: str) -> Set[str]:
|
||||
raise PackageNotFoundException(name)
|
||||
|
||||
depends_on = set()
|
||||
for out in new_subprocess(['grep', '-Po', 'Depends\s+On\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
|
||||
for out in new_subprocess(['grep', '-Po', r'Depends\s+On\s+:\s\K(.+)'], stdin=dep_info.stdout).stdout:
|
||||
if out:
|
||||
line = out.decode().strip()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
@@ -90,13 +90,13 @@ class RepositorySuggestionsDownloader(Thread):
|
||||
timestamp_str = f.read()
|
||||
|
||||
try:
|
||||
suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self._log.error(f'Could not parse the Arch cached suggestions timestamp: {timestamp_str}')
|
||||
traceback.print_exc()
|
||||
return True
|
||||
|
||||
update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.utcnow()
|
||||
update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.now(timezone.utc)
|
||||
|
||||
if update:
|
||||
self._log.info("The cached suggestions file is no longer valid")
|
||||
@@ -166,7 +166,7 @@ class RepositorySuggestionsDownloader(Thread):
|
||||
suggestions = parse(res.text, self._log, 'Arch')
|
||||
|
||||
if suggestions:
|
||||
self._save(text=res.text, timestamp=datetime.utcnow().timestamp())
|
||||
self._save(text=res.text, timestamp=datetime.now(timezone.utc).timestamp())
|
||||
else:
|
||||
self._log.warning(f"Could not parse any Arch suggestion from {self._file_suggestions_ts}")
|
||||
else:
|
||||
|
||||
@@ -6,7 +6,7 @@ import re
|
||||
import shutil
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
from typing import Optional
|
||||
@@ -71,8 +71,8 @@ class AURIndexUpdater(Thread):
|
||||
timestamp_str = f.read()
|
||||
|
||||
try:
|
||||
index_timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
return (index_timestamp + timedelta(hours=exp_hours)) <= datetime.utcnow()
|
||||
index_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
return (index_timestamp + timedelta(hours=exp_hours)) <= datetime.now(timezone.utc)
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
return True
|
||||
@@ -81,7 +81,7 @@ class AURIndexUpdater(Thread):
|
||||
self.logger.info('Indexing AUR packages')
|
||||
self.taskman.update_progress(self.task_id, 5, self.i18n['arch.task.aur.index.substatus.download'])
|
||||
try:
|
||||
index_ts = datetime.utcnow().timestamp()
|
||||
index_ts = datetime.now(timezone.utc).timestamp()
|
||||
res = self.http_client.get(URL_INDEX)
|
||||
|
||||
if res and res.text:
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
import re
|
||||
import traceback
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from json import JSONDecodeError
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
@@ -52,14 +52,14 @@ class ApplicationIndexer:
|
||||
return True
|
||||
|
||||
try:
|
||||
timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self._log.error(f'Could not parse the Debian applications index timestamp: {timestamp_str} '
|
||||
f'({self._file_timestamp_path})')
|
||||
traceback.print_exc()
|
||||
return True
|
||||
|
||||
expired = timestamp + timedelta(minutes=exp_minutes) <= datetime.utcnow()
|
||||
expired = timestamp + timedelta(minutes=exp_minutes) <= datetime.now(timezone.utc)
|
||||
|
||||
if expired:
|
||||
self._log.info("Debian applications index has expired. A new one must be generated.")
|
||||
@@ -122,7 +122,7 @@ class ApplicationIndexer:
|
||||
raise ApplicationIndexError()
|
||||
|
||||
if update_timestamp:
|
||||
index_timestamp = datetime.utcnow().timestamp()
|
||||
index_timestamp = datetime.now(timezone.utc).timestamp()
|
||||
try:
|
||||
with open(self._file_timestamp_path, 'w+') as f:
|
||||
f.write(str(index_timestamp))
|
||||
@@ -140,7 +140,7 @@ class ApplicationsMapper:
|
||||
def __init__(self, logger: Logger, workers: int = 10):
|
||||
self._log = logger
|
||||
self._re_desktop_file = re.compile(r'(.+):\s+(/usr/share/applications/.+\.desktop)')
|
||||
self._re_desktop_file_fields = re.compile('(Exec|TryExec|Icon|Categories|NoDisplay|Terminal)\s*=\s*(.+)')
|
||||
self._re_desktop_file_fields = re.compile(r'(Exec|TryExec|Icon|Categories|NoDisplay|Terminal)\s*=\s*(.+)')
|
||||
self._workers = workers
|
||||
|
||||
def _read_file(self, file_path: str) -> Optional[str]:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
@@ -95,13 +95,13 @@ class DebianSuggestionsDownloader(Thread):
|
||||
timestamp_str = f.read()
|
||||
|
||||
try:
|
||||
suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
suggestions_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self._log.error(f'Could not parse the Debian cached suggestions timestamp: {timestamp_str}')
|
||||
traceback.print_exc()
|
||||
return True
|
||||
|
||||
update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.utcnow()
|
||||
update = suggestions_timestamp + timedelta(hours=exp_hours) <= datetime.now(timezone.utc)
|
||||
return update
|
||||
|
||||
def _save(self, text: str, timestamp: float):
|
||||
@@ -184,7 +184,7 @@ class DebianSuggestionsDownloader(Thread):
|
||||
suggestions = parse(res.text, self._log, 'Debian')
|
||||
|
||||
if suggestions:
|
||||
self._save(text=res.text, timestamp=datetime.utcnow().timestamp())
|
||||
self._save(text=res.text, timestamp=datetime.now(timezone.utc).timestamp())
|
||||
else:
|
||||
self._log.warning("No Debian suggestions to cache")
|
||||
else:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from logging import Logger
|
||||
from threading import Thread
|
||||
from typing import Optional, Set
|
||||
@@ -149,14 +149,14 @@ class SynchronizePackages(Thread):
|
||||
return True
|
||||
|
||||
try:
|
||||
last_timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
last_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
logger.error(f'Could not parse the packages synchronization timestamp: {timestamp_str} '
|
||||
f'({PACKAGE_SYNC_TIMESTAMP_FILE})')
|
||||
traceback.print_exc()
|
||||
return True
|
||||
|
||||
expired = last_timestamp + timedelta(minutes=period) <= datetime.utcnow()
|
||||
expired = last_timestamp + timedelta(minutes=period) <= datetime.now(timezone.utc)
|
||||
|
||||
if expired:
|
||||
logger.info("Packages synchronization is outdated")
|
||||
@@ -176,7 +176,7 @@ class SynchronizePackages(Thread):
|
||||
self._taskman.update_progress(self._id, 99, None)
|
||||
|
||||
if updated:
|
||||
index_timestamp = datetime.utcnow().timestamp()
|
||||
index_timestamp = datetime.now(timezone.utc).timestamp()
|
||||
finish_msg = None
|
||||
try:
|
||||
with open(PACKAGE_SYNC_TIMESTAMP_FILE, 'w+') as f:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from operator import attrgetter
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
@@ -60,7 +60,7 @@ class FlatpakManager(SoftwareManager, SettingsController):
|
||||
if app.runtime and app.latest_version is None:
|
||||
app.latest_version = app.version
|
||||
|
||||
expired_data = api_data and api_data.get('expires_at') and api_data['expires_at'] <= datetime.utcnow()
|
||||
expired_data = api_data and api_data.get('expires_at') and api_data['expires_at'] <= datetime.now(timezone.utc)
|
||||
|
||||
data_loader: Optional[FlatpakAsyncDataLoader] = None
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ from bauh.gems.flatpak.constants import FLATHUB_URL
|
||||
|
||||
RE_SEVERAL_SPACES = re.compile(r'\s+')
|
||||
RE_COMMIT = re.compile(r'(Latest commit|Commit)\s*:\s*(.+)')
|
||||
RE_REQUIRED_RUNTIME = re.compile(f'Required\s+runtime\s+.+\(([\w./]+)\)\s*.+\s+remote\s+([\w+./]+)')
|
||||
RE_REQUIRED_RUNTIME = re.compile(r'Required\s+runtime\s+.+\(([\w./]+)\)\s*.+\s+remote\s+([\w+./]+)')
|
||||
OPERATION_UPDATE_SYMBOLS = {'i', 'u'}
|
||||
|
||||
|
||||
@@ -56,12 +56,20 @@ def get_fields(app_id: str, branch: str, fields: List[str]) -> List[str]:
|
||||
if branch:
|
||||
cmd.append(branch)
|
||||
|
||||
info = new_subprocess(cmd).stdout
|
||||
info = run_cmd(' '.join(cmd), print_error=False)
|
||||
|
||||
if not info:
|
||||
return []
|
||||
|
||||
res = []
|
||||
for o in new_subprocess(('grep', '-E', '({}):.+'.format('|'.join(fields)), '-o'), stdin=info).stdout:
|
||||
if o:
|
||||
res.append(o.decode().split(':')[-1].strip())
|
||||
for line in info.splitlines():
|
||||
if not line or ':' not in line:
|
||||
continue
|
||||
|
||||
field_name, field_value = line.split(':', 1)
|
||||
|
||||
if field_name.strip() in fields:
|
||||
res.append(field_value.strip())
|
||||
|
||||
return res
|
||||
|
||||
@@ -97,11 +105,14 @@ def list_installed(version: Tuple[str, ...]) -> List[dict]:
|
||||
apps = []
|
||||
|
||||
if version < VERSION_1_2:
|
||||
app_list = new_subprocess(('flatpak', 'list', '-d'), lang=None)
|
||||
app_list = run_cmd('flatpak list -d', print_error=False, lang=None)
|
||||
|
||||
for o in app_list.stdout:
|
||||
if o:
|
||||
data = o.decode().strip().split('\t')
|
||||
if not app_list:
|
||||
return apps
|
||||
|
||||
for line in app_list.splitlines():
|
||||
if line:
|
||||
data = line.strip().split('\t')
|
||||
ref_split = data[0].split('/')
|
||||
runtime = 'runtime' in data[5]
|
||||
|
||||
@@ -121,11 +132,14 @@ def list_installed(version: Tuple[str, ...]) -> List[dict]:
|
||||
else:
|
||||
name_col = '' if version < VERSION_1_3 else 'name,'
|
||||
cols = f'application,ref,arch,branch,description,origin,options,{name_col}version'
|
||||
app_list = new_subprocess(('flatpak', 'list', f'--columns={cols}'), lang=None)
|
||||
app_list = run_cmd(f'flatpak list --columns={cols}', print_error=False, lang=None)
|
||||
|
||||
for o in app_list.stdout:
|
||||
if o:
|
||||
data = o.decode().strip().split('\t')
|
||||
if not app_list:
|
||||
return apps
|
||||
|
||||
for line in app_list.splitlines():
|
||||
if line:
|
||||
data = line.strip().split('\t')
|
||||
runtime = 'runtime' in data[6]
|
||||
|
||||
if version < VERSION_1_3:
|
||||
@@ -237,14 +251,17 @@ def fill_updates(version: Tuple[str, ...], installation: str, res: Dict[str, Set
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
else:
|
||||
updates = new_subprocess(('flatpak', 'update', f'--{installation}', '--no-deps')).stdout
|
||||
|
||||
reg = r'[0-9]+\.\s+.+'
|
||||
|
||||
try:
|
||||
for o in new_subprocess(('grep', '-E', reg, '-o', '--color=never'), stdin=updates).stdout:
|
||||
if o:
|
||||
line_split = o.decode().strip().split('\t')
|
||||
output = run_cmd(f'flatpak update --{installation} --no-deps', ignore_return_code=True, print_error=False)
|
||||
|
||||
if not output:
|
||||
return
|
||||
|
||||
for line in output.splitlines():
|
||||
if re.match(reg, line):
|
||||
line_split = line.strip().split('\t')
|
||||
|
||||
if len(line_split) >= 5:
|
||||
if version >= VERSION_1_5:
|
||||
|
||||
@@ -81,7 +81,7 @@ class WebApplicationManager(SoftwareManager, SettingsController):
|
||||
|
||||
def get_accept_language_header(self) -> str:
|
||||
try:
|
||||
syslocale = locale.getdefaultlocale()
|
||||
syslocale = locale.getlocale()
|
||||
|
||||
if syslocale:
|
||||
locale_split = syslocale[0].split('_')
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
import shutil
|
||||
import tarfile
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
from typing import Dict, List, Optional
|
||||
@@ -245,12 +245,12 @@ class EnvironmentUpdater:
|
||||
env_ts_str = f.read()
|
||||
|
||||
try:
|
||||
env_timestamp = datetime.fromtimestamp(float(env_ts_str))
|
||||
env_timestamp = datetime.fromtimestamp(float(env_ts_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self.logger.error(f"Could not parse environment settings file timestamp: {env_ts_str}")
|
||||
return True
|
||||
|
||||
expired = env_timestamp + timedelta(hours=settings_exp) <= datetime.utcnow()
|
||||
expired = env_timestamp + timedelta(hours=settings_exp) <= datetime.now(timezone.utc)
|
||||
|
||||
if expired:
|
||||
self.logger.info("Environment settings file has expired. It should be re-downloaded")
|
||||
@@ -314,7 +314,7 @@ class EnvironmentUpdater:
|
||||
self._finish_task_download_settings()
|
||||
return
|
||||
|
||||
cache_timestamp = datetime.utcnow().timestamp()
|
||||
cache_timestamp = datetime.now(timezone.utc).timestamp()
|
||||
with open(ENVIRONMENT_SETTINGS_CACHED_FILE, 'w+') as f:
|
||||
f.write(yaml.safe_dump(settings))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
import traceback
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from logging import Logger
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
@@ -68,12 +68,12 @@ class SuggestionsManager:
|
||||
timestamp_str = f.read()
|
||||
|
||||
try:
|
||||
sugs_timestamp = datetime.fromtimestamp(float(timestamp_str))
|
||||
sugs_timestamp = datetime.fromtimestamp(float(timestamp_str), tz=timezone.utc)
|
||||
except Exception:
|
||||
self.logger.error(f"Could not parse the cached suggestions file timestamp: {timestamp_str}")
|
||||
return True
|
||||
|
||||
expired = sugs_timestamp + timedelta(days=exp) <= datetime.utcnow()
|
||||
expired = sugs_timestamp + timedelta(days=exp) <= datetime.now(timezone.utc)
|
||||
|
||||
if expired:
|
||||
self.logger.info("Cached suggestions file has expired.")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import logging
|
||||
import time
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from threading import Thread
|
||||
from typing import Optional
|
||||
|
||||
@@ -53,7 +53,7 @@ class SuggestionsLoader(Thread):
|
||||
self.suggestions = self.manager.read_cached(check_file=False)
|
||||
else:
|
||||
try:
|
||||
timestamp = datetime.utcnow().timestamp()
|
||||
timestamp = datetime.now(timezone.utc).timestamp()
|
||||
self.suggestions = self.manager.download()
|
||||
|
||||
if self.suggestions:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import inspect
|
||||
import importlib
|
||||
import os
|
||||
import pkgutil
|
||||
from logging import Logger
|
||||
from typing import List, Generator
|
||||
|
||||
@@ -49,11 +49,14 @@ def load_managers(locale: str, context: ApplicationContext, config: dict, defaul
|
||||
logger.warning(f"gem '{f.name}' could not be loaded because it was marked as forbidden in '{FORBIDDEN_GEMS_FILE}'")
|
||||
continue
|
||||
|
||||
loader = pkgutil.find_loader(f'bauh.gems.{f.name}.controller')
|
||||
module_name = f'bauh.gems.{f.name}.controller'
|
||||
|
||||
if loader:
|
||||
module = loader.load_module()
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
except ModuleNotFoundError:
|
||||
module = None
|
||||
|
||||
if module:
|
||||
manager_class = find_manager(module)
|
||||
|
||||
if manager_class:
|
||||
|
||||
@@ -27,7 +27,7 @@ class DefaultMemoryCache(MemoryCache):
|
||||
|
||||
def _add(self, key: str, val: object):
|
||||
if key:
|
||||
self._cache[key] = {'val': val, 'expires_at': datetime.datetime.utcnow() + datetime.timedelta(seconds=self.expiration_time) if self.expiration_time > 0 else None}
|
||||
self._cache[key] = {'val': val, 'expires_at': datetime.datetime.now(UTC) + datetime.timedelta(seconds=self.expiration_time) if self.expiration_time > 0 else None}
|
||||
|
||||
def add_non_existing(self, key: str, val: object):
|
||||
if key and self. is_enabled():
|
||||
@@ -46,7 +46,7 @@ class DefaultMemoryCache(MemoryCache):
|
||||
if val:
|
||||
expiration = val.get('expires_at')
|
||||
|
||||
if expiration and expiration <= datetime.datetime.utcnow():
|
||||
if expiration and expiration <= datetime.datetime.now(UTC):
|
||||
if lock:
|
||||
self.lock.acquire()
|
||||
|
||||
@@ -86,3 +86,4 @@ class DefaultMemoryCacheFactory(MemoryCacheFactory):
|
||||
|
||||
def new(self, expiration: Optional[int] = None) -> MemoryCache:
|
||||
return DefaultMemoryCache(expiration if expiration is not None else self.expiration_time)
|
||||
UTC = datetime.timezone.utc
|
||||
|
||||
@@ -50,7 +50,7 @@ def get_locale_keys(key: str = None, locale_dir: str = resource.get_path('locale
|
||||
|
||||
if key is None:
|
||||
try:
|
||||
current_locale = locale.getdefaultlocale()
|
||||
current_locale = locale.getlocale()
|
||||
|
||||
if current_locale is None or current_locale[0] is None:
|
||||
current_locale = ('en', 'UTF-8')
|
||||
|
||||
@@ -3,13 +3,16 @@ requires = ["setuptools", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "bauh"
|
||||
description = "Graphical interface to manage Linux applications (AppImage, Arch / AUR, Flatpak, Snap and Web)"
|
||||
name = "bearhub"
|
||||
description = "Community-maintained fork of bauh focused on modern Linux compatibility"
|
||||
license = {file = "LICENSE"}
|
||||
requires-python = ">=3.6"
|
||||
dynamic = ["version"]
|
||||
readme = "README.md"
|
||||
authors = [{name = "Vinicius Moreira", email = "vinicius_fmoreira@hotmail.com"}]
|
||||
authors = [
|
||||
{name = "Vinicius Moreira", email = "vinicius_fmoreira@hotmail.com"},
|
||||
{name = "Bearhub maintainers"}
|
||||
]
|
||||
classifiers = [
|
||||
'Topic :: Utilities',
|
||||
'Programming Language :: Python',
|
||||
@@ -36,12 +39,12 @@ web = [
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
bauh = "bauh.app:main"
|
||||
bauh-tray = "bauh.app:tray"
|
||||
bauh-cli = "bauh.cli.app:main"
|
||||
bearhub = "bauh.app:main"
|
||||
bearhub-tray = "bauh.app:tray"
|
||||
bearhub-cli = "bauh.cli.app:main"
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/vinifmor/bauh"
|
||||
Repository = "https://github.com/spalencsar/bearhub"
|
||||
|
||||
[tool.setuptools]
|
||||
license-files = ["LICENSE"]
|
||||
|
||||
19
setup.py
19
setup.py
@@ -4,13 +4,14 @@ import os
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
DESCRIPTION = (
|
||||
"Graphical interface to manage Linux applications (AppImage, Arch / AUR, Flatpak, Snap and Web)"
|
||||
"Community-maintained fork of bauh focused on modern Linux compatibility"
|
||||
)
|
||||
|
||||
AUTHOR = "Vinicius Moreira"
|
||||
AUTHOR_EMAIL = "vinicius_fmoreira@hotmail.com"
|
||||
NAME = 'bauh'
|
||||
URL = "https://github.com/vinifmor/" + NAME
|
||||
DIST_NAME = 'bearhub'
|
||||
APP_PACKAGE = 'bauh'
|
||||
URL = "https://github.com/spalencsar/" + DIST_NAME
|
||||
|
||||
file_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
@@ -21,12 +22,12 @@ else:
|
||||
requirements = [line.strip() for line in f.readlines() if line]
|
||||
|
||||
|
||||
with open(file_dir + '/{}/__init__.py'.format(NAME), 'r') as f:
|
||||
with open(file_dir + '/{}/__init__.py'.format(APP_PACKAGE), 'r') as f:
|
||||
exec(f.readlines()[0])
|
||||
|
||||
|
||||
setup(
|
||||
name=NAME,
|
||||
name=DIST_NAME,
|
||||
version=eval('__version__'),
|
||||
description=DESCRIPTION,
|
||||
long_description=DESCRIPTION,
|
||||
@@ -35,14 +36,14 @@ setup(
|
||||
python_requires=">=3.5",
|
||||
url=URL,
|
||||
packages=find_packages(exclude=["tests.*", "tests"]),
|
||||
package_data={NAME: ["view/resources/locale/*", "view/resources/img/*", "view/resources/style/*", 'view/resources/style/*/img/*', "gems/*/resources/img/*", "gems/*/resources/locale/*", "desktop/*"]},
|
||||
package_data={APP_PACKAGE: ["view/resources/locale/*", "view/resources/img/*", "view/resources/style/*", 'view/resources/style/*/img/*', "gems/*/resources/img/*", "gems/*/resources/locale/*", "desktop/*"]},
|
||||
install_requires=requirements,
|
||||
test_suite="tests",
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"{name}={name}.app:main".format(name=NAME),
|
||||
"{name}-tray={name}.app:tray".format(name=NAME),
|
||||
"{name}-cli={name}.cli.app:main".format(name=NAME)
|
||||
"bearhub=bauh.app:main",
|
||||
"bearhub-tray=bauh.app:tray",
|
||||
"bearhub-cli=bauh.cli.app:main"
|
||||
]
|
||||
},
|
||||
include_package_data=True,
|
||||
|
||||
Reference in New Issue
Block a user