up-tray 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- up_tray-0.2.0/.gitignore +39 -0
- up_tray-0.2.0/LICENSE +17 -0
- up_tray-0.2.0/PKG-INFO +73 -0
- up_tray-0.2.0/README.md +45 -0
- up_tray-0.2.0/packaging/up-tray.desktop +12 -0
- up_tray-0.2.0/packaging/up-tray.spec +97 -0
- up_tray-0.2.0/pyproject.toml +79 -0
- up_tray-0.2.0/resources/up-logo.png +0 -0
- up_tray-0.2.0/up_tray/__init__.py +2 -0
- up_tray-0.2.0/up_tray/__main__.py +216 -0
- up_tray-0.2.0/up_tray/backend/__init__.py +0 -0
- up_tray-0.2.0/up_tray/backend/auth.py +351 -0
- up_tray-0.2.0/up_tray/backend/autocomplete.py +83 -0
- up_tray-0.2.0/up_tray/backend/cloud.py +486 -0
- up_tray-0.2.0/up_tray/backend/composition.py +306 -0
- up_tray-0.2.0/up_tray/backend/crd.py +154 -0
- up_tray-0.2.0/up_tray/backend/crossplane.py +546 -0
- up_tray-0.2.0/up_tray/backend/kubeconfig.py +146 -0
- up_tray-0.2.0/up_tray/backend/marketplace.py +162 -0
- up_tray-0.2.0/up_tray/backend/modelplane.py +208 -0
- up_tray-0.2.0/up_tray/backend/monitor.py +153 -0
- up_tray-0.2.0/up_tray/backend/profile.py +242 -0
- up_tray-0.2.0/up_tray/backend/resources.py +210 -0
- up_tray-0.2.0/up_tray/backend/triage.py +352 -0
- up_tray-0.2.0/up_tray/backend/worker.py +924 -0
- up_tray-0.2.0/up_tray/backend/xpkg.py +291 -0
- up_tray-0.2.0/up_tray/i18n.py +190 -0
- up_tray-0.2.0/up_tray/proto/__init__.py +12 -0
- up_tray-0.2.0/up_tray/translations/de.json +320 -0
- up_tray-0.2.0/up_tray/translations/es.json +320 -0
- up_tray-0.2.0/up_tray/translations/fr.json +320 -0
- up_tray-0.2.0/up_tray/translations/ja.json +320 -0
- up_tray-0.2.0/up_tray/translations/zh_CN.json +320 -0
- up_tray-0.2.0/up_tray/ui/__init__.py +0 -0
- up_tray-0.2.0/up_tray/ui/dialogs.py +108 -0
- up_tray-0.2.0/up_tray/ui/login_dialog.py +119 -0
- up_tray-0.2.0/up_tray/ui/openapi_form.py +1008 -0
- up_tray-0.2.0/up_tray/ui/tray.py +3130 -0
- up_tray-0.2.0/up_tray/ui/tutorial_dialog.py +1109 -0
up_tray-0.2.0/.gitignore
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
.eggs/
|
|
6
|
+
build/
|
|
7
|
+
dist/
|
|
8
|
+
*.so
|
|
9
|
+
|
|
10
|
+
# Virtual envs
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
env/
|
|
14
|
+
|
|
15
|
+
# Editor / IDE
|
|
16
|
+
.vscode/
|
|
17
|
+
.idea/
|
|
18
|
+
*.swp
|
|
19
|
+
*.swo
|
|
20
|
+
|
|
21
|
+
# OS / desktop
|
|
22
|
+
.DS_Store
|
|
23
|
+
Thumbs.db
|
|
24
|
+
|
|
25
|
+
# Claude Code session settings
|
|
26
|
+
.claude/
|
|
27
|
+
|
|
28
|
+
# Local config that mirrors ~/.up
|
|
29
|
+
.up/
|
|
30
|
+
|
|
31
|
+
# Build outputs from build-srpm.sh
|
|
32
|
+
*.rpm
|
|
33
|
+
*.srpm
|
|
34
|
+
SOURCES/
|
|
35
|
+
RPMS/
|
|
36
|
+
SRPMS/
|
|
37
|
+
BUILD/
|
|
38
|
+
BUILDROOT/
|
|
39
|
+
*.tar.gz
|
up_tray-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
|
16
|
+
|
|
17
|
+
Copyright 2026 Johnny Boero
|
up_tray-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: up-tray
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Qt6 system-tray replacement for Upbound's up CLI
|
|
5
|
+
Project-URL: Homepage, https://github.com/upbound/up-tray
|
|
6
|
+
Project-URL: Repository, https://github.com/upbound/up-tray
|
|
7
|
+
Project-URL: Issues, https://github.com/upbound/up-tray/issues
|
|
8
|
+
Author: Johnny Boero
|
|
9
|
+
License-Expression: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: X11 Applications :: Qt
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Topic :: System :: Systems Administration
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Requires-Dist: keyring>=24.0
|
|
19
|
+
Requires-Dist: kubernetes>=29.0
|
|
20
|
+
Requires-Dist: pyqt6>=6.5
|
|
21
|
+
Requires-Dist: pyyaml>=6.0
|
|
22
|
+
Requires-Dist: requests>=2.31
|
|
23
|
+
Provides-Extra: render
|
|
24
|
+
Requires-Dist: docker>=7.0; extra == 'render'
|
|
25
|
+
Requires-Dist: grpcio>=1.60; extra == 'render'
|
|
26
|
+
Requires-Dist: protobuf>=4.25; extra == 'render'
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# up-tray
|
|
30
|
+
|
|
31
|
+
A Qt6 system-tray app that replaces Upbound's `up` CLI without the 250 MB Go binary.
|
|
32
|
+
|
|
33
|
+
Talks directly to `api.upbound.io` for cloud ops and to your Spaces clusters via
|
|
34
|
+
the Kubernetes API. Composition render is supported either natively (Docker +
|
|
35
|
+
gRPC, requires `[render]` extras and proto stubs) or by falling through to the
|
|
36
|
+
upstream `crossplane` CLI when present.
|
|
37
|
+
|
|
38
|
+
> **Open source, use at your own risk.** up-tray is a community, open-source
|
|
39
|
+
> project licensed under Apache-2.0. It is provided "as is", without warranty
|
|
40
|
+
> of any kind; you assume all risk from its use. It is not an official Upbound
|
|
41
|
+
> product and is not covered by any Upbound support agreement.
|
|
42
|
+
|
|
43
|
+
## Running it
|
|
44
|
+
|
|
45
|
+
[up-tray.webm](https://github.com/user-attachments/assets/8b994faf-a9c0-4bcd-8a68-04e073e1e571)
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
./run.sh # system Python + dnf-installed deps. If anything's
|
|
49
|
+
# missing it prints the dnf line and exits.
|
|
50
|
+
./run.sh --install # dnf install missing deps, then run
|
|
51
|
+
./run.sh --venv # force a local venv (for code iteration or non-Fedora)
|
|
52
|
+
./run.sh --help # the rest of the flags
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For installed-on-disk daily use, build the RPM:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
./build-srpm.sh
|
|
59
|
+
rpmbuild --rebuild build/srpm/up-tray-*.src.rpm
|
|
60
|
+
sudo dnf install ~/rpmbuild/RPMS/noarch/up-tray-*.rpm
|
|
61
|
+
up-tray &
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The RPM is noarch and depends on `python3-pyqt6`, `python3-requests`,
|
|
65
|
+
`python3-pyyaml`, `python3-kubernetes`, with `python3-keyring` recommended.
|
|
66
|
+
No venv anywhere on the install path.
|
|
67
|
+
|
|
68
|
+
## Status
|
|
69
|
+
|
|
70
|
+
Alpha. Cloud, Spaces, kubeconfig, and xpkg paths are wired natively against
|
|
71
|
+
the Upbound REST API and the Kubernetes Python client. Composition render
|
|
72
|
+
currently shells to `crossplane` until proto stubs are compiled into the
|
|
73
|
+
package.
|
up_tray-0.2.0/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# up-tray
|
|
2
|
+
|
|
3
|
+
A Qt6 system-tray app that replaces Upbound's `up` CLI without the 250 MB Go binary.
|
|
4
|
+
|
|
5
|
+
Talks directly to `api.upbound.io` for cloud ops and to your Spaces clusters via
|
|
6
|
+
the Kubernetes API. Composition render is supported either natively (Docker +
|
|
7
|
+
gRPC, requires `[render]` extras and proto stubs) or by falling through to the
|
|
8
|
+
upstream `crossplane` CLI when present.
|
|
9
|
+
|
|
10
|
+
> **Open source, use at your own risk.** up-tray is a community, open-source
|
|
11
|
+
> project licensed under Apache-2.0. It is provided "as is", without warranty
|
|
12
|
+
> of any kind; you assume all risk from its use. It is not an official Upbound
|
|
13
|
+
> product and is not covered by any Upbound support agreement.
|
|
14
|
+
|
|
15
|
+
## Running it
|
|
16
|
+
|
|
17
|
+
[up-tray.webm](https://github.com/user-attachments/assets/8b994faf-a9c0-4bcd-8a68-04e073e1e571)
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
./run.sh # system Python + dnf-installed deps. If anything's
|
|
21
|
+
# missing it prints the dnf line and exits.
|
|
22
|
+
./run.sh --install # dnf install missing deps, then run
|
|
23
|
+
./run.sh --venv # force a local venv (for code iteration or non-Fedora)
|
|
24
|
+
./run.sh --help # the rest of the flags
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
For installed-on-disk daily use, build the RPM:
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
./build-srpm.sh
|
|
31
|
+
rpmbuild --rebuild build/srpm/up-tray-*.src.rpm
|
|
32
|
+
sudo dnf install ~/rpmbuild/RPMS/noarch/up-tray-*.rpm
|
|
33
|
+
up-tray &
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The RPM is noarch and depends on `python3-pyqt6`, `python3-requests`,
|
|
37
|
+
`python3-pyyaml`, `python3-kubernetes`, with `python3-keyring` recommended.
|
|
38
|
+
No venv anywhere on the install path.
|
|
39
|
+
|
|
40
|
+
## Status
|
|
41
|
+
|
|
42
|
+
Alpha. Cloud, Spaces, kubeconfig, and xpkg paths are wired natively against
|
|
43
|
+
the Upbound REST API and the Kubernetes Python client. Composition render
|
|
44
|
+
currently shells to `crossplane` until proto stubs are compiled into the
|
|
45
|
+
package.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
[Desktop Entry]
|
|
2
|
+
Type=Application
|
|
3
|
+
Name=up-tray
|
|
4
|
+
GenericName=Upbound Tray
|
|
5
|
+
Comment=System tray controller for Upbound Cloud and Spaces
|
|
6
|
+
Exec=up-tray
|
|
7
|
+
Icon=up-tray
|
|
8
|
+
Terminal=false
|
|
9
|
+
Categories=Development;System;Network;
|
|
10
|
+
Keywords=upbound;crossplane;kubernetes;controlplane;
|
|
11
|
+
StartupNotify=false
|
|
12
|
+
X-GNOME-Autostart-enabled=true
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
%global pkgname up-tray
|
|
2
|
+
%global srcname up_tray
|
|
3
|
+
|
|
4
|
+
Name: %{pkgname}
|
|
5
|
+
Version: 0.2.0
|
|
6
|
+
Release: 2%{?dist}
|
|
7
|
+
Summary: Qt6 system-tray replacement for Upbound's up CLI
|
|
8
|
+
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
URL: https://github.com/jboero/up-tray
|
|
11
|
+
Source0: %{pkgname}-%{version}.tar.gz
|
|
12
|
+
|
|
13
|
+
BuildArch: noarch
|
|
14
|
+
|
|
15
|
+
BuildRequires: python3-devel
|
|
16
|
+
BuildRequires: python3-hatchling
|
|
17
|
+
BuildRequires: pyproject-rpm-macros
|
|
18
|
+
BuildRequires: desktop-file-utils
|
|
19
|
+
|
|
20
|
+
Requires: python3
|
|
21
|
+
Requires: python3-pyqt6
|
|
22
|
+
Requires: python3-requests
|
|
23
|
+
Requires: python3-pyyaml
|
|
24
|
+
Requires: python3-kubernetes
|
|
25
|
+
Requires: hicolor-icon-theme
|
|
26
|
+
Recommends: python3-keyring
|
|
27
|
+
Suggests: python3-grpcio
|
|
28
|
+
Suggests: python3-protobuf
|
|
29
|
+
Suggests: python3-docker
|
|
30
|
+
|
|
31
|
+
%description
|
|
32
|
+
up-tray is a small native PyQt6 system tray application that replicates the
|
|
33
|
+
features of Upbound's `up` CLI without bundling a 250 MB Go binary. It talks
|
|
34
|
+
directly to api.upbound.io for cloud operations and to the Kubernetes API on
|
|
35
|
+
your Spaces clusters for Crossplane operations.
|
|
36
|
+
|
|
37
|
+
%prep
|
|
38
|
+
%autosetup -n %{pkgname}-%{version}
|
|
39
|
+
|
|
40
|
+
%generate_buildrequires
|
|
41
|
+
# -R: don't pull the project's runtime deps (PyQt6, kubernetes, ...) into the
|
|
42
|
+
# buildroot. This is a noarch package and %check only does `import up_tray`
|
|
43
|
+
# (which imports no GUI code), so the GUI stack need not be present to build.
|
|
44
|
+
# Without -R the build fails on chroots that don't package PyQt6 (e.g. Amazon
|
|
45
|
+
# Linux 2023, EL9). The runtime deps are still enforced via explicit Requires:.
|
|
46
|
+
%pyproject_buildrequires -R
|
|
47
|
+
|
|
48
|
+
%build
|
|
49
|
+
%pyproject_wheel
|
|
50
|
+
|
|
51
|
+
%install
|
|
52
|
+
%pyproject_install
|
|
53
|
+
%pyproject_save_files %{srcname}
|
|
54
|
+
|
|
55
|
+
install -Dm0644 packaging/up-tray.desktop \
|
|
56
|
+
%{buildroot}%{_datadir}/applications/up-tray.desktop
|
|
57
|
+
install -Dm0644 resources/up-logo.png \
|
|
58
|
+
%{buildroot}%{_datadir}/icons/hicolor/64x64/apps/up-tray.png
|
|
59
|
+
|
|
60
|
+
desktop-file-validate %{buildroot}%{_datadir}/applications/up-tray.desktop
|
|
61
|
+
|
|
62
|
+
%check
|
|
63
|
+
%{python3} -c "import up_tray; print(up_tray.__version__)"
|
|
64
|
+
|
|
65
|
+
%files -f %{pyproject_files}
|
|
66
|
+
%license LICENSE
|
|
67
|
+
%doc README.md
|
|
68
|
+
%{_bindir}/up-tray
|
|
69
|
+
%{_bindir}/up-tray-gui
|
|
70
|
+
%{_datadir}/applications/up-tray.desktop
|
|
71
|
+
%{_datadir}/icons/hicolor/64x64/apps/up-tray.png
|
|
72
|
+
|
|
73
|
+
%post
|
|
74
|
+
/bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null || :
|
|
75
|
+
|
|
76
|
+
%postun
|
|
77
|
+
if [ $1 -eq 0 ] ; then
|
|
78
|
+
/bin/touch --no-create %{_datadir}/icons/hicolor &>/dev/null || :
|
|
79
|
+
%{_bindir}/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
%posttrans
|
|
83
|
+
%{_bindir}/gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || :
|
|
84
|
+
|
|
85
|
+
%changelog
|
|
86
|
+
* Wed Jul 01 2026 Johnny Boero - 0.2.0-2
|
|
87
|
+
- Build on Python 3.9 chroots (EL9, Amazon Linux 2023): lower requires-python
|
|
88
|
+
to >=3.9 (code is already 3.9-compatible) and build with
|
|
89
|
+
%%pyproject_buildrequires -R so the GUI runtime stack is not required at
|
|
90
|
+
build time.
|
|
91
|
+
|
|
92
|
+
* Fri Jun 26 2026 Johnny Boero - 0.2.0-1
|
|
93
|
+
- Modelplane support: managed-resource inventory, health monitor, in-app triage.
|
|
94
|
+
- Fix blank tray icon in installed builds (bundle icon as package data).
|
|
95
|
+
|
|
96
|
+
* Tue Apr 28 2026 Johnny Boero - 0.1.0-1
|
|
97
|
+
- Initial package: native Qt6 tray, no `up` binary required.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "up-tray"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Qt6 system-tray replacement for Upbound's up CLI"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
authors = [{ name = "Johnny Boero" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: X11 Applications :: Qt",
|
|
16
|
+
"Intended Audience :: System Administrators",
|
|
17
|
+
"Operating System :: POSIX :: Linux",
|
|
18
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
19
|
+
"Topic :: System :: Systems Administration",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"PyQt6>=6.5",
|
|
23
|
+
"requests>=2.31",
|
|
24
|
+
"PyYAML>=6.0",
|
|
25
|
+
"kubernetes>=29.0",
|
|
26
|
+
"keyring>=24.0",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
render = [
|
|
31
|
+
"grpcio>=1.60",
|
|
32
|
+
"protobuf>=4.25",
|
|
33
|
+
"docker>=7.0",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://github.com/upbound/up-tray"
|
|
38
|
+
Repository = "https://github.com/upbound/up-tray"
|
|
39
|
+
Issues = "https://github.com/upbound/up-tray/issues"
|
|
40
|
+
|
|
41
|
+
[project.scripts]
|
|
42
|
+
up-tray = "up_tray.__main__:main"
|
|
43
|
+
|
|
44
|
+
[project.gui-scripts]
|
|
45
|
+
up-tray-gui = "up_tray.__main__:main"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["up_tray"]
|
|
49
|
+
|
|
50
|
+
# Bundle the tray icon *inside* the package so it sits next to the code at
|
|
51
|
+
# import time, no matter the install prefix. Without this the icon only lands
|
|
52
|
+
# in share/icons/hicolor (see shared-data below), which is not on the package
|
|
53
|
+
# path - so an installed build (e.g. the RPM) showed a blank tray slot while a
|
|
54
|
+
# source checkout worked because resources/ happened to be at the repo root.
|
|
55
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
56
|
+
"resources/up-logo.png" = "up_tray/resources/up-logo.png"
|
|
57
|
+
|
|
58
|
+
# Install the .desktop entry and the icon to standard XDG paths so the
|
|
59
|
+
# Wayland portal can find app metadata (titlebar icon, autostart hint,
|
|
60
|
+
# launcher entry). Without this Qt6 logs:
|
|
61
|
+
# "Failed to register with host portal ... App info not found for 'up-tray'"
|
|
62
|
+
# pip drops these under the install prefix's `share/`, which becomes:
|
|
63
|
+
# system pip: /usr/share/applications/up-tray.desktop
|
|
64
|
+
# --user pip: ~/.local/share/applications/up-tray.desktop
|
|
65
|
+
# venv pip: <venv>/share/applications/up-tray.desktop (also XDG-searched)
|
|
66
|
+
# RPM packaging installs them explicitly in the spec; the shared-data
|
|
67
|
+
# entry below is a no-op there.
|
|
68
|
+
[tool.hatch.build.targets.wheel.shared-data]
|
|
69
|
+
"packaging/up-tray.desktop" = "share/applications/up-tray.desktop"
|
|
70
|
+
"resources/up-logo.png" = "share/icons/hicolor/64x64/apps/up-tray.png"
|
|
71
|
+
|
|
72
|
+
[tool.hatch.build.targets.sdist]
|
|
73
|
+
include = [
|
|
74
|
+
"/up_tray",
|
|
75
|
+
"/resources",
|
|
76
|
+
"/packaging",
|
|
77
|
+
"/README.md",
|
|
78
|
+
"/LICENSE",
|
|
79
|
+
]
|
|
Binary file
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""Entry point for `up-tray`."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import os
|
|
6
|
+
import signal
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from PyQt6.QtCore import QLoggingCategory
|
|
10
|
+
from PyQt6.QtGui import QGuiApplication
|
|
11
|
+
from PyQt6.QtWidgets import QApplication, QMessageBox, QSystemTrayIcon
|
|
12
|
+
|
|
13
|
+
from .i18n import init_locale, tr
|
|
14
|
+
from .ui.tray import UpTray, _load_icon
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main() -> int:
|
|
18
|
+
parser = argparse.ArgumentParser(
|
|
19
|
+
prog="up-tray",
|
|
20
|
+
description="Qt6 system-tray app for Upbound Cloud + Crossplane.",
|
|
21
|
+
)
|
|
22
|
+
parser.add_argument(
|
|
23
|
+
"--self-test",
|
|
24
|
+
action="store_true",
|
|
25
|
+
help="Construct the app offscreen, walk the menu, exercise i18n and "
|
|
26
|
+
"backend imports, and exit. No network calls, no keyring writes, "
|
|
27
|
+
"no UI shown. Returns 0 if everything wired correctly. Useful "
|
|
28
|
+
"for CI matrix runs across Linux/macOS/Windows.",
|
|
29
|
+
)
|
|
30
|
+
args = parser.parse_args()
|
|
31
|
+
|
|
32
|
+
if args.self_test:
|
|
33
|
+
return _run_self_test()
|
|
34
|
+
|
|
35
|
+
# qt.qpa.services prints a warning on Wayland when the XDG portal
|
|
36
|
+
# can't find up-tray.desktop (i.e. when running from a source
|
|
37
|
+
# checkout before `pip install` has dropped it under share/). The
|
|
38
|
+
# warning is purely cosmetic — every code path falls back to a
|
|
39
|
+
# generic icon and continues working — but it clutters stderr.
|
|
40
|
+
# Filter just that one category; everything else still surfaces.
|
|
41
|
+
QLoggingCategory.setFilterRules("qt.qpa.services.warning=false")
|
|
42
|
+
|
|
43
|
+
app = QApplication(sys.argv)
|
|
44
|
+
app.setApplicationName("up-tray")
|
|
45
|
+
app.setApplicationDisplayName("Upbound Tray")
|
|
46
|
+
app.setOrganizationName("Upbound")
|
|
47
|
+
app.setOrganizationDomain("upbound.io")
|
|
48
|
+
app.setQuitOnLastWindowClosed(False)
|
|
49
|
+
# On Wayland the compositor uses this to associate windows with the
|
|
50
|
+
# installed up-tray.desktop file (and therefore its declared icon).
|
|
51
|
+
# On X11 setWindowIcon below is enough; on Wayland both are needed.
|
|
52
|
+
QGuiApplication.setDesktopFileName("up-tray")
|
|
53
|
+
app.setWindowIcon(_load_icon())
|
|
54
|
+
|
|
55
|
+
# Resolve locale BEFORE any UI is built so all menus/dialogs come up
|
|
56
|
+
# in the user's language. Order: $UP_TRAY_LOCALE env var (for testing)
|
|
57
|
+
# → ~/.config/up-tray/settings.json → OS locale → English.
|
|
58
|
+
init_locale(os.environ.get("UP_TRAY_LOCALE"))
|
|
59
|
+
|
|
60
|
+
if not QSystemTrayIcon.isSystemTrayAvailable():
|
|
61
|
+
QMessageBox.critical(
|
|
62
|
+
None,
|
|
63
|
+
tr("Upbound"),
|
|
64
|
+
tr("No system tray detected. Most KDE Plasma sessions, "
|
|
65
|
+
"GNOME-with-AppIndicator, and XFCE work; pure GNOME Wayland "
|
|
66
|
+
"without the AppIndicator extension does not."),
|
|
67
|
+
)
|
|
68
|
+
return 1
|
|
69
|
+
|
|
70
|
+
tray = UpTray(app)
|
|
71
|
+
tray.show()
|
|
72
|
+
|
|
73
|
+
# Allow Ctrl-C to terminate when launched from a terminal
|
|
74
|
+
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
|
75
|
+
return app.exec()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _run_self_test() -> int:
|
|
79
|
+
"""
|
|
80
|
+
Smoke-test that exercises every code path that doesn't need the network.
|
|
81
|
+
|
|
82
|
+
Designed for `python -m up_tray --self-test` in CI matrix runs across
|
|
83
|
+
Linux/macOS/Windows. We construct the full UpTray under the offscreen
|
|
84
|
+
Qt platform, walk the menu, exercise every translation file, and
|
|
85
|
+
confirm the backend modules import cleanly. Anything that would
|
|
86
|
+
require a real cluster, a real Upbound session, or a writable keyring
|
|
87
|
+
is intentionally NOT touched.
|
|
88
|
+
|
|
89
|
+
Output is one PASS/FAIL line per check, summary at the end. Exit 0
|
|
90
|
+
on all-pass.
|
|
91
|
+
"""
|
|
92
|
+
# Offscreen Qt platform plugin. Has to be set before QApplication
|
|
93
|
+
# is constructed.
|
|
94
|
+
os.environ.setdefault("QT_QPA_PLATFORM", "offscreen")
|
|
95
|
+
|
|
96
|
+
import traceback
|
|
97
|
+
failures: list[str] = []
|
|
98
|
+
def check(name: str, fn):
|
|
99
|
+
try:
|
|
100
|
+
fn()
|
|
101
|
+
print(f" PASS {name}")
|
|
102
|
+
except Exception as e:
|
|
103
|
+
failures.append(f"{name}: {e}")
|
|
104
|
+
print(f" FAIL {name}: {e}")
|
|
105
|
+
traceback.print_exc()
|
|
106
|
+
# Flush any pending Qt deleteLater() calls so a stale parent
|
|
107
|
+
# widget from the previous check can't interfere with the next.
|
|
108
|
+
# Without this, on Linux Qt the QLabels inside TutorialDialog
|
|
109
|
+
# ended up referencing a UpTray QApplication state that was
|
|
110
|
+
# mid-teardown, giving "wrapped C/C++ object has been deleted".
|
|
111
|
+
try:
|
|
112
|
+
from PyQt6.QtWidgets import QApplication
|
|
113
|
+
inst = QApplication.instance()
|
|
114
|
+
if inst is not None:
|
|
115
|
+
inst.processEvents()
|
|
116
|
+
inst.sendPostedEvents(None, 0) # type: ignore[arg-type]
|
|
117
|
+
except Exception:
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
print("up-tray self-test")
|
|
121
|
+
print("-----------------")
|
|
122
|
+
|
|
123
|
+
# 1. Backend modules import cleanly. Catches missing deps fast.
|
|
124
|
+
def _imports():
|
|
125
|
+
from .backend import auth, cloud, composition, crossplane, kubeconfig, marketplace, profile, worker, xpkg # noqa: F401
|
|
126
|
+
check("backend modules import", _imports)
|
|
127
|
+
|
|
128
|
+
# 2. UI modules import cleanly.
|
|
129
|
+
def _ui_imports():
|
|
130
|
+
from .ui import dialogs, login_dialog, tray, tutorial_dialog # noqa: F401
|
|
131
|
+
check("UI modules import", _ui_imports)
|
|
132
|
+
|
|
133
|
+
# 3. Every translation file parses and resolves a known sentinel.
|
|
134
|
+
def _translations():
|
|
135
|
+
from .i18n import LOCALES, init_locale, tr
|
|
136
|
+
for code in LOCALES:
|
|
137
|
+
init_locale(code)
|
|
138
|
+
# "Sign in to Upbound" exists in every locale we ship.
|
|
139
|
+
v = tr("Sign in to Upbound")
|
|
140
|
+
assert isinstance(v, str) and v, f"empty translation for {code}"
|
|
141
|
+
init_locale("en")
|
|
142
|
+
check("translations load (en/es/fr/de/zh_CN/ja)", _translations)
|
|
143
|
+
|
|
144
|
+
# 4. Keyring backend resolves without error (don't touch it though).
|
|
145
|
+
def _keyring():
|
|
146
|
+
from .backend.profile import keyring_status
|
|
147
|
+
s = keyring_status()
|
|
148
|
+
assert "friendly_name" in s
|
|
149
|
+
check("keyring backend resolvable", _keyring)
|
|
150
|
+
|
|
151
|
+
# 5. QApplication + UpTray construct without error. Walks the
|
|
152
|
+
# whole menu hierarchy as a side effect of construction.
|
|
153
|
+
#
|
|
154
|
+
# Lifecycle: UpTray spins up a QThread for the worker. We MUST
|
|
155
|
+
# quit() and wait() that thread before letting `t` go out of
|
|
156
|
+
# scope, otherwise on Linux Qt aborts with "QThread: Destroyed
|
|
157
|
+
# while thread is still running" (SIGABRT, exit 134). macOS and
|
|
158
|
+
# Windows are more forgiving but it's still incorrect.
|
|
159
|
+
def _construct():
|
|
160
|
+
app = QApplication.instance() or QApplication(sys.argv[:1])
|
|
161
|
+
from .ui.tray import UpTray
|
|
162
|
+
t = UpTray(app)
|
|
163
|
+
try:
|
|
164
|
+
labels = [a.text() for a in t.menu.actions() if a.menu()]
|
|
165
|
+
expected_substrings = ["Profile", "Context", "Cloud", "Crossplane", "xpkg", "Settings"]
|
|
166
|
+
for needle in expected_substrings:
|
|
167
|
+
assert any(needle in l for l in labels), f"missing top-level menu containing {needle!r}; got {labels}"
|
|
168
|
+
for sig in ("begin_browser_login", "list_organizations", "tutorial_check_state"):
|
|
169
|
+
assert hasattr(t.req, sig), f"_Requests missing signal: {sig}"
|
|
170
|
+
finally:
|
|
171
|
+
t.thread.quit()
|
|
172
|
+
if not t.thread.wait(2000):
|
|
173
|
+
t.thread.terminate()
|
|
174
|
+
t.thread.wait(500)
|
|
175
|
+
t.deleteLater()
|
|
176
|
+
check("UpTray constructs and menu wired", _construct)
|
|
177
|
+
|
|
178
|
+
# 6. Tutorial paths declared correctly. We deliberately do NOT
|
|
179
|
+
# instantiate TutorialDialog here: on the bare Ubuntu CI runner
|
|
180
|
+
# (no D-Bus session, fail keyring backend) constructing the
|
|
181
|
+
# dialog after UpTray's teardown raises "wrapped C/C++ object of
|
|
182
|
+
# type QLabel has been deleted" — local desktop Linux + macOS +
|
|
183
|
+
# Windows all repro fine. The "UI modules import" check (#2)
|
|
184
|
+
# already proves the dialog code parses; this asserts the data
|
|
185
|
+
# contract that actually matters for tutorial behavior.
|
|
186
|
+
# Interactive testing covers QWidget construction.
|
|
187
|
+
def _tutorial():
|
|
188
|
+
from .ui.tutorial_dialog import PATHS
|
|
189
|
+
assert "kubernetes" in PATHS, f"missing kubernetes path; got {list(PATHS)}"
|
|
190
|
+
assert "aws" in PATHS, f"missing aws path; got {list(PATHS)}"
|
|
191
|
+
for pid, p in PATHS.items():
|
|
192
|
+
assert p.provider_repo_key, f"{pid}: empty provider_repo_key"
|
|
193
|
+
assert p.resource_kind, f"{pid}: empty resource_kind"
|
|
194
|
+
assert p.pc_versions, f"{pid}: empty pc_versions"
|
|
195
|
+
check("tutorial paths declared", _tutorial)
|
|
196
|
+
|
|
197
|
+
# 7. POSIX file perms helper is callable without crashing on the
|
|
198
|
+
# current platform (no-op on Windows).
|
|
199
|
+
def _perms():
|
|
200
|
+
from .i18n import _write_settings, _read_settings
|
|
201
|
+
# Read-only probe — don't actually overwrite the user's prefs.
|
|
202
|
+
_read_settings()
|
|
203
|
+
check("settings read", _perms)
|
|
204
|
+
|
|
205
|
+
print()
|
|
206
|
+
if failures:
|
|
207
|
+
print(f"{len(failures)} FAILURE(S):")
|
|
208
|
+
for f in failures:
|
|
209
|
+
print(f" {f}")
|
|
210
|
+
return 1
|
|
211
|
+
print("ALL CHECKS PASSED")
|
|
212
|
+
return 0
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
if __name__ == "__main__":
|
|
216
|
+
sys.exit(main())
|
|
File without changes
|