Skip to content

Commit 9cb41fb

Browse files
committed
fix(flet pack): take the Linux identity from --bundle-id when given
The identity was the executable's name, which reads well in a dock but disagrees with what `flet build` uses. After #2269 a built app's WM_CLASS, StartupWMClass and Icon= are all its bundle_id, so an app published both ways would have needed two different desktop entries -- and moving between the two would silently stop matching, which is the trap the reporter of #5422 already hit once. `--bundle-id` now decides it, falling back to the executable name when it is not given. That keeps the readable default for the many apps that never pass one, converges with `flet build` for those that do, and finally makes --bundle-id mean something outside macOS, where it only ever reached --osx-bundle-identifier. The id is chosen at pack time but applied at launch, so it is carried into the bundle as a data file and read back by the runtime hook -- only when --bundle-id was given, since the fallback needs nothing bundled. Exercised all four resolution paths: bundled id, no bundled id, an empty bundled file, and an executable with an extension.
1 parent e61728a commit 9cb41fb

5 files changed

Lines changed: 43 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535

3636
### Bug fixes
3737

38-
* Fix Linux apps packaged with `flet pack` appearing in the taskbar as "flet", grouped together with every other Flet app and unable to carry an icon. `flet pack` runs the shared prebuilt client binary, and the Linux desktop keys a window's identity on its X11 `WM_CLASS` or Wayland `app_id` — both of which GTK derives from the client's `argv[0]`, so every packed app inherited that binary's own name. The client is now launched under the app's own identity instead, taken from the new `FLET_APP_ID` environment variable that the PyInstaller runtime hook sets to the executable's name; the binary is unchanged, so this needs no client rebuild and works with clients already cached. Because the desktop resolves an app's display name and icon from an installed desktop entry matched through `StartupWMClass`, [Linux taskbar identity](https://flet.dev/docs/publish/using-pyinstaller#linux-taskbar-identity) documents how to install one — and `--icon`'s claim to work on Linux, which it never did, is corrected in both the docs and the CLI help ([#5422](https://github.com/flet-dev/flet/issues/5422)) by @ndonkoHenri.
38+
* Fix Linux apps packaged with `flet pack` appearing in the taskbar as "flet", grouped together with every other Flet app and unable to carry an icon. `flet pack` runs the shared prebuilt client binary, and the Linux desktop keys a window's identity on its X11 `WM_CLASS` or Wayland `app_id` — both of which GTK derives from the client's `argv[0]`, so every packed app inherited that binary's own name. The client is now launched under the app's own identity instead, taken from the new `FLET_APP_ID` environment variable that the PyInstaller runtime hook sets to `--bundle-id` when one is given and to the executable's name otherwise — so an app published both ways can carry one identity, and a single desktop entry matches whichever build a user installs; the binary is unchanged, so this needs no client rebuild and works with clients already cached. Because the desktop resolves an app's display name and icon from an installed desktop entry matched through `StartupWMClass`, [Linux taskbar identity](https://flet.dev/docs/publish/using-pyinstaller#linux-taskbar-identity) documents how to install one — and `--icon`'s claim to work on Linux, which it never did, is corrected in both the docs and the CLI help ([#5422](https://github.com/flet-dev/flet/issues/5422)) by @ndonkoHenri.
3939
* Fix Windows apps packaged with `flet pack` showing a second taskbar identity named "Flet description", whose right-click entry and pin launch a blank Flet client window instead of the app. Two defects stacked up: the PyInstaller runtime hook carrying the AppUserModelID fix from [#6403](https://github.com/flet-dev/flet/pull/6403) was never bundled into packed apps (its `rthooks.dat` manifest was missing from the `flet-cli` wheel, and PyInstaller skips a missing manifest silently), and a process-level AppUserModelID only fixes taskbar *grouping* anyway — the taskbar name, icon and pin target resolve through the shell's relaunch properties, which were never set, so they fell back to the cached `flet.exe`. The wheel now ships the manifest, and `flet_desktop` stamps `System.AppUserModel.ID`/`RelaunchCommand`/`RelaunchDisplayNameResource`/`RelaunchIconResource` on the client window right after launch (new `flet_desktop.win_taskbar` module, pure ctypes), driven by environment variables the runtime hook sets — and settable manually when packaging by other means, such as Nuitka. Apps started hidden (`AppView.FLET_APP_HIDDEN`) get their taskbar identity as well, and executable paths containing spaces or longer than 128 characters are supported ([#6767](https://github.com/flet-dev/flet/discussions/6767), [#6793](https://github.com/flet-dev/flet/pull/6793)) by @ndonkoHenri.
4040
* Fix `flet pack`-patched desktop clients and the vanilla client shadowing each other through the shared `~/.flet/client/` cache. The cache was keyed by flavor and version only, so a packaged app whose bundled client was patched with a custom icon and metadata could silently run whatever same-version client happened to be cached first — and vice versa. Bundled client archives are now content-fingerprinted and each distinct client gets its own cache directory. Client archives are built deterministically so rebuilds with unchanged content reuse the same cache entry, superseded entries unused for 30 days are garbage-collected (never touching a running app's client), and concurrent first-run extractions of the same archive no longer crash the losing process ([#6793](https://github.com/flet-dev/flet/pull/6793)) by @ndonkoHenri.
4141
* Fix `flet create --template extension` generating an example app whose `pyproject.toml` cannot be parsed on Windows. The `[tool.flet.dev_packages]` and `[tool.uv.sources]` entries that point back at the extension package interpolated the host's `os.sep` into a TOML basic string, so on Windows they rendered as `"..\.."` and `"..\..\"`: the first is an invalid escape sequence, and in the second the trailing `\"` escapes the closing quote and leaves the string unterminated — the "unbalanced quotes" error reporters hit before they could build or run the generated example. Both paths are now written with forward slashes, which need no escaping in TOML and which `pathlib` and `uv` accept on Windows just as they do elsewhere ([#5507](https://github.com/flet-dev/flet/issues/5507), [#6775](https://github.com/flet-dev/flet/pulls/6775)) by @ndonkoHenri.

sdk/python/packages/flet-cli/src/flet_cli/__pyinstaller/rthooks/pyi_rth_localhost_fletd.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@
3232
# own name -- so the app groups and labels as itself, and can be matched by
3333
# a desktop entry carrying StartupWMClass.
3434
if sys.platform.startswith("linux") and "FLET_APP_ID" not in os.environ:
35-
os.environ["FLET_APP_ID"] = os.path.splitext(
35+
# --bundle-id when it was given, so the identity matches what `flet build`
36+
# uses and what a desktop entry's StartupWMClass would name. Otherwise the
37+
# executable's own name, which reads better bare than a reverse-DNS id and
38+
# follows the binary if it is renamed.
39+
app_id = ""
40+
meipass = getattr(sys, "_MEIPASS", None)
41+
if meipass:
42+
try:
43+
with open(os.path.join(meipass, "flet_app_id"), encoding="utf-8") as f:
44+
app_id = f.read().strip()
45+
except OSError:
46+
pass
47+
os.environ["FLET_APP_ID"] = app_id or os.path.splitext(
3648
os.path.basename(os.path.abspath(sys.executable))
3749
)[0]

sdk/python/packages/flet-cli/src/flet_cli/commands/pack.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import shutil
66
import sys
77
import tarfile
8+
import tempfile
89
import zipfile
910
from pathlib import Path
1011

@@ -128,7 +129,10 @@ def add_arguments(self, parser: argparse.ArgumentParser) -> None:
128129
parser.add_argument(
129130
"--bundle-id",
130131
dest="bundle_id",
131-
help="Bundle identifier used for macOS app packaging",
132+
help="Bundle identifier for the app. Used for macOS app "
133+
"packaging, and on Linux as the app's taskbar identity — match it "
134+
"with the `StartupWMClass` key of your desktop entry. Defaults on "
135+
"Linux to the executable's name.",
132136
)
133137
parser.add_argument(
134138
"--debug-console",
@@ -293,6 +297,18 @@ def handle(self, options: argparse.Namespace) -> None:
293297
for add_data_arr in options.add_data:
294298
for add_data_item in add_data_arr:
295299
pyi_args.extend(["--add-data", add_data_item])
300+
301+
# The Linux taskbar identity is chosen here but applied at launch,
302+
# so carry it into the bundle for the runtime hook to read. Only
303+
# when asked for: without it the hook falls back to the
304+
# executable's name, which needs nothing bundled.
305+
if is_linux() and options.bundle_id:
306+
identity_dir = Path(tempfile.mkdtemp())
307+
identity_file = identity_dir.joinpath("flet_app_id")
308+
identity_file.write_text(options.bundle_id, encoding="utf-8")
309+
pyi_args.extend(
310+
["--add-data", f"{identity_file}{os.pathsep}."]
311+
)
296312
if options.add_binary:
297313
for add_binary_arr in options.add_binary:
298314
for add_binary_item in add_binary_arr:

website/docs/publish/using-pyinstaller.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,13 @@ from an installed [desktop entry](#linux-taskbar-identity) instead, so passing
110110
`flet pack` launches the shared, prebuilt Flet client, so without help every
111111
packed app reaches the taskbar under that binary's name — grouped together and
112112
labelled `flet`. To avoid that, the app is relaunched under its own identity
113-
(see [`FLET_APP_ID`](../reference/environment-variables.md#flet_app_id)),
114-
which defaults to the executable's name.
113+
(see [`FLET_APP_ID`](../reference/environment-variables.md#flet_app_id)):
114+
[`--bundle-id`](../cli/flet-pack.md#--bundle-id) when you pass one, and
115+
otherwise the executable's name.
116+
117+
Pass `--bundle-id` if you also publish the app with
118+
[`flet build linux`](linux.md), so both give the window the same identity and
119+
one desktop entry matches either.
115120

116121
That gives the app its own taskbar group and label. To also give it a **name of
117122
your choosing and an icon**, install a desktop entry — the desktop takes both
@@ -128,7 +133,8 @@ Categories=Utility;
128133
StartupWMClass=my-app
129134
```
130135

131-
`StartupWMClass` must match the executable's name, and `Icon=` names a PNG
136+
`StartupWMClass` must match the app's identity — the `--bundle-id` you passed,
137+
or the executable's name if you did not — and `Icon=` names a PNG
132138
installed into the icon theme, for example
133139
`~/.local/share/icons/hicolor/256x256/apps/my-app.png`. Refresh the caches
134140
afterwards:
@@ -138,7 +144,7 @@ update-desktop-database ~/.local/share/applications
138144
gtk-update-icon-cache -f -t ~/.local/share/icons/hicolor
139145
```
140146

141-
:::note[Or let `flet build` package it]
147+
:::tip[Or let `flet build` package it]
142148
[`flet build linux`](linux.md) compiles a runner per app, so the identity is
143149
built in rather than applied at launch. Reach for it if you would rather have
144150
packaging handled than assemble it yourself.

website/docs/reference/environment-variables.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ and icon from it — see
7676
[Linux taskbar identity](../publish/using-pyinstaller.md#linux-taskbar-identity).
7777

7878
Linux desktop apps only; set automatically by [`flet pack`](../cli/flet-pack.md)
79-
to the executable's name.
79+
to [`--bundle-id`](../cli/flet-pack.md#--bundle-id) when given, and otherwise to
80+
the executable's name.
8081

8182
### `FLET_APP_RELAUNCH_COMMAND`
8283

0 commit comments

Comments
 (0)