Skip to content

ci(appimage): build and bundle qtkeychain for SmartLink credential persistence (#3639)#3640

Open
ten9876 wants to merge 1 commit into
mainfrom
ci/appimage-qtkeychain
Open

ci(appimage): build and bundle qtkeychain for SmartLink credential persistence (#3639)#3640
ten9876 wants to merge 1 commit into
mainfrom
ci/appimage-qtkeychain

Conversation

@ten9876

@ten9876 ten9876 commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator

Problem

The Linux AppImage is built without Qt6Keychain, so HAVE_KEYCHAIN is undefined and SmartLink (and MQTT) credential persistence silently compiles out. The Auth0 refresh token is never stored, the email is remembered (it lives in AppSettings, not the keychain) but the password/login is not, and there's no log line explaining why.

The reporter confirmed a from-source build on the same Manjaro/KDE box stores and retrieves the credential through KDE Wallet — so the system wallet is fine. The gap is purely AppImage packaging. Fixes #3639.

This is the Linux counterpart to the Windows packaging fix in #3634.

Changes

  • scripts/setup/setup-qtkeychain.sh (new) — builds qtkeychain 0.16.0 from source against the active Qt and stages it in third_party/qtkeychain. Building from source (vs apt install qtkeychain-qt6-dev) guarantees an ABI match with the aqt Qt 6.8.3 the AppImage links. LIBSECRET_SUPPORT=OFF selects qtkeychain's pure Qt-D-Bus Secret Service backend, which talks to KDE Wallet (kwalletd) and GNOME Keyring over the session bus with no extra native runtime deps to bundle — only Qt6 DBus.
  • .github/workflows/appimage.yml — runs the setup before configure, passes -DREQUIRE_KEYCHAIN=ON, and adds the staged lib dir to LD_LIBRARY_PATH so linuxdeploy resolves and bundles libqt6keychain.so (Qt6 DBus comes in transitively).
  • CMakeLists.txt — adds REQUIRE_KEYCHAIN (mirrors the existing REQUIRE_SERIALPORT guard) so a missing keychain is a hard build failure for release/packaged builds instead of a silent compile-out; prepends the third_party/qtkeychain staging dir to CMAKE_PREFIX_PATH.
  • src/core/SmartLinkClient.cpp — logs credential-persistence availability at construction (a qCWarning under aether.smartlink / Help → Support → SmartLink when disabled), so the compiled-out case is diagnosable instead of failing silently.

Validation note

⚠️ The AppImage workflow triggers only on tags: ['v*'] and workflow_dispatch — it will not run automatically on this PR. A maintainer should dispatch it on ci/appimage-qtkeychain (Actions → AppImage → Run workflow) and confirm:

  • the build log prints Qt6Keychain found — SmartLink credential persistence enabled
  • -DREQUIRE_KEYCHAIN=ON would now fail the build if keychain were missing
  • the produced AppImage contains libqt6keychain.so (--appimage-extract then find squashfs-root -iname '*keychain*')
  • on a KDE box, login → restart → auto-login works and KDE Wallet shows an AetherSDR entry

Follow-up (out of scope)

Windows (#3634) could also pass -DREQUIRE_KEYCHAIN=ON now that the guard exists, to get the same release-time protection.

🤖 Generated with Claude Code

…rsistence (#3639)

The Linux AppImage was built without Qt6Keychain, so HAVE_KEYCHAIN was
undefined and SmartLink (and MQTT) credential persistence silently
compiled out: the refresh token was never stored, with no log line to
explain why. A from-source build on the same machine works, confirming
the system wallet is fine — the gap is purely AppImage packaging (#3639).

- scripts/setup/setup-qtkeychain.sh: build qtkeychain 0.16.0 from source
  against the active Qt (matches the aqt 6.8.3 ABI on x86_64, system Qt
  on ARM). LIBSECRET_SUPPORT=OFF selects the pure Qt-D-Bus Secret Service
  backend, which talks to KDE Wallet (kwalletd) and GNOME Keyring with no
  extra native runtime deps to bundle — only Qt6 DBus.
- appimage.yml: run the setup before configure, pass -DREQUIRE_KEYCHAIN=ON,
  and add the staged lib dir to LD_LIBRARY_PATH so linuxdeploy resolves and
  bundles libqt6keychain.so (and Qt6 DBus transitively).
- CMakeLists.txt: add REQUIRE_KEYCHAIN (mirrors REQUIRE_SERIALPORT) so a
  missing keychain is a hard build failure for release artifacts instead of
  a silent compile-out; prepend the third_party/qtkeychain staging dir.
- SmartLinkClient: log credential-persistence availability at construction
  (a warning when disabled) so the compiled-out case is visible in the
  SmartLink support log rather than failing silently.

Mirrors the Windows packaging approach in #3634.

Fixes #3639

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ten9876 ten9876 requested review from a team as code owners June 17, 2026 14:19

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clean, well-scoped packaging fix — thanks @ten9876. This is the correct Linux counterpart to #3634, and the approach is sound: build qtkeychain from source against the active Qt for ABI match, LIBSECRET_SUPPORT=OFF for the pure Qt-D-Bus Secret Service backend (no extra native deps to bundle), and REQUIRE_KEYCHAIN mirroring the existing REQUIRE_SERIALPORT guard so a release artifact can't silently ship without persistence.

A few things I verified that look correct:

  • CMake change is macOS/system-safe — the list(PREPEND CMAKE_PREFIX_PATH ...) is gated on EXISTS .../third_party/qtkeychain, so macOS (brew) and distro-package builds are untouched; only staged builds pick up the prepend.
  • LD_LIBRARY_PATH lib dir matches the script's CMAKE_INSTALL_LIBDIR=lib, and Qt6 DBus is correctly expected to come in transitively via libqt6keychain.so's NEEDED entry once linuxdeploy resolves it.
  • Setup script uses set -euo pipefail, the bare 0.16.0 tag matches qtkeychain's tag scheme, and the config-file existence check is a clean cache/re-run guard.
  • Logging category (aether.smartlink) defaults to QtWarningMsg, so the qCWarning disabled-path diagnostic surfaces by default while the qCInfo enabled line stays quiet — the right asymmetry for the "password not remembered" case.

One thing worth flagging before dispatch:

REQUIRE_KEYCHAIN=ON applies to both matrix legs, but validation only covered x86_64. The Configure step sets it unconditionally, so it also hard-fails the aarch64 leg (ubuntu-24.04-arm, system Qt via apt, qtkeychain built against that Qt through the qmake6 fallback). If the source build hiccups on the system-Qt path, the entire aarch64 AppImage release fails rather than degrading. Since the workflow only runs on tags/dispatch, a bad aarch64 build wouldn't surface until release time. Recommend the maintainer workflow_dispatch validation explicitly confirms both arches produce an AppImage containing libqt6keychain.so, not just the x86_64/KDE box in the PR notes.

Minor (optional, non-blocking): the script comment says the config-file check "lets CI cache third_party/qtkeychain," but no actions/cache step is added here — though that's consistent with the existing DeepFilterNet3 setup (also uncached), so each tagged build rebuilds from source. Fine as-is given the trigger cadence; just noting the comment is slightly aspirational.

Nothing blocking from my side — the diagnosable-logging addition and the hard-fail guard are both good defensive touches.


🤖 aethersdr-agent · cost: $6.3128 · model: claude-opus-4-8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linux AppImage does not persist SmartLink credentials via KDE Wallet / QtKeychain

1 participant