diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 9b56fc39..50491b33 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -8,28 +8,27 @@ assignees: ''
---
**Before opening a new issue**
-Install the **staging** branch and check if the unexpected behavior is happening there as well.
-If you are on ArchLinux-based distro, you can install it through AUR (**bauh-staging**). Otherwise, you have
-to clone the repository and install it with pip:
+Install the latest `bearhub-git` package from AUR and check if the unexpected behavior is happening there as well.
+Otherwise, install from source:
```
-git clone https://github.com/vinifmor/bauh.git -b staging --depth=1
-cd bauh
+git clone https://github.com/spalencsar/bearhub.git --depth=1
+cd bearhub
python3 -m venv venv
venv/bin/pip install pip --upgrade
venv/bin/pip install setuptools --upgrade
venv/bin/pip install -r requirements.txt
venv/bin/pip install .
-venv/bin/bauh # or venv/bin/bauh-tray
+venv/bin/bearhub # or venv/bin/bearhub-tray
```
**Describe the bug**
A clear and concise description of what the bug is.
**Software Environment**
-bauh version:
+Bearhub version:
O.S: name and version
Python version:
Installation method: pip | distro package manager (e.g: pacman)
-P.S: these instructions and the template must be respected, otherwise your issue will be closed.
+P.S: these instructions and the template must be respected, otherwise your issue will be closed.
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/suggest-a-new-appimage-application-to-the-database.md b/.github/ISSUE_TEMPLATE/suggest-a-new-appimage-application-to-the-database.md
index 3b7afe03..3128af5e 100644
--- a/.github/ISSUE_TEMPLATE/suggest-a-new-appimage-application-to-the-database.md
+++ b/.github/ISSUE_TEMPLATE/suggest-a-new-appimage-application-to-the-database.md
@@ -1,6 +1,6 @@
---
name: Suggest a new AppImage application to the database
-about: At the moment bauh's backend is only able to work with GitHub/GitLab projects. The project also needs to attach the AppImage file to its releases.
+about: At the moment Bearhub's AppImage backend is only able to work with GitHub/GitLab projects. The project also needs to attach the AppImage file to its releases.
title: "[New AppImage] application_name"
labels: appimage
assignees: ''
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000..4a52f5fe
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,35 @@
+name: Tests
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ workflow_dispatch:
+
+jobs:
+ unittest:
+ name: unittest (${{ matrix.python-version }})
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+ matrix:
+ python-version: ["3.11", "3.12"]
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r requirements.txt
+ pip install -e .
+
+ - name: Run unit tests
+ run: python -m unittest discover -s tests -p "test_*.py" -v
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 2c17b501..0e8f80fd 100755
--- a/.gitignore
+++ b/.gitignore
@@ -16,5 +16,6 @@ linux_dist/appimage/*.zsync
linux_dist/appimage/*.AppImage
linux_dist/appimage/*.gz
linux_dist/appimage/*.gz.*
+linux_dist/appimage/bearhub-*
linux_dist/appimage/bauh-*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index feab457d..9413ccbb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,38 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
+## [0.10.7-bearhub.6] 2026-06-26
+### Fixed
+- Arch metadata URLs no longer point to non-existent `bearhub-files` (restored `bauh-files` data sources)
+- AppImage repository URL now points to `spalencsar/bearhub` instead of `vinifmor/bearhub`
+- Unit test mocks corrected for namespace transition (`bauh.gems` paths)
+- Web locale tests updated for `locale.getlocale()` behavior
+
+### Changed
+- CLI/log output uses dynamic app name (`bearhub`) instead of hardcoded `[bauh]`
+- Tray icon theme lookup prefers `bearhub_tray_*` with legacy `bauh_tray_*` fallback
+- AppImage-installed desktop entries use `bearhub_appimage_*` prefix
+- About dialog displays `Bearhub` (capitalized)
+- AUR/makepkg.conf marker uses dynamic app name
+
+### Packaging
+- Removed legacy `bauh/desktop/*` entries from distribution
+- Desktop files and icons ship from `bearhub/desktop` and `bearhub/view/resources`
+- AppImage builder migrated to Bearhub naming (`bearhub`, `bearhub-tray`, `BEARHUB_VERSION`)
+- Added brand assets under `gfx/`
+- AUR stable release tag: `0.10.7-bearhub.6` (`pkgrel=1`)
+- AUR `bearhub-git` `pkgrel` bumped to `5`
+
+### Documentation
+- README aligned with Bearhub runtime paths, commands, and maintainer workflows
+- GitHub issue templates updated for Bearhub
+- `CONTRIBUTING.md` locale paths refreshed
+- Added `docs/qt6-migration.md` and roadmap milestone M6 (PySide6 decision)
+- Maintainer docs: AppImage build and Qt6 migration references
+
+### CI
+- Added GitHub Actions workflow for unit tests (Python 3.11 and 3.12)
+
## [0.10.7-bearhub.5] 2026-05-31
### Packaging
- Desktop integration finalized under `bearhub/desktop/*`
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 06427c78..222035b0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -23,10 +23,8 @@ The way to go here is to ask yourself if the improvement would be useful for mor
# Adding a new translation
* To add a new translation, you will have to create a file in each directory listed below named as the first two letters of the language in the ISO format (e.g: for 'english' would be 'en'):
-- `bauh/view/resources/locale`
+- `bearhub/view/resources/locale` (or `bauh/view/resources/locale` during namespace migration)
- `bauh/gems/appimage/resources/locale`
- `bauh/gems/arch/resources/locale`
- `bauh/gems/flatpak/resources/locale`
-- `bauh/gems/snap/resources/locale`
- `bauh/gems/web/resources/locale`
-- `bauh/gems/debian/resources/locale`
diff --git a/MANIFEST.in b/MANIFEST.in
index 60509bf6..3496185e 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,6 +1,5 @@
include LICENSE CHANGELOG.md README.md requirements.txt
-recursive-include bauh/desktop *
-recursive-exclude bauh/desktop *.orig
+recursive-include gfx *
recursive-include bearhub/desktop *
recursive-exclude bearhub/desktop *.orig
recursive-include bauh/view/resources *
diff --git a/README.md b/README.md
index a350f7ec..16835211 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
[](https://github.com/spalencsar/bearhub/blob/main/LICENSE)
-
+
Bearhub
@@ -10,7 +10,7 @@
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.
-Some sections still include historical `bauh` naming in technical paths or screenshots. Runtime commands and package names below follow Bearhub naming.
+Legacy `bauh` config and cache directories are migrated automatically on first start when present. Screenshots may still show the original upstream UI.
## Status
@@ -80,7 +80,7 @@ Key features
13. [bauh-files](#bauh_files)
14. [Code structure](#code)
15. [Roadmap](#roadmap)
-16. [Contributing](https://github.com/vinifmor/bauh/blob/master/CONTRIBUTING.md)
+16. [Contributing](CONTRIBUTING.md)
17. [Code of Conduct](CODE_OF_CONDUCT.md)
18. [Security Policy](SECURITY.md)
19. [Maintainer docs](#maintainer_docs)
@@ -97,11 +97,11 @@ Key features
- `qt5dxcb-plugin` (or equivalent): the package name may vary from distribution
##### Steps
-- Download the .AppImage file attached with the latest release from https://github.com/vinifmor/bauh/releases
+- Download the .AppImage file attached with the latest release from https://github.com/spalencsar/bearhub/releases (when available)
- Generally the desktop environment allows you to double-click the downloaded file to execute it. If that's not your case, follow the steps below from a console:
- - `chmod a+x bauh-${version}-x86_64.AppImage` (replace `${version}` by the respective downloaded version)
- - `./bauh-${version}-x86_64.AppImage`
-- If you want to integrate bauh to your desktop: click on bauh's settings menu ("sandwich") and then `Install bauh`
+ - `chmod a+x bearhub-${version}-x86_64.AppImage` (replace `${version}` by the respective downloaded version)
+ - `./bearhub-${version}-x86_64.AppImage`
+- If you want to integrate Bearhub to your desktop: click on Bearhub's settings menu ("sandwich") and then `Install Bearhub`
@@ -174,7 +174,7 @@ rm -rf bearhub_env
To create a shortcut for Bearhub on your desktop menu:
-- Copy desktop files from `bauh/desktop` to `~/.local/share/applications` (or `/usr/share/applications` for **root**)
+- Copy desktop files from `bearhub/desktop` to `~/.local/share/applications` (or `/usr/share/applications` for **root**)
- Ensure `Exec` points to the Bearhub binary. e.g: `Exec=/usr/bin/bearhub` (or `bearhub_env/bin/bearhub`)
- Ensure the icon `bearhub.svg` exists under `/usr/share/icons/hicolor/scalable/apps`
@@ -202,6 +202,14 @@ Bearhub is currently distributed through source releases and AUR:
#### Maintainer docs
- Namespace migration notes: `NAMESPACE_MIGRATION.md`
+- Qt6 migration plan: `docs/qt6-migration.md`
+- Brand assets: `gfx/bearhub.svg`, `gfx/bearhub.png` (synced into `bearhub/view/resources/img/logo.svg` for runtime/packaging)
+- AppImage build (from `linux_dist/appimage/`):
+
+```bash
+export BEARHUB_VERSION=0.10.7-bearhub.6 # release tag to build
+./build.sh
+```
#### Supported types
@@ -217,14 +225,14 @@ Bearhub is currently distributed through source releases and AUR:
- `Install AppImage file`: allows to install an external AppImage file
- `Upgrade file`: allows to upgrade a manually installed AppImage file
- `Update database`: manually synchronize the AppImage database
- - `Install bauh`: installs bauh if it is running as an AppImage
+ - `Install Bearhub`: installs Bearhub if it is running as an AppImage
-- Installed applications are store at `~/.local/share/bauh/appimage/installed` (or `/usr/local/share/bauh/installed` for **root**)
-- Desktop entries (menu shortcuts) of the installed applications are stored at `~/.local/share/applications` (or `/usr/share/applications` for **root**). Name pattern: `bauh_appimage_appname.desktop`
+- Installed applications are store at `~/.local/share/bearhub/appimage/installed` (or `/usr/local/share/bearhub/installed` for **root**)
+- Desktop entries (menu shortcuts) of the installed applications are stored at `~/.local/share/applications` (or `/usr/share/applications` for **root**). Name pattern: `bearhub_appimage_appname.desktop`
- Symlinks are created at `~/.local/bin` (or `/usr/local/bin` for **root**). They have the same name of the application (if the name already exists, it will be created as 'app_name-appimage'. e.g: `rpcs3-appimage`)
-- Downloaded database files are stored at `~/.cache/bauh/appimage` (or `/var/cache/bauh/appimage` for **root**) as **apps.db** and **releases.db**
+- Downloaded database files are stored at `~/.cache/bearhub/appimage` (or `/var/cache/bearhub/appimage` for **root**) as **apps.db** and **releases.db**
- Databases are updated during the initialization process if they are considered outdated
-- The configuration file is located at `~/.config/bauh/appimage.yml` (or `/etc/bauh/appimage.yml` for **root**) and it allows the following customizations:
+- The configuration file is located at `~/.config/bearhub/appimage.yml` (or `/etc/bearhub/appimage.yml` for **root**) and it allows the following customizations:
```
database:
@@ -233,8 +241,8 @@ suggestions:
expiration: 24 # defines the period (in hours) in which the suggestions stored in disc will be considered up to date. Use 0 if you always want to update them. Default: 24.
```
-- Applications with ignored updates are defined at `~/.config/bauh/appimage/updates_ignored.txt` (or `/etc/bauh/appimage/updates_ignored.txt` for **root**)
-- Cached package suggestions: `~/.cache/bauh/web/suggestions.txt` (or `/var/cache/bauh/web/suggestions.yml` for **root**)
+- Applications with ignored updates are defined at `~/.config/bearhub/appimage/updates_ignored.txt` (or `/etc/bearhub/appimage/updates_ignored.txt` for **root**)
+- Cached package suggestions: `~/.cache/bearhub/web/suggestions.txt` (or `/var/cache/bearhub/web/suggestions.yml` for **root**)
##### Arch packages/AUR
@@ -245,7 +253,7 @@ suggestions:
- Automatically makes simple package compilation improvements (for AUR packages):
a) if `MAKEFLAGS` is not set in `/etc/makepkg.conf`,
- then a copy of `/etc/makepkg.conf` will be generated at `~/.config/bauh/arch/makepkg.conf` defining MAKEFLAGS to work with
+ then a copy of `/etc/makepkg.conf` will be generated at `~/.config/bearhub/arch/makepkg.conf` defining MAKEFLAGS to work with
the number of your machine processors (`-j${nproc}`).
b) same as previous, but related to `COMPRESSXZ` and `COMPRESSZST` definitions (if '--threads=0' is not defined)
@@ -267,17 +275,17 @@ suggestions:
- `Ignore reinstallation check`: it does not to check if a given AUR packages requires to be rebuilt
- `Reinstall` (AUR only): rebuilds an installed package.
-- If you have AUR added as a repository on you pacman configuration, make sure to disable bauh's support (through the settings described below)
-- AUR package compilation may require additional installed packages to work properly. Some of them are defined on the field `optdepends` of the [PKGBUILD](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=bauh)
- - for a **root** user the following additional applications must be installed:
- - `useradd`: required to create a simple user named **bauh-aur** (since **makepkg** does not allow building packages as the **root** user)
+- If you have AUR added as a repository on you pacman configuration, make sure to disable Bearhub's support (through the settings described below)
+- AUR package compilation may require additional installed packages to work properly. Some of them are defined on the field `optdepends` of the [PKGBUILD](https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=bearhub)
+ - for a **root** user the following additional applications must be installed:
+ - `useradd`: required to create a simple user named **bearhub-aur** (since **makepkg** does not allow building packages as the **root** user)
- `runuser`: required to run commands as another user
- **Repository packages currently do not support the following actions: Downgrade and History**
- If some of your installed packages are not categorized, open a PullRequest to the **bauh-files** repository changing [categories.txt](https://github.com/vinifmor/bauh-files/blob/master/arch/categories.txt)
-- During bauh initialization a full AUR normalized index is saved at `~/.cache/bauh/arch/aur/index.txt`
-- Installed AUR packages have their PKGBUILD files cached at `~/.cache/bauh/arch/installed/$pkgname`
-- Packages with ignored updates are defined at `~/.config/bauh/arch/updates_ignored.txt`
-- The configuration file is located at `~/.config/bauh/arch.yml` and it allows the following customizations:
+- During Bearhub initialization a full AUR normalized index is saved at `~/.cache/bearhub/arch/aur/index.txt`
+- Installed AUR packages have their PKGBUILD files cached at `~/.cache/bearhub/arch/installed/$pkgname`
+- Packages with ignored updates are defined at `~/.config/bearhub/arch/updates_ignored.txt`
+- The configuration file is located at `~/.config/bearhub/arch.yml` and it allows the following customizations:
```
aur: true # allows to manage AUR packages. Default: true
repositories: true # allows to manage packages from the configured repositories. Default: true
@@ -290,7 +298,7 @@ mirrors_sort_limit: 5 # defines the maximum number of mirrors that will be used
repositories_mthread_download: false # enable multi-threaded download for repository packages if aria2/axel is installed (otherwise pacman will download the packages). Default: false
automatch_providers: true # if a possible provider for a given package dependency exactly matches its name, it will be chosen instead of asking for the user to decide (false). Default: true.
edit_aur_pkgbuild: false # if the AUR PKGBUILD file should be displayed for edition before the make process. true (PKGBUILD will always be displayed for edition), false (PKGBUILD never will be displayed), null (a popup will ask if the user want to edit the PKGBUILD). Default: false.
-aur_build_dir: null # defines a custom build directory for AUR packages (a null value will point to /tmp/bauh/arch (non-root user) or /tmp/bauh_root/arch (root user)). Default: null.
+aur_build_dir: null # defines a custom build directory for AUR packages (a null value will point to /tmp/bearhub/arch (non-root user) or /tmp/bearhub_root/arch (root user)). Default: null.
aur_remove_build_dir: true # it defines if a package's generated build directory should be removed after the operation is finished (installation, upgrading, ...). Options: true, false (default: true).
aur_build_only_chosen : true # some AUR packages have a common file definition declaring several packages to be built. When this property is 'true' only the package the user select to install will be built (unless its name is different from those declared in the PKGBUILD base). With a 'null' value a popup asking if the user wants to build all of them will be displayed. 'false' will build and install all packages. Default: true.
aur_idx_exp: 1 # It defines the period (in HOURS) for the AUR index stored in disc to be considered up to date during the initialization process. Use 0 so that it is always updated. Default: 1. (P.S: this index is always updated when a package is installed/upgraded)
@@ -303,12 +311,12 @@ aur_rebuild_detector: true # it checks if packages built with old library versio
prefer_repository_provider: true # when there is just one repository provider for a given a dependency and several from AUR, it will be automatically picked.
suggestions_exp: 24 # it defines the period (in hours) in which the suggestions stored in disc will be considered up to date. Use 0 if you always want to update them.
```
-- Cached package suggestions: `~/.cache/bauh/arch/suggestions.txt` (or `/var/cache/bauh/arch/suggestions.yml` for **root**)
+- Cached package suggestions: `~/.cache/bearhub/arch/suggestions.txt` (or `/var/cache/bearhub/arch/suggestions.yml` for **root**)
##### Flatpak
-- Applications with ignored updates are defined at `~/.config/bauh/flatpak/updates_ignored.txt`
-- The configuration file is located at `~/.config/bauh/flatpak.yml` and it allows the following customizations:
+- Applications with ignored updates are defined at `~/.config/bearhub/flatpak/updates_ignored.txt`
+- The configuration file is located at `~/.config/bearhub/flatpak.yml` and it allows the following customizations:
```
installation_level: null # defines a default installation level: "user" or "system". (null will display a popup asking the level)
```
@@ -339,13 +347,13 @@ defined at [suggestions.yml](https://raw.githubusercontent.com/vinifmor/bauh-fil
- It relies on [NodeJS](https://nodejs.org/en/), [Electron](https://electronjs.org/) and [nativefier](https://github.com/jiahaog/nativefier) to do all the magic, but you do not need them installed on your system. An isolated installation environment
-will be generated at `~/.local/share/bauh/web/env` (or `/usr/local/share/bauh/web/env` for **root**).
+will be generated at `~/.local/share/bearhub/web/env` (or `/usr/local/share/bearhub/web/env` for **root**).
- It supports DRM protected content through a custom Electron implementation provided by [castLabs](https://github.com/castlabs/electron-releases). nativefier handles the switch between the official Electron and the custom.
- The isolated environment is created based on the settings defined in [environment.yml](https://raw.githubusercontent.com/vinifmor/bauh-files/master/web/env/v1/environment.yml)
(downloaded during runtime).
-- Some applications require Javascript fixes to properly work. If there is a known fix, bauh will download the file from [fix](https://github.com/vinifmor/bauh-files/tree/master/web/env/v2/fix) and
-attach it to the generated app. The fix files are saved on the disk following the pattern `~/.local/share/bauh/web/fixes/electron_{branch}/{app_name}.js` (or `/usr/local/share/bauh/web/fixes/...` for **root**)
-- The installed applications are located at `~/.local/share/bauh/installed` (or `/usr/local/share/bauh/web/installed` for **root**).
+- Some applications require Javascript fixes to properly work. If there is a known fix, Bearhub will download the file from [fix](https://github.com/vinifmor/bauh-files/tree/master/web/env/v2/fix) and
+attach it to the generated app. The fix files are saved on the disk following the pattern `~/.local/share/bearhub/web/fixes/electron_{branch}/{app_name}.js` (or `/usr/local/share/bearhub/web/fixes/...` for **root**)
+- The installed applications are located at `~/.local/share/bearhub/installed` (or `/usr/local/share/bearhub/web/installed` for **root**).
- A desktop entry / menu shortcut will be generated for the installed applications at `~/.local/share/applications` (or `/usr/share/applications` for **root**)
- If the Tray Mode **Start Minimized** is defined during the installation setup, a desktop entry will be also generated at `~/.config/autostart` (or `/etc/xdg/autostart` for **root**)
allowing the application to launch automatically after the system's boot attached to the tray.
@@ -358,7 +366,7 @@ allowing the application to launch automatically after the system's boot attache
- Extra actions
- `Clean installation environment`: removes all the installation environment folders (it does not remove installed apps)
-- The configuration file is located at `~/.config/bauh/web.yml` (or `/etc/bauh/web.yml` for **root**) and it allows the following customizations:
+- The configuration file is located at `~/.config/bearhub/web.yml` (or `/etc/bearhub/web.yml` for **root**) and it allows the following customizations:
```
environment:
@@ -370,14 +378,14 @@ environment:
suggestions:
cache_exp: 24 # defines the period (in HOURS) in which suggestions stored on the disk are considered up to date during the initialization process. Use 0 so that they are always updated. Default: 24.
```
-- Cached package suggestions: `~/.cache/bauh/web/suggestions.txt` (or `/var/cache/bauh/web/suggestions.yml` for **root**)
+- Cached package suggestions: `~/.cache/bearhub/web/suggestions.txt` (or `/var/cache/bearhub/web/suggestions.yml` for **root**)
#### General settings
##### Parameters
-You can change some application settings via environment variables or arguments (type ```bauh --help``` to get more information).
-- `--tray`: if bauh should be launched attaching itself to the system tray.
+You can change some application settings via environment variables or arguments (type ```bearhub --help``` to get more information).
+- `--tray`: if Bearhub should be launched attaching itself to the system tray.
- `--settings`: it displays only the settings window.
- `--reset`: it cleans all configurations and cached data stored in the HOME directory.
- `--logs`: it enables logs (for debugging purposes).
@@ -385,16 +393,16 @@ You can change some application settings via environment variables or arguments
- `--suggestions`: it forces loading software suggestions after the initialization process.
-##### Configuration file (**~/.config/bauh/config.yml**)
+##### Configuration file (**~/.config/bearhub/config.yml**)
```
download:
- icons: true # allows bauh to download the applications icons when they are not saved on the disk
- multithreaded: true # allows bauh to use a multithreaded download client installed on the system to download applications source files faster
+ icons: true # allows Bearhub to download the applications icons when they are not saved on the disk
+ multithreaded: true # allows Bearhub to use a multithreaded download client installed on the system to download applications source files faster
multithreaded_client: null # defines the multi-threaded download tool to be used. If null, the default installed tool will be used (priority: aria2 > axel). Possible tools/values: aria2, axel
check_ssl: true # if the security certificate (SSL) should be checked before downloading files.
-gems: null # defines the enabled applications types managed by bauh (a null value means "all available")
-locale: null # defines a different translation for bauh (a null value will retrieve the system's default locale)
+gems: null # defines the enabled applications types managed by Bearhub (a null value means "all available")
+locale: null # defines a different translation for Bearhub (a null value will retrieve the system's default locale)
store_root_password: true # if the root password should be asked only once
memory_cache:
data_expiration: 3600 # the interval in SECONDS that data cached in memory will live
@@ -403,8 +411,8 @@ suggestions:
by_type: 20 # the maximum number of application suggestions that must be retrieved per type
enabled: true # if suggestions must be displayed when no application is installed
system:
- notifications: true # if system popup should be displayed for some events. e.g: when there are updates, bauh will display a system popup
- single_dependency_checking: false # if bauh should check only once if for the available technologies on the system.
+ notifications: true # if system popup should be displayed for some events. e.g: when there are updates, Bearhub will display a system popup
+ single_dependency_checking: false # if Bearhub should check only once if for the available technologies on the system.
ui:
qt_style: fusion # defines the Qt style. A null value will map to 'fusion' as well.
table:
@@ -415,8 +423,8 @@ ui:
hdpi: true # enables HDPI rendering improvements. Use 'false' to disable them if you think the interface looks strange
auto_scale: false # activates Qt auto screen scale factor (QT_AUTO_SCREEN_SCALE_FACTOR). It fixes scaling issues for some desktop environments (like Gnome)
scale_factor: 1.0 # defines the interface display scaling factor (Qt). Raise the value to raise the interface size. The settings window display this value as a percentage (e.g: 1.0 -> 100%).
- theme: defines the path to the theme/stylesheet file with a .qss extension (e.g: /path/to/my/theme.qss). For themes provided by bauh, only a string key is needed (e.g: light). Default: light
- system_theme: merges the system's theme/stylesheet with bauh's. Default: false.
+ theme: defines the path to the theme/stylesheet file with a .qss extension (e.g: /path/to/my/theme.qss). For themes provided by Bearhub, only a string key is needed (e.g: light). Default: light
+ system_theme: merges the system's theme/stylesheet with Bearhub's. Default: false.
updates:
check_interval: 5 # the updates checking interval in minutes
ask_for_reboot: true # if a dialog asking for a system reboot should be displayed after a successful upgrade
@@ -438,7 +446,7 @@ boot:
##### Forbidden packaging formats
- System administrators and package managers of Linux distributions can disable the usage/management of supported packaging formats
-by adding their ids to the file `/etc/bauh/gems.forbidden`. This will prevent their management code to be loaded.
+by adding their ids to the file `/etc/bearhub/gems.forbidden`. This will prevent their management code to be loaded.
- Example (one id per line):
```
arch
@@ -459,52 +467,52 @@ appimage
- The priority number is used to sort the retrieved suggestions
- If Linux distributions want to provide their custom suggestions files:
- - Create the file `/etc/bauh/suggestions.conf`
+ - Create the file `/etc/bearhub/suggestions.conf`
- The content is basically a mapping for each gem to a url or local file (absolute path).
- Example:
```
arch=https://mydomain.com/arch/suggestions.txt # remote file
- appimage=/etc/bauh/appimage/suggestions.txt # local file (absolute path)
+ appimage=/etc/bearhub/appimage/suggestions.txt # local file (absolute path)
# snap = my mapping # comments with a '#' are allowed
```
- If a given gem name is omitted, its suggestions will be downloaded from the default location.
#### Directory structure, caching and logs
-- `~/.config/bauh` (or `/etc/bauh` for **root**): stores configuration files
-- `~/.cache/bauh` (or `/var/cache/bauh` for **root**): stores data about your installed applications, databases, indexes, etc. Files are stored here to provide a faster initialization and data recovery.
-- `/tmp/bauh@$USER` (e.g: `/tmp/bauh@root`): stores logging and temporary files (e.g: build dependencies)
+- `~/.config/bearhub` (or `/etc/bearhub` for **root**): stores configuration files
+- `~/.cache/bearhub` (or `/var/cache/bearhub` for **root**): stores data about your installed applications, databases, indexes, etc. Files are stored here to provide a faster initialization and data recovery.
+- `/tmp/bearhub@$USER` (e.g: `/tmp/bearhub@root`): stores logging and temporary files (e.g: build dependencies)
#### Custom themes
-- Custom themes can be provided by adding their files at `~/.local/share/bauh/themes` (or `/usr/share/bauh/themes` for **root**). Sub-folders are allowed.
+- Custom themes can be provided by adding their files at `~/.local/share/bearhub/themes` (or `/usr/share/bearhub/themes` for **root**). Sub-folders are allowed.
- Themes are composed by 2 required and 1 optional files sharing the same name:
- - `my_theme.qss`: file with the qss rules. Full example: [light.qss](https://github.com/vinifmor/bauh/blob/master/bauh/view/resources/style/light/light.qss)
- - `my_theme.meta`: file defining the theme's data. Full example: [light.meta](https://github.com/vinifmor/bauh/blob/master/bauh/view/resources/style/light/light.meta)
+ - `my_theme.qss`: file with the qss rules. Full example: [light.qss](https://github.com/spalencsar/bearhub/blob/main/bearhub/view/resources/style/light/light.qss)
+ - `my_theme.meta`: file defining the theme's data. Full example: [light.meta](https://github.com/spalencsar/bearhub/blob/main/bearhub/view/resources/style/light/light.meta)
- available fields:
- `name`: name that will be displayed on the interface. It supports translations by adding additional `name` fields with brackets and the language code (e.g: `name[es]=Mi tema`)
- `description`: theme's description that will be displayed on the interface. It supports translations like `name` (e.g: description[es] = Mi tema).
- `version`: theme's version. It just works as information at the moment. (e.g: 1.0)
- - `root_theme`: optional attribute that points to a theme that must be loaded before the theme. It supports the bauh's default theme keys (e.g: default, light, ...) or a file path (e.g: `/path/to/root/file.qss`).
- - `abstract`: optional boolean attribute (true/false) that should only be used by themes that are not complete on their own and just work as a base (root) for other themes. Abstract themes are not displayed on the interface. Full example: [default.qss](https://github.com/vinifmor/bauh/blob/master/bauh/view/resources/style/default/default.qss)
- - `my_theme.vars`: optional file defining `key=value` pairs of variables that will be available for the .qss file (can be referenced through the symbol **@**. e.g `@my_var`). Full example: [light.vars](https://github.com/vinifmor/bauh/blob/master/bauh/view/resources/style/light/light.vars)
+ - `root_theme`: optional attribute that points to a theme that must be loaded before the theme. It supports Bearhub's default theme keys (e.g: default, light, ...) or a file path (e.g: `/path/to/root/file.qss`).
+ - `abstract`: optional boolean attribute (true/false) that should only be used by themes that are not complete on their own and just work as a base (root) for other themes. Abstract themes are not displayed on the interface. Full example: [default.qss](https://github.com/spalencsar/bearhub/blob/main/bearhub/view/resources/style/default/default.qss)
+ - `my_theme.vars`: optional file defining `key=value` pairs of variables that will be available for the .qss file (can be referenced through the symbol **@**. e.g `@my_var`). Full example: [light.vars](https://github.com/spalencsar/bearhub/blob/main/bearhub/view/resources/style/light/light.vars)
- common theme variables available:
- `style_dir`: path to the .qss file directory. Example: @style_dir/my_icon.svg
- - `images`: path to bauh's icons directory (gem icons are not available through this variable). Example: @images/logo.svg
+ - `images`: path to Bearhub's icons directory (gem icons are not available through this variable). Example: @images/logo.svg
#### Tray icons
Priority:
- 1) Icon paths defined in **~/.config/bauh/config.yml**
- 2) Icons from the system with the following names: `bauh_tray_default` and `bauh_tray_updates`
+ 1) Icon paths defined in **~/.config/bearhub/config.yml**
+ 2) Icons from the system with the following names: `bearhub_tray_default` and `bearhub_tray_updates` (legacy `bauh_tray_*` names are still accepted as fallback)
3) Own packaged icons
#### CLI (Command Line Interface)
-- For now it only allows checking for software updates (`bauh-cli updates`).
-- To verify the available commands: `bauh-cli --help`.
-- To list the command parameters: `bauh-cli [command] --help`. (e.g: `bauh-cli updates --help`)
+- For now it only allows checking for software updates (`bearhub-cli updates`).
+- To verify the available commands: `bearhub-cli --help`.
+- To list the command parameters: `bearhub-cli [command] --help`. (e.g: `bearhub-cli updates --help`)
#### Improving performance
@@ -533,7 +541,7 @@ the `view` code is only attached to them (it does not know how the `gems` handle
- Separate modules for each packaging technology
- Memory and performance improvements
- Improve user experience
-- The current development changes can be checked [here](https://github.com/vinifmor/bauh/blob/staging/CHANGELOG.md)
+- The current development changes can be checked in [CHANGELOG.md](CHANGELOG.md)
#### Donations
- You can support this project through [ko-fi](https://ko-fi.com/vinifmor).
diff --git a/ROADMAP.md b/ROADMAP.md
index 8d2674b8..a8881647 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -73,6 +73,72 @@ Definition of Done:
- At least 2 maintainers can publish stable release without ad-hoc steps.
- Changelog quality and release metadata are consistent.
+### M6: Qt6 Migration (PyQt5 -> Qt6 bindings) (4-8 weeks)
+
+Context:
+- PyQt5 remains viable for short-term maintenance, but Qt5 is in maintenance mode.
+- Bearhub already carries Qt5-specific workarounds (for example `QT_QPA_PLATFORM=xcb` on Wayland).
+- Qt6 improves Wayland, HiDPI, and long-term distro packaging support on Arch.
+
+Prerequisites:
+- M3 namespace migration complete (or near complete) to avoid dual refactors.
+- M2 stability baseline in place (thread teardown and uninstall paths understood).
+
+Scope:
+- Choose Qt6 binding target: **PySide6** (preferred default: LGPL) or **PyQt6** (if GPL-only strategy is confirmed).
+- Migrate `bauh/view/qt/*` and app entry modules to Qt6 APIs.
+- Update themes/QSS validation and tray behavior on Wayland.
+- Update packaging (AUR deps, AppImage Qt6 bundle, CI GUI smoke checks).
+
+Definition of Done:
+- Bearhub starts on Qt6 without requiring `QT_QPA_PLATFORM=xcb` for normal desktop use.
+- Tray mode, settings, package table, install/uninstall, and update flows pass smoke tests.
+- AUR and documented install paths no longer require `python-pyqt5`.
+- No PyQt5 imports left in runtime code paths.
+
+PR Plan (recommended order):
+
+1. **PR-6.1: Qt6 decision + spike** ✅
+ - Decision recorded in `docs/qt6-migration.md`: **PySide6** (2026-06-26).
+ - Offscreen spike passed on Python 3.14 + PySide6 6.11.1.
+ - Exit criteria met for binding choice and local bootstrap feasibility.
+
+2. **PR-6.2: Dependency and packaging switch (dual-stack transition)**
+ - Add Qt6 dependency in `pyproject.toml` / AUR while keeping temporary PyQt5 fallback behind a feature flag if needed.
+ - Update CI to install Qt6 bindings and run import-level checks.
+ - Exit criteria: package installs with Qt6 deps on Arch clean environment.
+
+3. **PR-6.3: Core app shell migration**
+ - Migrate `bearhub/app_main.py`, `context.py`, `manage.py`, `tray.py`.
+ - Replace deprecated app attributes (`AA_EnableHighDpiScaling`, `AA_UseHighDpiPixmaps`) with Qt6 equivalents.
+ - Replace `exec_()` with `exec()` in shared dialog helpers first.
+ - Exit criteria: `bearhub --version`, `bearhub --help`, and offscreen window open succeed on Qt6.
+
+4. **PR-6.4: Threading and async UI migration**
+ - Migrate `view/qt/thread.py`, `prepare.py`, `systray.py`, `settings.py` signal/slot and `QThread` lifecycle usage.
+ - Re-test cancellation paths and worker teardown (M2 crash hotspots).
+ - Exit criteria: initialization panel + update check + tray refresh run without thread warnings.
+
+5. **PR-6.5: Main UI migration**
+ - Migrate `window.py`, `apps_table.py`, `components.py`, `dialog.py`, `info.py`, `history.py`, `screenshots.py`, `root.py`, `about.py`.
+ - Validate custom QSS themes (`light`, `darcula`, `knight`, `sublime`, `default`).
+ - Exit criteria: search/filter/install/uninstall/update UX smoke test passes.
+
+6. **PR-6.6: Desktop integration and Wayland validation**
+ - Test native Wayland session (remove hard dependency on forced `xcb` where possible).
+ - Validate tray icons (`bearhub_tray_*`) and desktop launcher behavior.
+ - Exit criteria: no deterministic crash in tray/manage on Wayland during 30-minute session test.
+
+7. **PR-6.7: Distribution hardening + PyQt5 removal**
+ - Migrate AppImage builder from pinned PyQt5 to Qt6 runtime layout.
+ - Remove PyQt5 from AUR/package metadata and code imports.
+ - Update README and maintainer docs with Qt6 runtime requirements.
+ - Exit criteria: one stable release published fully on Qt6; PyQt5 marked unsupported.
+
+Rollback strategy:
+- Keep PR slices small and releasable; each PR must compile and pass tests.
+- If a PR regresses Wayland/tray behavior, revert only that slice and keep dual-stack fallback until fixed.
+
## Release Cadence
- `bearhub-git`: continuous
@@ -80,6 +146,7 @@ Definition of Done:
## Immediate Next Actions
-1. Complete M1 gap scan (`bauh` strings still visible in UI and logs).
+1. Close remaining M1 gaps (smoke-test checklist, `NAMESPACE_MIGRATION.md` refresh).
2. Build M2 issue list from current crash reports and prioritize top 3.
-3. Start M3 technical design (import shim + package move order).
+3. Continue M3 namespace migration (`gems/` move order + shim removal date).
+4. Schedule M6 spike after M3 core move (do not start full Qt6 migration in parallel).
diff --git a/bauh/desktop/bauh.desktop b/bauh/desktop/bauh.desktop
deleted file mode 100644
index 372daec8..00000000
--- a/bauh/desktop/bauh.desktop
+++ /dev/null
@@ -1,21 +0,0 @@
-[Desktop Entry]
-Type=Application
-Name=Applications (Bearhub)
-Name[pt]=Aplicativos (Bearhub)
-Name[es]=Aplicaciones (Bearhub)
-Name[ca]=Aplicacions (Bearhub)
-Name[it]=Applicazioni (Bearhub)
-Name[de]=Anwendungen (Bearhub)
-Name[ru]=Приложения (Bearhub)
-Name[tr]=Paket Yönetici (Bearhub)
-Categories=System;
-Comment=Install and remove applications (AppImage, Arch, Flatpak, Web)
-Comment[pt]=Instale e remova aplicativos (AppImage, Arch, Flatpak, Web)
-Comment[es]=Instalar y eliminar aplicaciones (AppImage, Arch, Flatpak, Web)
-Comment[it]=Installa e rimuovi applicazioni (AppImage, Arch, Flatpak, Web)
-Comment[de]=Anwendungen installieren und entfernen (AppImage, Arch, Flatpak, Web)
-Comment[ca]=Instal·lar i eliminar aplicacions (AppImage, Arch, Flatpak, Web)
-Comment[ru]=Установка и удаление приложений (AppImage, Arch, Flatpak, Web)
-Comment[tr]=Uygulama yükle/kaldır (AppImage, Arch, Flatpak, Web)
-Exec=env QT_QPA_PLATFORM=xcb /usr/bin/bearhub
-Icon=bearhub
diff --git a/bauh/desktop/bauh_tray.desktop b/bauh/desktop/bauh_tray.desktop
deleted file mode 100644
index 2d92ffef..00000000
--- a/bauh/desktop/bauh_tray.desktop
+++ /dev/null
@@ -1,21 +0,0 @@
-[Desktop Entry]
-Type=Application
-Name=Applications (Bearhub tray)
-Name[pt]=Aplicativos (Bearhub tray)
-Name[es]=Aplicaciones (Bearhub tray)
-Name[ca]=Aplicacions (Bearhub tray)
-Name[it]=Applicazioni (Bearhub tray)
-Name[de]=Anwendungen (Bearhub tray)
-Name[ru]=Приложения (Bearhub tray)
-Name[tr]=Paket Yönetici (Bearhub tray)
-Categories=System;
-Comment=Install and remove applications (AppImage, Arch, Flatpak, Web)
-Comment[pt]=Instale e remova aplicativos (AppImage, Arch, Flatpak, Web)
-Comment[es]=Instalar y eliminar aplicaciones (AppImage, Arch, Flatpak, Web)
-Comment[it]=Installa e rimuovi applicazioni (AppImage, Arch, Flatpak, Web)
-Comment[de]=Anwendungen installieren und entfernen (AppImage, Arch, Flatpak, Web)
-Comment[ca]=Instal·lar i eliminar aplicacions (AppImage, Arch, Flatpak, Web)
-Comment[ru]=Установка и удаление приложений (AppImage, Arch, Flatpak, Web)
-Comment[tr]=Uygulama yükle/kaldır (AppImage, Arch, Flatpak, Web)
-Exec=env QT_QPA_PLATFORM=xcb /usr/bin/bearhub-tray
-Icon=bearhub
diff --git a/bauh/gems/appimage/__init__.py b/bauh/gems/appimage/__init__.py
index 4812d5cc..cd6914c5 100644
--- a/bauh/gems/appimage/__init__.py
+++ b/bauh/gems/appimage/__init__.py
@@ -2,7 +2,6 @@ import os
from pathlib import Path
from typing import Optional
-from bauh import __app_name__
from bauh.api.paths import CONFIG_DIR, TEMP_DIR, CACHE_DIR, BINARIES_DIR, SHARED_FILES_DIR
from bauh.commons import resource
@@ -13,7 +12,7 @@ CONFIG_FILE = f'{CONFIG_DIR}/appimage.yml'
APPIMAGE_CONFIG_DIR = f'{CONFIG_DIR}/appimage'
UPDATES_IGNORED_FILE = f'{APPIMAGE_CONFIG_DIR}/updates_ignored.txt'
SYMLINKS_DIR = BINARIES_DIR
-URL_COMPRESSED_DATABASES = f'https://raw.githubusercontent.com/vinifmor/{__app_name__}-files/master/appimage/dbs.tar.gz'
+URL_COMPRESSED_DATABASES = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/appimage/dbs.tar.gz'
APPIMAGE_CACHE_DIR = f'{CACHE_DIR}/appimage'
DATABASE_APPS_FILE = f'{APPIMAGE_CACHE_DIR}/apps.db'
DATABASE_RELEASES_FILE = f'{APPIMAGE_CACHE_DIR}/releases.db'
diff --git a/bauh/gems/appimage/controller.py b/bauh/gems/appimage/controller.py
index 123707e6..2cb56695 100644
--- a/bauh/gems/appimage/controller.py
+++ b/bauh/gems/appimage/controller.py
@@ -12,6 +12,7 @@ from typing import Set, Type, List, Tuple, Optional, Iterable, Generator
from colorama import Fore
+from bauh import __app_name__
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, UpgradeRequirement, \
TransactionResult, SoftwareAction, SettingsView, SettingsController
@@ -721,7 +722,7 @@ class AppImageManager(SoftwareManager, SettingsController):
return TransactionResult.fail()
def _gen_desktop_entry_path(self, app: AppImage) -> str:
- return f'{DESKTOP_ENTRIES_DIR}/bauh_appimage_{app.get_clean_name()}.desktop'
+ return f'{DESKTOP_ENTRIES_DIR}/{__app_name__}_appimage_{app.get_clean_name()}.desktop'
def is_enabled(self) -> bool:
return self.enabled
@@ -855,14 +856,14 @@ class AppImageManager(SoftwareManager, SettingsController):
for f in glob.glob(f'{APPIMAGE_SHARED_DIR}/*.db'):
try:
if logs:
- print(f'[bauh][appimage] Deleting {f}')
+ print(f'[{__app_name__}][appimage] Deleting {f}')
os.remove(f)
if logs:
- print(f'{Fore.YELLOW}[bauh][appimage] {f} deleted{Fore.RESET}')
+ print(f'{Fore.YELLOW}[{__app_name__}][appimage] {f} deleted{Fore.RESET}')
except Exception:
if logs:
- print(f'{Fore.RED}[bauh][appimage] An exception has happened when deleting {f}{Fore.RESET}')
+ print(f'{Fore.RED}[{__app_name__}][appimage] An exception has happened when deleting {f}{Fore.RESET}')
traceback.print_exc()
def get_settings(self) -> Optional[Generator[SettingsView, None, None]]:
@@ -1092,7 +1093,7 @@ class AppImageManager(SoftwareManager, SettingsController):
@property
def app_repository(self) -> str:
if self._app_repository is None:
- self._app_repository = f"https://github.com/vinifmor/{self.context.app_name}"
+ self._app_repository = 'https://github.com/spalencsar/bearhub'
return self._app_repository
diff --git a/bauh/gems/arch/__init__.py b/bauh/gems/arch/__init__.py
index 048276d5..fc953859 100644
--- a/bauh/gems/arch/__init__.py
+++ b/bauh/gems/arch/__init__.py
@@ -1,15 +1,14 @@
import os
from typing import Optional
-from bauh import __app_name__
from bauh.api.paths import CONFIG_DIR, TEMP_DIR, CACHE_DIR, get_temp_dir
from bauh.commons import resource
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
ARCH_CACHE_DIR = f'{CACHE_DIR}/arch'
CATEGORIES_FILE_PATH = f'{ARCH_CACHE_DIR}/categories.txt'
-URL_CATEGORIES_FILE = f'https://raw.githubusercontent.com/vinifmor/{__app_name__}-files/master/arch/categories.txt'
-URL_GPG_SERVERS = f'https://raw.githubusercontent.com/vinifmor/{__app_name__}-files/master/arch/gpgservers.txt'
+URL_CATEGORIES_FILE = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/arch/categories.txt'
+URL_GPG_SERVERS = 'https://raw.githubusercontent.com/vinifmor/bauh-files/master/arch/gpgservers.txt'
ARCH_CONFIG_DIR = f'{CONFIG_DIR}/arch'
CUSTOM_MAKEPKG_FILE = f'{ARCH_CONFIG_DIR}/makepkg.conf'
AUR_INDEX_FILE = f'{ARCH_CACHE_DIR}/aur/index.txt'
diff --git a/bauh/gems/arch/worker.py b/bauh/gems/arch/worker.py
index 002331ce..97eb525c 100644
--- a/bauh/gems/arch/worker.py
+++ b/bauh/gems/arch/worker.py
@@ -13,6 +13,7 @@ from typing import Optional
import requests
+from bauh import __app_name__
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.handler import TaskManager
from bauh.commons.boot import CreateConfigFile
@@ -361,7 +362,7 @@ class ArchCompilationOptimizer(Thread):
self.taskman.update_progress(self.task_id, 80, None)
if custom_makepkg and optimizations:
- generated_by = '# \n'
+ generated_by = f'# \n'
custom_makepkg = custom_makepkg + '\n' + generated_by + '\n'.join(optimizations) + '\n'
with open(CUSTOM_MAKEPKG_FILE, 'w+') as f:
diff --git a/bauh/gems/web/controller.py b/bauh/gems/web/controller.py
index 65467fcd..da486332 100644
--- a/bauh/gems/web/controller.py
+++ b/bauh/gems/web/controller.py
@@ -15,6 +15,7 @@ import yaml
from colorama import Fore
from requests import Response
+from bauh import __app_name__
from bauh.api.abstract.context import ApplicationContext
from bauh.api.abstract.controller import SoftwareManager, SearchResult, UpgradeRequirements, TransactionResult, \
SoftwareAction, SettingsView, SettingsController
@@ -1149,15 +1150,15 @@ class WebApplicationManager(SoftwareManager, SettingsController):
def clear_data(self, logs: bool = True):
if os.path.exists(ENV_PATH):
if logs:
- print('[bauh][web] Deleting directory {}'.format(ENV_PATH))
+ print('[{}][web] Deleting directory {}'.format(__app_name__, ENV_PATH))
try:
shutil.rmtree(ENV_PATH)
if logs:
- print('{}[bauh][web] Directory {} deleted{}'.format(Fore.YELLOW, ENV_PATH, Fore.RESET))
+ print('{}[{}][web] Directory {} deleted{}'.format(Fore.YELLOW, __app_name__, ENV_PATH, Fore.RESET))
except Exception:
if logs:
- print('{}[bauh][web] An exception has happened when deleting {}{}'.format(Fore.RED, ENV_PATH,
+ print('{}[{}][web] An exception has happened when deleting {}{}'.format(Fore.RED, __app_name__, ENV_PATH,
Fore.RESET))
traceback.print_exc()
diff --git a/bauh/view/qt/about.py b/bauh/view/qt/about.py
index 882342d7..7a8ab56b 100644
--- a/bauh/view/qt/about.py
+++ b/bauh/view/qt/about.py
@@ -7,7 +7,7 @@ from bauh import __version__, ROOT_DIR
from bauh.context import generate_i18n
from bauh.view.util import resource
-DISPLAY_NAME = 'bearhub'
+DISPLAY_NAME = 'Bearhub'
PROJECT_URL = 'https://github.com/spalencsar/bearhub'
LICENSE_URL = 'https://raw.githubusercontent.com/spalencsar/bearhub/main/LICENSE'
diff --git a/bauh/view/qt/systray.py b/bauh/view/qt/systray.py
index 44228971..f17bcc91 100755
--- a/bauh/view/qt/systray.py
+++ b/bauh/view/qt/systray.py
@@ -136,7 +136,9 @@ class TrayIcon(QSystemTrayIcon):
if config['ui']['tray']['default_icon']:
self.icon_default = QIcon(config['ui']['tray']['default_icon'])
else:
- self.icon_default = QIcon.fromTheme('bauh_tray_default')
+ self.icon_default = QIcon.fromTheme(f'{__app_name__}_tray_default')
+ if self.icon_default.isNull():
+ self.icon_default = QIcon.fromTheme('bauh_tray_default')
if self.icon_default.isNull():
self.icon_default = load_resource_icon('img/logo.svg', 24)
@@ -144,7 +146,9 @@ class TrayIcon(QSystemTrayIcon):
if config['ui']['tray']['updates_icon']:
self.icon_updates = QIcon(config['ui']['tray']['updates_icon'])
else:
- self.icon_updates = QIcon.fromTheme('bauh_tray_updates')
+ self.icon_updates = QIcon.fromTheme(f'{__app_name__}_tray_updates')
+ if self.icon_updates.isNull():
+ self.icon_updates = QIcon.fromTheme('bauh_tray_updates')
if self.icon_updates.isNull():
self.icon_updates = load_resource_icon('img/logo_update.svg', 24)
diff --git a/bauh/view/resources/img/logo.svg b/bauh/view/resources/img/logo.svg
index 8861d987..741db6b4 100644
--- a/bauh/view/resources/img/logo.svg
+++ b/bauh/view/resources/img/logo.svg
@@ -1 +1,453 @@
-
\ No newline at end of file
+
+
diff --git a/bauh/view/util/util.py b/bauh/view/util/util.py
index 42dbe685..1611d20e 100644
--- a/bauh/view/util/util.py
+++ b/bauh/view/util/util.py
@@ -64,20 +64,20 @@ def get_distro():
def clean_app_files(managers: List[SoftwareManager], logs: bool = True):
if logs:
- print('[bauh] Cleaning configuration and cache files')
+ print('[{}] Cleaning configuration and cache files'.format(__app_name__))
for path in (CACHE_DIR, CONFIG_DIR, TEMP_DIR):
if logs:
- print('[bauh] Deleting directory {}'.format(path))
+ print('[{}] Deleting directory {}'.format(__app_name__, path))
if os.path.exists(path):
try:
shutil.rmtree(path)
if logs:
- print('{}[bauh] Directory {} deleted{}'.format(Fore.YELLOW, path, Fore.RESET))
+ print('{}[{}] Directory {} deleted{}'.format(Fore.YELLOW, __app_name__, path, Fore.RESET))
except Exception:
if logs:
- print('{}[bauh] An exception has happened when deleting {}{}'.format(Fore.RED, path, Fore.RESET))
+ print('{}[{}] An exception has happened when deleting {}{}'.format(Fore.RED, __app_name__, path, Fore.RESET))
traceback.print_exc()
if managers:
@@ -85,4 +85,4 @@ def clean_app_files(managers: List[SoftwareManager], logs: bool = True):
m.clear_data()
if logs:
- print('[bauh] Cleaning finished')
+ print('[{}] Cleaning finished'.format(__app_name__))
diff --git a/bearhub/view/resources/img/logo.svg b/bearhub/view/resources/img/logo.svg
index 8861d987..741db6b4 100644
--- a/bearhub/view/resources/img/logo.svg
+++ b/bearhub/view/resources/img/logo.svg
@@ -1 +1,453 @@
-
\ No newline at end of file
+
+
diff --git a/docs/qt6-migration.md b/docs/qt6-migration.md
new file mode 100644
index 00000000..71687bc1
--- /dev/null
+++ b/docs/qt6-migration.md
@@ -0,0 +1,251 @@
+# Qt6 Migration Guide (PR-6.1)
+
+This document defines the technical approach for migrating Bearhub from **PyQt5** to a **Qt6 Python binding**.
+
+Related roadmap milestone: `ROADMAP.md` → **M6: Qt6 Migration**.
+
+## Goals
+
+- Keep Bearhub feature parity during migration (no UI redesign).
+- Improve long-term desktop compatibility (Wayland, HiDPI, Arch packaging).
+- Remove PyQt5-only workarounds where Qt6 provides stable behavior.
+- Land migration in small, reversible PR slices.
+
+## Non-Goals (for M6)
+
+- Rewriting the UI toolkit (GTK, Electron, web UI).
+- Visual redesign or UX overhaul.
+- Parallel feature development inside migration PRs.
+
+## Prerequisites
+
+- **M3** namespace migration is complete enough that UI modules live under stable `bearhub.*` import paths.
+- **M2** thread/uninstall crash paths are documented and reproducible.
+- Unit test CI is green (`tests/`, GitHub workflow `tests.yml`).
+
+## Current Qt Footprint (inventory)
+
+Runtime PyQt5 usage is concentrated in:
+
+| Area | Files | Notes |
+|---|---|---|
+| App entry | `bearhub/app_main.py`, `bearhub/context.py`, `bearhub/manage.py`, `bearhub/tray.py` | app attributes, screen scale env |
+| Main window/UI | `bauh/view/qt/window.py`, `apps_table.py`, `components.py`, `dialog.py` | largest migration surface |
+| Async/threading | `bauh/view/qt/thread.py`, `prepare.py`, `systray.py`, `settings.py`, `root.py` | `QThread`, `pyqtSignal` |
+| Utility/UI helpers | `bauh/view/qt/qt_utils.py`, `about.py`, `info.py`, `history.py`, `screenshots.py` | includes `QDesktopWidget` |
+| Settings integration | `bauh/view/core/settings.py` | `QStyleFactory` |
+| Misc | `bauh/view/util/util.py` | icon helpers |
+
+Known Qt5-specific patterns in code today:
+
+- `app.exec_()` and dialog `exec_()` calls
+- `pyqtSignal` declarations
+- `Qt.AA_EnableHighDpiScaling` / `Qt.AA_UseHighDpiPixmaps`
+- `QDesktopWidget` screen geometry logic (`qt_utils.py`)
+- Forced Wayland workaround in desktop files: `QT_QPA_PLATFORM=xcb`
+
+Packaging/runtime dependencies today:
+
+- Python package: `pyqt5>=5.12` (`requirements.txt`, `pyproject.toml`)
+- AUR: `python-pyqt5`
+- AppImage builder pins `pyqt5==5.15.10` in `linux_dist/appimage/AppImageBuilder.yml`
+
+## Binding Decision Matrix: PySide6 vs PyQt6
+
+| Criterion | PySide6 | PyQt6 | Notes for Bearhub |
+|---|---|---|---|
+| Qt version | Qt6 | Qt6 | Equal target platform |
+| License | LGPL v3 | GPL v3 / commercial | Bearhub app license is `zlib/libpng` |
+| License fit for permissive app | Strong | Weaker unless commercial license purchased | PySide6 is lower compliance risk for distro packaging |
+| Arch packaging | `pyside6` | `python-pyqt6` | Arch `[extra]` provides `pyside6` |
+| API style | Qt official style (`Signal`, `Slot`) | PyQt style (`pyqtSignal`, `pyqtSlot`) | Mechanical rename/refactor needed either way |
+| Documentation/examples | Qt docs + Qt for Python | Riverbank docs | Comparable quality |
+| Migration effort from PyQt5 | Medium | Medium-low | PyQt6 is closer syntactically, but not enough to outweigh license fit |
+| Long-term maintenance | Qt Company backed | Riverbank | Both active |
+| Wayland/tray maturity | Qt6 baseline | Qt6 baseline | Main gain comes from Qt6 itself |
+
+### Recommendation
+
+**Default choice: PySide6**.
+
+Reasoning:
+
+1. Bearhub is permissively licensed (`zlib/libpng`); LGPL binding is a safer default for distribution.
+2. Migration effort is dominated by Qt5→Qt6 API changes, not PyQt5→PyQt6 naming alone.
+3. Wayland/HiDPI improvements come from Qt6 runtime regardless of binding.
+
+### Decision fallback
+
+Choose **PyQt6** only if maintainers explicitly accept GPL obligations for combined distribution and/or purchase commercial licensing.
+
+Record the final decision in this file before PR-6.2 starts:
+
+```
+Decision: PySide6
+Date: 2026-06-26
+Owner: Bearhub maintainers
+Rationale: LGPL fit for zlib-licensed app; Qt6 gains independent of binding choice
+```
+
+## Migration Design
+
+### 1) Introduce a thin Qt compatibility layer (short-lived)
+
+Add `bearhub/view/qt/bindings.py` (or similar) exposing:
+
+- signal/slot aliases
+- app exec helper
+- screen geometry helper
+- common enum imports
+
+Purpose: reduce churn while migrating module-by-module. Remove this layer in PR-6.7.
+
+### 2) Mechanical API updates
+
+| PyQt5 / Qt5 pattern | Qt6 target |
+|---|---|
+| `exec_()` | `exec()` |
+| `pyqtSignal` | `Signal` (PySide6) / `pyqtSignal` (PyQt6) |
+| `AA_EnableHighDpiScaling`, `AA_UseHighDpiPixmaps` | Qt6 high-DPI env defaults + `QT_SCALE_FACTOR` |
+| `QDesktopWidget` | `QScreen` via `QApplication.primaryScreen()` / `screenAt()` |
+| enum usage (`Qt.AlignCenter`) | namespaced enums (`Qt.AlignmentFlag.AlignCenter`) where required |
+
+### 3) High-risk functional areas
+
+Prioritize manual validation for:
+
+- tray icon lifecycle and update notifications
+- initialization panel thread cancellation (`prepare.py`, `thread.py`)
+- uninstall flow teardown (M2 crash history)
+- settings window + root password dialog modal behavior
+- QSS theme rendering across all bundled themes
+
+## PR Slice Plan (M6)
+
+1. **PR-6.1** (this doc + spike)
+2. **PR-6.2** dependency/packaging dual-stack
+3. **PR-6.3** app shell migration
+4. **PR-6.4** threading/async migration
+5. **PR-6.5** main UI migration
+6. **PR-6.6** Wayland/tray validation
+7. **PR-6.7** remove PyQt5 + distribution hardening
+
+Each PR must:
+
+- compile
+- pass unit tests
+- include a short manual smoke note in PR description
+
+## PR-6.1 Spike Procedure
+
+Goal: prove Qt6 app boot in <15 minutes on a maintainer machine.
+
+Suggested spike steps:
+
+```bash
+python3 -m venv /tmp/bearhub-qt6-spike
+/tmp/bearhub-qt6-spike/bin/pip install PySide6 # first install can take several minutes
+
+# offscreen run (must quit explicitly; plain exec() blocks)
+QT_QPA_PLATFORM=offscreen /tmp/bearhub-qt6-spike/bin/python -c "
+from PySide6.QtWidgets import QApplication, QLabel
+from PySide6.QtCore import QTimer
+app = QApplication([])
+label = QLabel('Bearhub Qt6 spike')
+label.show()
+QTimer.singleShot(0, app.quit)
+app.exec()
+print('spike-ok')
+"
+```
+
+Spike acceptance criteria:
+
+- chosen Qt6 binding installs cleanly on Arch
+- minimal window can open offscreen
+- no blocking packaging conflicts with current `python-pyqt5` install path
+- maintainer records binding decision in this document
+
+### Spike Result (2026-06-26)
+
+Environment:
+
+- OS: Arch Linux
+- Python: `3.14.6`
+- Binding installed: `PySide6 6.11.1` (pip wheel in `/tmp/bearhub-qt6-spike`)
+- Arch repo package available: `pyside6 6.11.1-1` (`pacman -Ss pyside6`)
+
+Results:
+
+| Check | Result |
+|---|---|
+| PySide6 install | Pass (`pip install PySide6`) |
+| Offscreen window bootstrap | Pass (`spike-ok`) |
+| `import bearhub` with PySide6 present in same venv | Pass (metadata import only) |
+| Tray availability in offscreen env | Expected false (`QSystemTrayIcon.isSystemTrayAvailable() == False`) |
+| Theme icon lookup (`bearhub_tray_default`) in offscreen env | Expected null icon |
+
+Notes:
+
+- Offscreen spike must call `app.quit()` (for example via `QTimer.singleShot`) or the process blocks in `app.exec()`.
+- Tray/icon checks are not valid in offscreen mode; validate in PR-6.6 on a real desktop session (X11/Wayland).
+- Editable install of Bearhub into the spike venv was not required for PR-6.1 and can be deferred to PR-6.2.
+
+PR-6.1 status: **accepted** (decision + technical spike complete).
+
+## Test Strategy
+
+### Automated
+
+- keep existing unit tests green in every PR
+- add import smoke test for Qt binding module once dual-stack starts
+- optional CI job later: offscreen `bearhub --version` + window bootstrap
+
+### Manual smoke (required per PR from PR-6.3 onward)
+
+1. `bearhub` starts to manage panel
+2. search + filter table
+3. install + uninstall small test package
+4. update check from tray and panel
+5. settings open/close
+6. Wayland session test (GNOME/KDE) without forced `xcb` where possible
+
+## Packaging Impact
+
+| Channel | Current | Target |
+|---|---|---|
+| `pyproject.toml` / `requirements.txt` | `pyqt5` | `PySide6` (or `PyQt6`) |
+| AUR `depends` | `python-pyqt5` | `pyside6` (or `python-pyqt6`) |
+| AppImage | pinned PyQt5 wheel payload | Qt6 runtime bundle aligned with binding |
+| Desktop files | `QT_QPA_PLATFORM=xcb` workaround | remove workaround when Wayland stable |
+
+## Rollback Plan
+
+- Keep PRs small and independently revertible.
+- During PR-6.2..PR-6.6, allow temporary dual-stack install docs for maintainers.
+- If tray/Wayland regressions appear, revert only the failing PR slice.
+- Do not ship stable release with mixed Qt5/Qt6 runtime dependencies.
+
+## Risks and Mitigations
+
+| Risk | Impact | Mitigation |
+|---|---|---|
+| Tray behavior differs on Wayland | High | dedicated PR-6.6 matrix tests + fallback docs |
+| `QThread` teardown regressions | High | migrate threading in isolated PR-6.4 with M2 crash retest |
+| QSS theme breakage | Medium | theme snapshot checks for all bundled styles |
+| AppImage size growth with Qt6 | Medium | keep module pruning in AppImage `after_bundle` |
+| License mismatch (PyQt6 GPL) | Legal/process | default to PySide6 unless explicit maintainer decision |
+
+## Open Questions
+
+- Do we require native Wayland support in first Qt6 stable, or is XWayland acceptable initially?
+- Should CI add GUI smoke with `QT_QPA_PLATFORM=offscreen` in M6 or post-M6?
+- Do we keep temporary `xcb` launcher fallback for one release cycle after Qt6 cutover?
+
+## Completion Criteria (M6 Done)
+
+- No `PyQt5` imports in runtime modules
+- Bearhub stable release published on Qt6 binding
+- AUR/AppImage/README no longer list PyQt5
+- Wayland smoke test passes without mandatory `QT_QPA_PLATFORM=xcb`
+- PyQt5 marked unsupported in changelog and maintainer docs
\ No newline at end of file
diff --git a/gfx/bearhub.png b/gfx/bearhub.png
new file mode 100644
index 00000000..0115019f
Binary files /dev/null and b/gfx/bearhub.png differ
diff --git a/gfx/bearhub.svg b/gfx/bearhub.svg
new file mode 100644
index 00000000..741db6b4
--- /dev/null
+++ b/gfx/bearhub.svg
@@ -0,0 +1,453 @@
+
+
diff --git a/linux_dist/appimage/AppImageBuilder.yml b/linux_dist/appimage/AppImageBuilder.yml
index f9b360df..0e5276d2 100755
--- a/linux_dist/appimage/AppImageBuilder.yml
+++ b/linux_dist/appimage/AppImageBuilder.yml
@@ -3,26 +3,27 @@ script:
- rm -rf AppDir appimage-builder-cache *.AppImage *.zsync |true
- mkdir -p AppDir/usr/share/icons/hicolor/scalable/apps
- mkdir -p AppDir/usr/share/applications
- - wget https://github.com/vinifmor/bauh/archive/${BAUH_VERSION}.tar.gz || exit 1
- - tar -xf ${BAUH_VERSION}.tar.gz || exit 1
- - cd bauh-${BAUH_VERSION} || exit 1
- - rm setup.cfg setup.py requirements.txt || exit 1 # removing the outdated installation files
+ - wget -O bearhub-${BEARHUB_VERSION}.tar.gz https://github.com/spalencsar/bearhub/archive/refs/tags/${BEARHUB_VERSION}.tar.gz || exit 1
+ - tar -xf bearhub-${BEARHUB_VERSION}.tar.gz || exit 1
+ - cd bearhub-${BEARHUB_VERSION} || exit 1
- python3 -m build --wheel --no-isolation || exit 1
- python3 -m installer --destdir=../AppDir --prefix=/usr dist/*.whl || exit 1
- - test -e ../AppDir/usr/bin/bauh || exit 1
- - cp bauh/view/resources/img/logo.svg ../AppDir/usr/share/icons/hicolor/scalable/apps/bauh.svg || exit 1
- - cp bauh/desktop/bauh.desktop ../AppDir/usr/share/applications || exit 1
+ - test -e ../AppDir/usr/bin/bearhub || exit 1
+ - test -e ../AppDir/usr/bin/bearhub-tray || exit 1
+ - cp bearhub/view/resources/img/logo.svg ../AppDir/usr/share/icons/hicolor/scalable/apps/bearhub.svg || exit 1
+ - cp bearhub/desktop/bearhub.desktop ../AppDir/usr/share/applications || exit 1
+ - cp bearhub/desktop/bearhub_tray.desktop ../AppDir/usr/share/applications || exit 1
AppDir:
path: ./AppDir
app_info:
- id: bauh
- name: bauh
- icon: bauh
- version: !ENV ${BAUH_VERSION}
+ id: bearhub
+ name: Bearhub
+ icon: bearhub
+ version: !ENV ${BEARHUB_VERSION}
exec: /usr/bin/python3
- exec_args: "$APPDIR/usr/bin/bauh $@"
+ exec_args: "$APPDIR/usr/bin/bearhub $@"
apt:
arch: amd64
@@ -94,7 +95,6 @@ AppDir:
use_host_x: true
AppImage:
- update-information: 'gh-releases-zsync|vinifmor|bauh|latest|bauh-*x86_64.AppImage.zsync'
+ update-information: 'gh-releases-zsync|spalencsar|bearhub|latest|bearhub-*x86_64.AppImage.zsync'
sign-key: None
- arch: x86_64
-
+ arch: x86_64
\ No newline at end of file
diff --git a/linux_dist/appimage/build.sh b/linux_dist/appimage/build.sh
index ffe65632..50454c0c 100755
--- a/linux_dist/appimage/build.sh
+++ b/linux_dist/appimage/build.sh
@@ -1,6 +1,11 @@
#!/bin/bash
set -Ceufox pipefail
-docker build -t bauh-appimage .
-docker run -e BAUH_VERSION=$BAUH_VERSION -v ./AppImageBuilder.yml:/build/AppImageBuilder.yml --rm --cap-add=SYS_ADMIN --device /dev/fuse --mount type=bind,source="$(pwd)",target=/build bauh-appimage
-# volume required to run tests: -v /var/run/docker.sock:/var/run/docker.sock
+if [[ -z "${BEARHUB_VERSION:-}" ]]; then
+ echo "Set BEARHUB_VERSION to a Bearhub release tag (e.g. 0.10.7-bearhub.6)." >&2
+ exit 1
+fi
+
+docker build -t bearhub-appimage .
+docker run -e BEARHUB_VERSION="$BEARHUB_VERSION" -v ./AppImageBuilder.yml:/build/AppImageBuilder.yml --rm --cap-add=SYS_ADMIN --device /dev/fuse --mount type=bind,source="$(pwd)",target=/build bearhub-appimage
+# volume required to run tests: -v /var/run/docker.sock:/var/run/docker.sock
\ No newline at end of file
diff --git a/packaging/aur/README.md b/packaging/aur/README.md
index 38eaca50..da13a49d 100644
--- a/packaging/aur/README.md
+++ b/packaging/aur/README.md
@@ -10,3 +10,26 @@ Before publishing:
1. Generate `.SRCINFO` with `makepkg --printsrcinfo > .SRCINFO` inside each package directory.
2. Build-test locally with `makepkg -si`.
3. Push each package to its own AUR Git repository (`aur@aur.archlinux.org:bearhub.git` and `aur@aur.archlinux.org:bearhub-git.git`).
+
+## Stable release checklist (`bearhub`)
+
+1. Update `CHANGELOG.md` and packaging paths for the new tag (e.g. `0.10.7-bearhub.6`).
+2. Commit all release changes on `main`.
+3. Create and push the git tag:
+ ```bash
+ git tag -a 0.10.7-bearhub.6 -m "Release 0.10.7-bearhub.6"
+ git push origin main
+ git push origin 0.10.7-bearhub.6
+ ```
+4. Refresh tarball checksum in `bearhub/PKGBUILD`:
+ ```bash
+ cd packaging/aur/bearhub
+ makepkg -g >> PKGBUILD # or updpkgsums after source URL is reachable
+ makepkg --printsrcinfo > .SRCINFO
+ ```
+5. Sync to local AUR clones:
+ ```bash
+ scripts/sync-aur-packaging.sh
+ ```
+6. Build-test both AUR packages, then publish AUR commits.
+7. Optional: create GitHub release from tag `0.10.7-bearhub.6`.
diff --git a/packaging/aur/bearhub-git/PKGBUILD b/packaging/aur/bearhub-git/PKGBUILD
index 8d46bb1b..c4acb2cb 100644
--- a/packaging/aur/bearhub-git/PKGBUILD
+++ b/packaging/aur/bearhub-git/PKGBUILD
@@ -1,6 +1,6 @@
pkgname=bearhub-git
pkgver=0.10.7.r0.g0000000
-pkgrel=4
+pkgrel=5
pkgdesc="Arch-first package manager hub (development snapshot, fork of bauh)"
arch=('any')
url="https://github.com/spalencsar/bearhub"
diff --git a/packaging/aur/bearhub/PKGBUILD b/packaging/aur/bearhub/PKGBUILD
index 135b8bf8..28f93c96 100644
--- a/packaging/aur/bearhub/PKGBUILD
+++ b/packaging/aur/bearhub/PKGBUILD
@@ -1,6 +1,6 @@
pkgname=bearhub
pkgver=0.10.7
-pkgrel=8
+pkgrel=1
pkgdesc="Arch-first package manager hub (community-maintained fork of bauh)"
arch=('any')
url="https://github.com/spalencsar/bearhub"
@@ -22,16 +22,16 @@ optdepends=(
makedepends=('python-build' 'python-installer' 'python-setuptools' 'python-wheel')
provides=('bearhub' 'bauh')
conflicts=('bearhub-git' 'bauh' 'bauh-git')
-source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/${pkgver}-bearhub.5.tar.gz")
-sha256sums=('42001198cccd5ade206f93fc0570c9d3dbb7e49a1f85b790fa577c3af41aee70')
+source=("$pkgname-$pkgver.tar.gz::$url/archive/refs/tags/${pkgver}-bearhub.6.tar.gz")
+sha256sums=('SKIP')
build() {
- cd "$srcdir/$pkgname-${pkgver}-bearhub.5"
+ cd "$srcdir/$pkgname-${pkgver}-bearhub.6"
python -m build --wheel --no-isolation
}
package() {
- cd "$srcdir/$pkgname-${pkgver}-bearhub.5"
+ cd "$srcdir/$pkgname-${pkgver}-bearhub.6"
sed -i 's|^Exec=/usr/bin/bearhub$|Exec=env QT_QPA_PLATFORM=xcb /usr/bin/bearhub|' "bearhub/desktop/bearhub.desktop"
sed -i 's|^Exec=/usr/bin/bearhub-tray$|Exec=env QT_QPA_PLATFORM=xcb /usr/bin/bearhub-tray|' "bearhub/desktop/bearhub_tray.desktop"
diff --git a/pyproject.toml b/pyproject.toml
index f0237b2f..ab8f8fd9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -54,3 +54,14 @@ version = {attr = "bearhub.__version__"}
[tool.setuptools.packages.find]
exclude = ["tests.*", "tests"]
+
+[tool.setuptools.package-data]
+bearhub = ["desktop/*"]
+bauh = [
+ "view/resources/locale/*",
+ "view/resources/img/*",
+ "view/resources/style/*",
+ "view/resources/style/*/img/*",
+ "gems/*/resources/img/*",
+ "gems/*/resources/locale/*",
+]
diff --git a/setup.py b/setup.py
index 2199a5d6..0c112ae4 100644
--- a/setup.py
+++ b/setup.py
@@ -36,7 +36,17 @@ setup(
python_requires=">=3.5",
url=URL,
packages=find_packages(exclude=["tests.*", "tests"]),
- package_data={APP_PACKAGE: ["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/*",
+ ],
+ "bearhub": ["desktop/*"],
+ },
install_requires=requirements,
test_suite="tests",
entry_points={
diff --git a/tests/gems/arch/test_pacman.py b/tests/gems/arch/test_pacman.py
index 283746f3..1e62f6f4 100644
--- a/tests/gems/arch/test_pacman.py
+++ b/tests/gems/arch/test_pacman.py
@@ -3,7 +3,7 @@ import warnings
from unittest import TestCase
from unittest.mock import patch, Mock
-from bauh import __app_name__
+
from bauh.gems.arch import pacman
FILE_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -29,7 +29,7 @@ class PacmanTest(TestCase):
self.assertIsNotNone(ignored)
self.assertEqual(0, len(ignored))
- @patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
+ @patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -42,7 +42,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {}}, res)
- @patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
+ @patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -55,7 +55,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {'lib32-vulkan-icd-loader': 'Vulkan support'}}, res)
- @patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
+ @patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -68,7 +68,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {'pipewire-alsa': ''}}, res)
- @patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
+ @patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
@@ -81,7 +81,7 @@ Required By : None
run_cmd.assert_called_once_with('pacman -Qi package-test')
self.assertEqual({'package-test': {}}, res)
- @patch(f'{__app_name__}.gems.arch.pacman.run_cmd', return_value="""
+ @patch("bauh.gems.arch.pacman.run_cmd", return_value="""
Name : package-test
Version : 3.4.4-1
Description : Test
diff --git a/tests/gems/arch/test_updates.py b/tests/gems/arch/test_updates.py
index 0f209d60..4e9e6a3a 100644
--- a/tests/gems/arch/test_updates.py
+++ b/tests/gems/arch/test_updates.py
@@ -1,7 +1,6 @@
from unittest import TestCase
from unittest.mock import patch, Mock, MagicMock
-from bauh import __app_name__
from bauh.api.abstract.controller import UpgradeRequirement
from bauh.gems.arch.dependencies import DependenciesAnalyser
from bauh.gems.arch.model import ArchPackage
@@ -28,7 +27,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
"prefer_repository_provider": True,
"check_dependency_breakage": True}
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_installed_version_fails(self, pacman: Mock):
"""
If the newest version o package A conflicts with an installed package named B, but the conflict expression
@@ -76,7 +75,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_to_remove_when_conflict_with_installed_version_matches(self, pacman: Mock):
"""
If the newest version o package A conflicts with an installed package named B and the conflict expression
@@ -130,7 +129,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_to_remove_when_conflict_with_installed_matches(self, pacman: Mock):
"""
If the newest version o package A conflicts with an installed package named B,
@@ -184,7 +183,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_is_self_conflict(self, pacman: Mock):
"""
If the newest version o package A conflicts with itself, then A should not be marked as a package to be removed.
@@ -226,7 +225,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_provided_version_fails(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by installed package B),
@@ -273,7 +272,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__not_return_to_remove_when_conflict_with_provided_virtual_package_with_version_fails(self, pacman: Mock):
"""
Scenario:
@@ -325,7 +324,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_installed_virtual_with_defined_version_to_remove_when_conflict_version_matches(self, pacman: Mock):
"""
Scenario:
@@ -390,7 +389,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="X", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'V'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_installed_virtual_with_defined_version_to_remove_when_conflict_matches__case_2(self, pacman: Mock):
"""
This test case covers the same scenario as the above, but adds an additional provider for the same
@@ -466,7 +465,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="X", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'V'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_package_to_remove_when_conflict_with_provided_matches_version(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by installed package B),
@@ -523,7 +522,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_return_installed_package_to_remove_when_conflict_with_provided_matches(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by installed package B),
@@ -579,7 +578,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
pkg_b = ArchPackage(name="B", installed=True, i18n=self.i18n)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__should_not_return_installed_to_remove_when_conflict_with_provider_is_self_conflict(self, pacman: Mock):
"""
If the newest version o package A conflicts with a provided package C (by A itself),
@@ -622,7 +621,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertFalse(res.to_install)
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_several_to_upgrade_conflict_with_each_other(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -682,7 +681,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason=" 'B'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason=" 'A'"), res.cannot_upgrade)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_several_to_upgrade_conflict_with_provided_by_each_other(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -744,7 +743,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason=" 'B'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason=" 'A'"), res.cannot_upgrade)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_a_package_to_upgrade_conflicts_with_another(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -806,7 +805,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_a_package_to_upgrade_conflicts_with_provided_by_another(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -871,7 +870,7 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_a_package_to_upgrade_conflicts_with_provided_version_by_another(self, pacman: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -937,8 +936,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, reason=" 'A'", extra_size=1)], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.dependencies.pacman")
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.dependencies.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_several_packages_to_install_conflict_with_each_other(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1039,8 +1038,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason="'C' 'D'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason="'C' 'D'"), res.cannot_upgrade)
- @patch(f"{__app_name__}.gems.arch.dependencies.pacman")
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.dependencies.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_cannot_upgrade_when_packages_to_install_conflict_with_version_of_each_other(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1141,8 +1140,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertIn(UpgradeRequirement(pkg=pkg_a, reason="'C' 'D'"), res.cannot_upgrade)
self.assertIn(UpgradeRequirement(pkg=pkg_b, reason="'C' 'D'"), res.cannot_upgrade)
- @patch(f"{__app_name__}.gems.arch.dependencies.pacman")
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.dependencies.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_to_update_conflicts_with_to_install(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1228,8 +1227,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, extra_size=1, reason=" 'C'")], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.dependencies.pacman")
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.dependencies.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_to_update_conflicts_with_specific_version_of_install(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
@@ -1315,8 +1314,8 @@ class UpdatesSummarizerGetUpgradeRequirementsTest(TestCase):
self.assertEqual([UpgradeRequirement(pkg=pkg_a, required_size=1, extra_size=0)], res.to_upgrade)
self.assertEqual([UpgradeRequirement(pkg=pkg_b, extra_size=1, reason=" 'C'")], res.to_remove)
- @patch(f"{__app_name__}.gems.arch.dependencies.pacman")
- @patch(f"{__app_name__}.gems.arch.updates.pacman")
+ @patch("bauh.gems.arch.dependencies.pacman")
+ @patch("bauh.gems.arch.updates.pacman")
def test__return_as_to_remove_when_to_update_conflicts_with_to_install_and_it_has_deps(self, *mocks: Mock):
"""
Consider package A and B are selected to be upgraded:
diff --git a/tests/gems/flatpak/test_flatpak.py b/tests/gems/flatpak/test_flatpak.py
index b460b7c5..6e2a5582 100644
--- a/tests/gems/flatpak/test_flatpak.py
+++ b/tests/gems/flatpak/test_flatpak.py
@@ -1,14 +1,14 @@
from unittest import TestCase
from unittest.mock import patch, Mock
-from bauh import __app_name__
+
from bauh.gems.flatpak import flatpak, VERSION_1_2
class FlatpakTest(TestCase):
- @patch(f'{__app_name__}.gems.flatpak.flatpak.SimpleProcess')
- @patch(f'{__app_name__}.gems.flatpak.flatpak.ProcessHandler.handle_simple', return_value=(True, """
+ @patch("bauh.gems.flatpak.flatpak.SimpleProcess")
+ @patch("bauh.gems.flatpak.flatpak.ProcessHandler.handle_simple", return_value=(True, """
Looking for updates...
\tID\tArch\tBranch\tRemote\tDownload
diff --git a/tests/gems/web/test_controller.py b/tests/gems/web/test_controller.py
index 32ff603d..30ea5149 100644
--- a/tests/gems/web/test_controller.py
+++ b/tests/gems/web/test_controller.py
@@ -4,6 +4,8 @@ from unittest.mock import Mock, patch
from bauh.gems.web.controller import DEFAULT_LANGUAGE_HEADER
from bauh.gems.web.controller import WebApplicationManager
+LOCALE_TARGET = 'bauh.gems.web.controller.locale.getlocale'
+
class ControllerTest(TestCase):
@@ -16,35 +18,35 @@ class WebApplicationManagerTest(TestCase):
def setUp(self):
self.manager = WebApplicationManager(context=Mock())
- @patch('locale.getdefaultlocale', side_effect=Exception)
- def test_get_accept_language_header__must_return_default_locale_when_exception_raised(self, getdefaultlocale: Mock):
+ @patch(LOCALE_TARGET, side_effect=Exception)
+ def test_get_accept_language_header__must_return_default_locale_when_exception_raised(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(DEFAULT_LANGUAGE_HEADER, returned)
- getdefaultlocale.assert_called_once()
+ getlocale.assert_called_once()
- @patch('locale.getdefaultlocale', return_value=None)
- def test_get_accept_language_header__must_return_default_locale_when_no_locale_is_returned(self, getdefaultlocale: Mock):
+ @patch(LOCALE_TARGET, return_value=(None, None))
+ def test_get_accept_language_header__must_return_default_locale_when_no_locale_is_returned(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(DEFAULT_LANGUAGE_HEADER, returned)
- getdefaultlocale.assert_called_once()
+ getlocale.assert_called_once()
- @patch('locale.getdefaultlocale', return_value=['es_AR'])
- def test_get_accept_language_header__must_return_the_system_locale_without_underscore_plus_default_locale(self, getdefaultlocale: Mock):
+ @patch(LOCALE_TARGET, return_value=('es_AR', 'UTF-8'))
+ def test_get_accept_language_header__must_return_the_system_locale_without_underscore_plus_default_locale(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(f'es-AR, es, {DEFAULT_LANGUAGE_HEADER}', returned)
- getdefaultlocale.assert_called_once()
+ getlocale.assert_called_once()
- @patch('locale.getdefaultlocale', return_value=['es'])
- def test_get_accept_language_header__must_return_the_simple_system_locale_plus_default_locale(self, getdefaultlocale: Mock):
+ @patch(LOCALE_TARGET, return_value=('es', 'UTF-8'))
+ def test_get_accept_language_header__must_return_the_simple_system_locale_plus_default_locale(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
self.assertEqual(f'es, {DEFAULT_LANGUAGE_HEADER}', returned)
- getdefaultlocale.assert_called_once()
+ getlocale.assert_called_once()
- @patch('locale.getdefaultlocale', return_value=['en_IN'])
- def test_get_accept_language_header__must_not_concatenate_default_locale_if_system_locale_has_it(self, getdefaultlocale: Mock):
+ @patch(LOCALE_TARGET, return_value=('en_IN', 'UTF-8'))
+ def test_get_accept_language_header__must_not_concatenate_default_locale_if_system_locale_has_it(self, getlocale: Mock):
returned = self.manager.get_accept_language_header()
- self.assertEqual(f'en-IN, en', returned)
- getdefaultlocale.assert_called_once()
+ self.assertEqual('en-IN, en', returned)
+ getlocale.assert_called_once()
def test_strip_url_protocol__http_no_www(self):
res = self.manager.strip_url_protocol('http://test.com')
@@ -60,4 +62,4 @@ class WebApplicationManagerTest(TestCase):
def test_strip_url_protocol__https_with_www(self):
res = self.manager.strip_url_protocol('https://www.test.com')
- self.assertEqual('test.com', res)
+ self.assertEqual('test.com', res)
\ No newline at end of file