Skip to content

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

Merged
ten9876 merged 2 commits into
mainfrom
ci/appimage-qtkeychain
Jun 19, 2026
Merged

ci(appimage): build and bundle qtkeychain for SmartLink credential persistence (#3639)#3640
ten9876 merged 2 commits 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

@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

…rsistence (#3639)

Linux AppImage counterpart to the Windows fix (#3634): the AppImage shipped
without Qt6Keychain, so SmartLink/MQTT credential persistence silently compiled
out (no Auth0 refresh token stored, no log line explaining why).

- scripts/setup/setup-qtkeychain.sh — builds qtkeychain 0.16.0 from source
  against the active Qt (ABI match with the aqt Qt the AppImage links).
  LIBSECRET_SUPPORT=OFF selects the pure Qt-DBus Secret Service backend
  (KDE Wallet / GNOME Keyring), so only Qt6 DBus is bundled — no extra native deps.
- .github/workflows/appimage.yml — setup before configure, -DREQUIRE_KEYCHAIN=ON,
  and stage the lib dir on LD_LIBRARY_PATH so linuxdeploy bundles libqt6keychain.so.
- CMakeLists.txt — new REQUIRE_KEYCHAIN option (mirrors REQUIRE_SERIALPORT): a
  missing Qt6Keychain becomes a hard build failure for release/packaged builds
  instead of a silent compile-out. The EXISTS-guarded third_party/qtkeychain
  prepend supersedes #3634's WIN32-only one (covers both platforms).
- windows-installer.yml — pass -DREQUIRE_KEYCHAIN=ON too, now that the guard
  exists and #3634 stages qtkeychain there (its own follow-up note).
- SmartLinkClient.cpp — log credential-persistence availability at construction
  (qCWarning under aether.smartlink when disabled) so the compiled-out case is
  diagnosable instead of silent.

Rebased onto main after #3634 merged; CMakeLists conflict resolved by collapsing
the two prepends into the general EXISTS-guarded form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ten9876 ten9876 force-pushed the ci/appimage-qtkeychain branch from 535b09e to 66f2cdb Compare June 19, 2026 13:03
@aethersdr-agent

Copy link
Copy Markdown
Contributor

Hi @ten9876 — thanks for tackling #3639, shipping credential persistence in the AppImage is a genuinely useful fix. 🙏 Quick triage on the red CI so you're not hunting in the dark.

Where it failed

The failure is isolated to the Build AppImage step on commit 66f2cdb — and it failed on both arches:

Everything before packaging was green, which is the important clue:

Step Result
Setup qtkeychain
Configure (-DREQUIRE_KEYCHAIN=ON)
Build (compile + link)
Install to AppDir
Download linuxdeploy + Qt plugin
Build AppImage

check-macos and the main build job also passed. So this is not a code/compile problem — your C++ and the REQUIRE_KEYCHAIN wiring are fine. qtkeychain built from source, find_package(Qt6Keychain REQUIRED) resolved, and the binary linked.

It breaks at the linuxdeploy bundling stage — which is precisely the part this PR changes (the new LD_LIBRARY_PATH=.../third_party/qtkeychain/lib export + expecting linuxdeploy to resolve and bundle libqt6keychain.so). So this is most likely your packaging change, not CI infrastructure — the same hunk failing identically on two independent runners points at the logic, not a flaky agent.

Honesty note

The triage tooling I have surfaces the failing step, but not the raw linuxdeploy stderr. I don't want to guess at an error string I can't see, so please open the x86_64 job log and scroll to Build AppImage for the exact line. Below are the most likely culprits to check against it.

Most likely causes (check these against the real log)

  1. linuxdeploy can't resolve libqt6keychain.so. linuxdeploy reads the NEEDED entries of the main binary and searches LD_LIBRARY_PATH — but the qt plugin (--plugin qt) runs its own deployment pass and doesn't always honor that export. If the error is a missing/unresolved libqt6keychain.so*, try passing it explicitly, e.g. --library third_party/qtkeychain/lib/libqt6keychain.so.1, instead of relying solely on LD_LIBRARY_PATH.
  2. arch mismatch in the path. The export uses third_party/qtkeychain/lib, and your setup-qtkeychain.sh forces -DCMAKE_INSTALL_LIBDIR=lib (good — sidesteps lib64). Worth confirming the .so actually landed there on both runners; the aarch64 job builds qtkeychain against system Qt (the "Install system Qt (ARM only)" path) rather than aqt Qt, so an ABI/soname difference could surface only there.
  3. Qt6Keychain_FOUND define plumbing. SmartLinkClient.cpp now branches on HAVE_KEYCHAIN. Make sure that compile definition is actually set when Qt6Keychain_FOUND is true (the diff doesn't show the target_compile_definitions); if it isn't, that's a separate latent issue but worth verifying while you're in here.

Suggested next step

Grab the exact linuxdeploy error from the log above, then reproduce locally with the same invocation:

bash scripts/setup/setup-qtkeychain.sh
export LD_LIBRARY_PATH="$PWD/third_party/qtkeychain/lib:$LD_LIBRARY_PATH"
./linuxdeploy-x86_64.AppImage --appdir AppDir --plugin qt --output appimage

Relevant files for reference: .github/workflows/appimage.yml (the Build AppImage step) and scripts/setup/setup-qtkeychain.sh.

No reviewer/Copilot comments are on the PR yet, so nothing else to fold in. Happy to take another look once you've pushed a fix — you're very close, it's just the bundling handoff. 🚀


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

A workflow_dispatch on a branch (e.g. ci/appimage-qtkeychain) puts a '/' in
github.ref_name, which became AetherSDR-ci/appimage-qtkeychain-x86_64.AppImage
and broke mksquashfs ("Could not create destination file: No such file or
directory"). Tag pushes (v26.6.x) are slash-free so releases were unaffected,
but dispatch-based validation of this very PR couldn't produce an AppImage.

Replace unsafe chars with '_' before building LDAI_OUTPUT, mirroring the
sanitization windows-installer.yml already applies for the same reason.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ten9876 ten9876 merged commit 8fddf1a into main Jun 19, 2026
8 checks passed
@ten9876 ten9876 deleted the ci/appimage-qtkeychain branch June 19, 2026 14:49
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