AUR bearhub: fix stale source cache collision (pkgrel=10)

Use tag-specific tarball name bearhub-0.10.7-bearhub.6.tar.gz so makepkg
and AUR helpers do not reuse an outdated bearhub-0.10.7.tar.gz from cache.
Ignore makepkg artifacts under packaging/aur/.
This commit is contained in:
Sebastian Palencsar
2026-06-27 10:02:45 +02:00
parent c56b8a2473
commit da377aecdc
198 changed files with 4543 additions and 4644 deletions

View File

@@ -1,89 +0,0 @@
import datetime
from threading import Lock
from typing import Optional
from bauh.api.abstract.cache import MemoryCache, MemoryCacheFactory
class DefaultMemoryCache(MemoryCache):
"""
A synchronized cache implementation
"""
def __init__(self, expiration_time: int):
super(DefaultMemoryCache, self).__init__()
self.expiration_time = expiration_time
self._cache = {}
self.lock = Lock()
def is_enabled(self):
return self.expiration_time < 0 or self.expiration_time > 0
def add(self, key: str, val: object):
if key and self.is_enabled():
self.lock.acquire()
self._add(key, val)
self.lock.release()
def _add(self, key: str, val: object):
if key:
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():
self.lock.acquire()
cur_val = self.get(key, lock=False)
if cur_val is None:
self._add(key, val)
self.lock.release()
def get(self, key: str, lock: bool = True):
if key and self.is_enabled():
val = self._cache.get(key)
if val:
expiration = val.get('expires_at')
if expiration and expiration <= datetime.datetime.now(UTC):
if lock:
self.lock.acquire()
del self._cache[key]
if lock:
self.lock.release()
return None
return val['val']
def delete(self, key):
if key and self.is_enabled():
if key in self._cache:
self.lock.acquire()
del self._cache[key]
self.lock.release()
def keys(self):
return set(self._cache.keys()) if self.is_enabled() else set()
def clean_expired(self):
if self.is_enabled():
for key in self.keys():
self.get(key)
class DefaultMemoryCacheFactory(MemoryCacheFactory):
def __init__(self, expiration_time: int):
"""
:param expiration_time: default expiration time for all instantiated caches
"""
super(DefaultMemoryCacheFactory, self).__init__()
self.expiration_time = expiration_time
def new(self, expiration: Optional[int] = None) -> MemoryCache:
return DefaultMemoryCache(expiration if expiration is not None else self.expiration_time)
UTC = datetime.timezone.utc

View File

@@ -1,115 +0,0 @@
import json
import logging
import os
import time
from threading import Thread, Lock
from typing import Type, Dict, Any, Optional
import yaml
from bauh.api.abstract.cache import MemoryCache
from bauh.api.abstract.disk import DiskCacheLoader, DiskCacheLoaderFactory
from bauh.api.abstract.model import SoftwarePackage
class AsyncDiskCacheLoader(Thread, DiskCacheLoader):
def __init__(self, cache_map: Dict[Type[SoftwarePackage], MemoryCache], logger: logging.Logger):
super(AsyncDiskCacheLoader, self).__init__(daemon=True)
self.pkgs = []
self._work = True
self.lock = Lock()
self.cache_map = cache_map
self.logger = logger
self.processed = 0
self._working = False
def fill(self, pkg: SoftwarePackage, sync: bool = False):
"""
Adds a package which data must be read from the disk to a queue (if not sync)
:param pkg:
:param sync:
:return:
"""
if pkg and pkg.supports_disk_cache():
if sync or not self._working:
self._fill_cached_data(pkg)
else:
self.pkgs.append(pkg)
def read(self, pkg: SoftwarePackage) -> Optional[Dict[str, Any]]:
if pkg and pkg.supports_disk_cache():
data_path = pkg.get_disk_data_path()
if data_path and os.path.isfile(data_path):
ext = data_path.split('.')[-1]
try:
with open(data_path) as f:
file_content = f.read()
except FileNotFoundError:
return
if file_content:
if ext == 'json':
cached_data = json.loads(file_content)
elif ext in {'yml', 'yaml'}:
cached_data = yaml.load(file_content)
else:
raise Exception(f'The cached data file {data_path} has an unsupported format')
if cached_data:
return cached_data
else:
self.logger.warning(f"No cached content in file {data_path}")
def stop_working(self):
self._work = False
def run(self):
self._working = True
last = 0
while True:
time.sleep(0.00001)
if len(self.pkgs) > self.processed:
pkg = self.pkgs[last]
self._fill_cached_data(pkg)
self.processed += 1
last += 1
elif not self._work:
break
self._working = False
def _fill_cached_data(self, pkg: SoftwarePackage) -> bool:
cached_data = self.read(pkg)
if cached_data:
pkg.fill_cached_data(cached_data)
cache = self.cache_map.get(pkg.__class__)
if cache:
cache.add_non_existing(str(pkg.id), cached_data)
return True
return False
class DefaultDiskCacheLoaderFactory(DiskCacheLoaderFactory):
def __init__(self, logger: logging.Logger):
super(DefaultDiskCacheLoaderFactory, self).__init__()
self.logger = logger
self.cache_map = {}
def map(self, pkg_type: Type[SoftwarePackage], cache: MemoryCache):
if pkg_type:
if pkg_type not in self.cache_map:
self.cache_map[pkg_type] = cache
def new(self) -> AsyncDiskCacheLoader:
return AsyncDiskCacheLoader(cache_map=self.cache_map, logger=self.logger)

View File

