feat(keycard): ship self-contained simulator in packages

Precompile at package time (JDK in CI image / nix) and bundle the runtime bits into the app,
resolved relative to the executable — downloaded builds run the simulator with just a JRE, no checkout.
This commit is contained in:
Sale Djenic
2026-08-06 13:00:42 +02:00
committed by saledjenic
parent 78ec903a9b
commit dde79aac96
5 changed files with 66 additions and 10 deletions
+12
View File
@@ -557,6 +557,9 @@ export STATUSKEYCARD_QT_LIB := $(STATUSKEYCARD_QT_LIBDIR)/$(STATUSKEYCARD_QT_LIB
STATUSKEYCARD_QT_DYLIB_NAME := $(notdir $(STATUSKEYCARD_QT_LIB))
STATUSKEYCARD_QT_LINKNAME := $(patsubst lib%,%,$(basename $(STATUSKEYCARD_QT_DYLIB_NAME)))
KEYCARD_SIM_SRC_DIR := $(STATUS_KEYCARD_QT_SOURCE_DIR)/test/keycard-simulator
KEYCARD_SIM_RUNTIME_BITS := run.sh libs versions out
status-keycard-qt: $(STATUSKEYCARD_QT_LIB)
$(STATUSKEYCARD_QT_LIB): | deps check-qt-dir
echo -e $(BUILD_MSG) "status-keycard-qt"
@@ -893,6 +896,15 @@ $(STATUS_CLIENT_DMG): nim_status_client
cp status-macos.svg $(MACOS_OUTER_BUNDLE)/Contents/
cp -R resources.rcc $(MACOS_OUTER_BUNDLE)/Contents/
# Precompile (JDK from nix) + bundle the keycard simulator into the app, before macdeployqt/signing
ifeq ($(USE_SIMULATED_KEYCARD),true)
echo -e $(BUILD_MSG) "keycard-simulator (precompile + bundle)"
nix shell .#jdk -c bash -c 'cd $(KEYCARD_SIM_SRC_DIR) && ./build.sh'
mkdir -p $(MACOS_OUTER_BUNDLE)/Contents/Resources/keycard-simulator
cp -R $(addprefix $(KEYCARD_SIM_SRC_DIR)/,$(KEYCARD_SIM_RUNTIME_BITS)) \
$(MACOS_OUTER_BUNDLE)/Contents/Resources/keycard-simulator/
endif
echo -e $(BUILD_MSG) "app"
MAC_QTQMLDIR=$(shell $(QMAKE) -query QT_INSTALL_QML) && \
macdeployqt \
+3
View File
@@ -21,6 +21,9 @@
hash = "sha256-Z2sXrNRIiZ9tSoOyGE4GV0gEROy2rJxJIoie+tnl2/s=";
};
});
# JDK to precompile the keycard simulator when packaging a USE_SIMULATED_KEYCARD build
jdk = pkgs.jdk;
}
);
};
+18
View File
@@ -25,6 +25,19 @@ copy_system_libs() {
cp -P /usr/local/lib/x86_64-linux-gnu/libpcsclite*.so* "$dest/"
}
bundle_keycard_simulator() {
local dest="$1"
local src="vendor/status-keycard-qt/test/keycard-simulator"
if ! command -v javac >/dev/null 2>&1; then
echo "WARNING: javac not found; skipping keycard simulator bundle." >&2
return 0
fi
echo "Bundling keycard simulator (precompile + copy)..."
( cd "$src" && ./build.sh )
mkdir -p "$dest"
cp -R "$src"/run.sh "$src"/libs "$src"/versions "$src"/out "$dest/"
}
DEST="${APP_DIR:?APP_DIR must be set}/usr"
rm -rf "${APP_DIR}"
@@ -48,6 +61,11 @@ cp status.png "${DEST}/"
copy_native_libs "${DEST}/lib"
cp "${FCITX5_QT}" "${DEST}/plugins/platforminputcontexts/"
if [[ "${USE_SIMULATED_KEYCARD:-}" == "true" ]]; then
# Bundled at usr/share/keycard-simulator; the app finds it at ../share relative to usr/bin.
bundle_keycard_simulator "${DEST}/share/keycard-simulator"
fi
# Qt WebEngine only (process + resources + locales)
copy_qt_webengine() {
local dest="$1"
@@ -14,13 +14,28 @@ when defined(useSimulatedKeycard):
const
KEYCARD_SIMULATOR_DEFAULT_VERSION = "3.2"
KEYCARD_SIMULATOR_DEFAULT_SIMULATOR_ADDRESS = "127.0.0.1:9025"
KEYCARD_SIMULATOR_DEFAULT_SIMULATOR_DIR = "vendor/status-keycard-qt/test/keycard-simulator"
# Subdirs (relative to the app executable) where packaging bundles the precompiled simulator:
# macOS Status.app/Contents/MacOS/nim_status_client -> ../Resources/keycard-simulator
# Linux AppDir/usr/bin/nim_status_client -> ../share/keycard-simulator
KEYCARD_SIMULATOR_BUNDLED_SUBDIRS = ["../Resources/keycard-simulator", "../share/keycard-simulator"]
KEYCARD_SIMULATOR_DEV_DIR = "vendor/status-keycard-qt/test/keycard-simulator"
var ignoreKeycardLibSignals = false # used to avoid triggering of any keycard actions while setting up the test keycard
proc shouldIgnoreKeycardLibSignals*(): bool =
return ignoreKeycardLibSignals
proc resolveSimDir(): string =
let envDir = getEnv("STATUS_KEYCARD_SIM_DIR")
if envDir.len > 0:
return absolutePath(envDir)
let appDir = getAppDir()
for sub in KEYCARD_SIMULATOR_BUNDLED_SUBDIRS:
let candidate = normalizedPath(appDir / sub)
if dirExists(candidate):
return candidate
return absolutePath(KEYCARD_SIMULATOR_DEV_DIR)
type
LoadCardArg = ref object of QObjectTaskArg
params: JsonNode
@@ -56,6 +71,21 @@ when defined(useSimulatedKeycard):
self.QObject.delete
proc startSimulator*(self: KeycardTestController, version: string) {.slot.} =
var safeVersion = ""
for c in version:
if c in {'0'..'9', '.'}: safeVersion.add(c)
if safeVersion.len == 0:
safeVersion = KEYCARD_SIMULATOR_DEFAULT_VERSION
let simDir = resolveSimDir()
if not dirExists(simDir):
error "keycard simulator directory not found; set STATUS_KEYCARD_SIM_DIR to " &
"'<status-desktop checkout>/vendor/status-keycard-qt/test/keycard-simulator'", dir = simDir
return
let port = getEnv("STATUS_KEYCARD_SIM_ENDPOINT", KEYCARD_SIMULATOR_DEFAULT_SIMULATOR_ADDRESS).rsplit(":", 1)[^1]
let launch =
if dirExists(simDir / "out"): "exec ./run.sh " & port & " " & safeVersion
else: "./build.sh && exec ./run.sh " & port & " " & safeVersion
if not self.simProcess.isNil and self.simProcess.running:
self.simProcess.terminate()
self.simProcess.close()
@@ -64,16 +94,9 @@ when defined(useSimulatedKeycard):
discard keycard_go.keycardTestRemoveCard()
discard keycard_go.keycardTestUnplugReader()
var safeVersion = ""
for c in version:
if c in {'0'..'9', '.'}: safeVersion.add(c)
if safeVersion.len == 0:
safeVersion = KEYCARD_SIMULATOR_DEFAULT_VERSION
let simDir = getEnv("STATUS_KEYCARD_SIM_DIR", KEYCARD_SIMULATOR_DEFAULT_SIMULATOR_DIR)
let port = getEnv("STATUS_KEYCARD_SIM_ENDPOINT", KEYCARD_SIMULATOR_DEFAULT_SIMULATOR_ADDRESS).rsplit(":", 1)[^1]
try:
self.simProcess = startProcess("/bin/bash",
args = @["-c", "cd '" & simDir & "' && ./build.sh && exec ./run.sh " & port & " " & safeVersion],
args = @["-c", "cd '" & simDir & "' && " & launch],
options = {poParentStreams})
info "starting keycard simulator", dir = simDir, port = port, version = safeVersion
except CatchableError as e: