diff --git a/AGENTS.md b/AGENTS.md index 8f241ed..f3b05b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,6 +42,14 @@ The portal API key is also a build-time argument: `-DAPI_KEY="..."` (CMake) or real value in CI secrets, never in source — rotate by rebuilding with a new key. +Version and API key can be baked in together with a single configure: + +```sh +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug \ + -DAPP_VERSION="1.2.3" \ + -DAPI_KEY="$API_KEY" +``` + - The app resolves `assets/...` relative to its working directory — launch `./build/radiantwave` from the repo root. (The Nix wrapper chdirs to `$out/share/radiantwave` before exec, which is why the installed app works.) diff --git a/README.md b/README.md index 305928a..7556fcd 100644 --- a/README.md +++ b/README.md @@ -153,6 +153,21 @@ Rotate the key by building with a new value and shipping the new binary. The Nix package accepts the same value via `callPackage ./nix/package.nix { apiKey = "…"; }`; the flake itself never embeds one. +#### Version + API key in one configure + +`APP_VERSION` and `API_KEY` are independent CMake cache variables, so pass +both — plus the build type — in a single configure: + +```sh +cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug \ + -DAPP_VERSION="1.2.3" \ + -DAPI_KEY="${{ secrets.PORTAL_API_KEY }}" +cmake --build build +``` + +`-D` values always win over the `APP_VERSION` / `API_KEY` environment +variables, which `nix develop` seeds from the ambient environment. + ### Building with Nix The flake supports `x86_64-linux` and `aarch64-linux` (the deployment diff --git a/flake.nix b/flake.nix index 89bd638..73a0718 100644 --- a/flake.nix +++ b/flake.nix @@ -54,6 +54,7 @@ echo "RadiantWave dev shell (APP_VERSION=dev, API_KEY=$([[ -n "$API_KEY" ]] && echo set || echo unset))" echo " Configure: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug" + echo " Bake version+key: cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DAPP_VERSION=\"1.2.3\" -DAPI_KEY=\"\"" echo " Build: cmake --build build" echo " Run: ./build/radiantwave" echo " Tests: ./build/test_assets && ./build/test_" diff --git a/src/network/nm_device.cpp b/src/network/nm_device.cpp index 73b6aaa..59ff31a 100644 --- a/src/network/nm_device.cpp +++ b/src/network/nm_device.cpp @@ -83,10 +83,11 @@ struct WifiDevice::Impl { ); if (r < 0) { - std::string err = "D-Bus call failed: "; - err += error.message ? error.message : strerror(-r); + radiantwave::logger::Logger::instance().warning("WiFi", + std::string("D-Bus call failed: ") + + (error.message ? error.message : strerror(-r))); sd_bus_error_free(&error); - throw std::runtime_error(err); + return nullptr; } sd_bus_error_free(&error); @@ -116,10 +117,16 @@ struct WifiDevice::Impl { ); if (r < 0) { - std::string err = "D-Bus property get failed: "; - err += error.message ? error.message : strerror(-r); + // Transient D-Bus errors (e.g. a reply raced with a state change + // while NetworkManager churns the device during a failed auth) + // must never throw out of here — callers treat a null reply as + // "unknown", which keeps the UI and wait loops running. + radiantwave::logger::Logger::instance().warning("WiFi", + "D-Bus property get failed on " + std::string(path) + "." + + std::string(prop) + ": " + + (error.message ? error.message : strerror(-r))); sd_bus_error_free(&error); - throw std::runtime_error(err); + return nullptr; } sd_bus_error_free(&error); @@ -145,7 +152,11 @@ std::string WifiDevice::getInterfaceName() const { "Interface", "s" ); - + + if (!msg) { + return ""; + } + const char* iface = nullptr; sd_bus_message_read(msg, "s", &iface); std::string result = iface ? iface : ""; @@ -162,7 +173,13 @@ uint32_t WifiDevice::getDeviceState() const { "State", "u" ); - + + if (!msg) { + // Unknown state keeps callers polling/falling back gracefully instead + // of aborting on a transient read failure. + return nm_device_state::Unknown; + } + uint32_t state = 0; sd_bus_message_read(msg, "u", &state); sd_bus_message_unref(msg); @@ -328,7 +345,11 @@ AccessPointInfo WifiDevice::getActiveAccessPoint() const { "ActiveAccessPoint", "o" ); - + + if (!msg) { + return AccessPointInfo{}; // No active AP + } + const char* ap_path = nullptr; sd_bus_message_read(msg, "o", &ap_path); sd_bus_message_unref(msg);