@@ -1,22 +0,0 @@
import logging
from logging import INFO
FORMAT = '%(asctime)s %(levelname)s [%(module_path)s:%(lineno)s - %(funcName)s()] - %(message)s'
class FilePathFilter(logging.Filter):
def filter(self, record):
record.module_path = record.pathname.split('site-packages/')[1] if 'site-packages' in record.pathname else str(record.pathname)
return True
def new_logger(name: str, enabled: bool) -> logging.Logger:
instance = logging.Logger(name, level=INFO)
instance.addFilter(FilePathFilter())
stream_handler = logging.StreamHandler()
stream_handler.setFormatter(logging.Formatter(FORMAT))
instance.addHandler(stream_handler)
instance.disabled = not enabled
return instance

View File

@@ -1,5 +0,0 @@
from bauh import ROOT_DIR
def get_path(resource_path):
return ROOT_DIR + '/view/resources/' + resource_path

View File

@@ -1,89 +0,0 @@
import glob
import locale
import os
from typing import Tuple, Set
from bauh.view.util import resource
class I18n(dict):
def __init__(self, current_key: str, current_locale: dict, default_key: str, default_locale: dict):
super(I18n, self).__init__()
self.current_key = current_key
self.current = current_locale
self.default_key = default_key
self.default = default_locale
def __getitem__(self, item):
try:
return self.current.__getitem__(item)
except KeyError:
if self.default:
try:
return self.default.__getitem__(item)
except KeyError:
return item
else:
return item
def get(self, *args, **kwargs):
res = self.current.get(args[0])
if res is None:
if self.default:
return self.default.get(*args, **kwargs)
else:
return self.current.get(*args, **kwargs)
return res
def get_available_keys() -> Set[str]:
locale_dir = resource.get_path('locale')
return {file.split('/')[-1] for file in glob.glob(locale_dir + '/*') if os.path.isfile(file)}
def get_locale_keys(key: str = None, locale_dir: str = resource.get_path('locale')) -> Tuple[str, dict]:
locale_path = None
if key is None:
try:
current_locale = locale.getlocale()
if current_locale is None or current_locale[0] is None:
current_locale = ('en', 'UTF-8')
except Exception:
current_locale = ('en', 'UTF-8')
else:
current_locale = [key.strip().lower()]
if current_locale:
current_locale = current_locale[0]
for locale_file in glob.glob(locale_dir + '/*'):
name = locale_file.split('/')[-1]
if current_locale == name or current_locale.startswith(name + '_'):
locale_path = locale_file
break
if not locale_path:
return current_locale if current_locale else key, {}
with open(locale_path, 'r') as f:
locale_keys = f.readlines()
locale_obj = {}
for line in locale_keys:
line_strip = line.strip()
if line_strip:
try:
keyval = line_strip.split('=')
locale_obj[keyval[0].strip()] = keyval[1].strip()
except Exception:
print("Error decoding i18n line '{}'".format(line))
return locale_path.split('/')[-1], locale_obj

View File

@@ -1,88 +0,0 @@
import os
import shutil
import subprocess
import sys
import traceback
from typing import List, Tuple
from PyQt5.QtCore import QCoreApplication
from PyQt5.QtGui import QIcon
from colorama import Fore
from bauh import __app_name__
from bauh.api.abstract.controller import SoftwareManager
from bauh.api.paths import CONFIG_DIR, CACHE_DIR, TEMP_DIR
from bauh.commons.system import run_cmd
from bauh.view.util import resource
def notify_user(msg: str, icon_path: str = None):
icon_id = icon_path
if not icon_id:
icon_id = get_default_icon()[0]
os.system("notify-send -a {} {} '{}'".format(__app_name__, "-i {}".format(icon_id) if icon_id else '', msg))
def get_default_icon(system: bool = True) -> Tuple[str, QIcon]:
if system:
system_icon = QIcon.fromTheme(__app_name__)
if not system_icon.isNull():
return system_icon.name(), system_icon
path = resource.get_path('img/logo.svg')
return path, QIcon(path)
def restart_app():
appimage_path = os.getenv('APPIMAGE')
restart_cmd = [appimage_path] if appimage_path else [sys.executable, *sys.argv]
subprocess.Popen(restart_cmd)
QCoreApplication.exit()
def get_distro():
if os.path.exists('/etc/arch-release'):
return 'arch'
if os.path.exists('/etc/os-release'):
with open('/etc/os-release', 'r') as os_release_file:
for line in os_release_file:
if 'ID_LIKE=arch' in line:
return 'arch'
if os.path.exists('/proc/version'):
if 'ubuntu' in run_cmd('cat /proc/version').lower():
return 'ubuntu'
return 'unknown'
def clean_app_files(managers: List[SoftwareManager], logs: bool = True):
if logs:
print('[{}] Cleaning configuration and cache files'.format(__app_name__))
for path in (CACHE_DIR, CONFIG_DIR, TEMP_DIR):
if logs:
print('[{}] Deleting directory {}'.format(__app_name__, path))
if os.path.exists(path):
try:
shutil.rmtree(path)
if logs:
print('{}[{}] Directory {} deleted{}'.format(Fore.YELLOW, __app_name__, path, Fore.RESET))
except Exception:
if logs:
print('{}[{}] An exception has happened when deleting {}{}'.format(Fore.RED, __app_name__, path, Fore.RESET))
traceback.print_exc()
if managers:
for m in managers:
m.clear_data()
if logs:
print('[{}] Cleaning finished'.format(__app_name__))