Generalize OS-environment detection; make storage failures non-fatal; add build provenance - #383
Open
3rdIteration wants to merge 13 commits into
Open
Generalize OS-environment detection; make storage failures non-fatal; add build provenance#3833rdIteration wants to merge 13 commits into
3rdIteration wants to merge 13 commits into
Conversation
… add build provenance
The app previously decided "am I on SeedSigner OS?" solely from the hostname
(HOSTNAME == "seedsigner-os"), wired into ~18 sites. That is fragile — the same
board can run either SeedSigner OS or a development OS — and it crashed the app
on Luckfox (wrong hostname -> desktop mode -> mkdir('/microsd') on a full rootfs
-> OSError [Errno 28], escaping main()).
- Add a generalized OS-environment detector in models/settings.py, distinct from
the board (RUNTIME_PROFILE): a positive marker file /etc/seedsigner-os-release
(shipped by seedsigner-os), with a hostname fallback and a /home/pi dev-board
check. New helpers Settings.is_seedsigner_os()/is_dev_board()/is_desktop().
- Route all Axis-B sites (settings.py, hardware/microsd.py, view.py,
settings_views.py, gpg_views.py, smartcard_views.py) through the helpers.
microsd.py is_desktop_mode() now delegates to Settings.is_desktop(). Fix the
missing lc_lafrite entry in the platform-name maps.
- Make storage/command failures non-fatal: wrap the boot-time interstitials in
controller.start() so they route into handle_exception instead of escaping
main(); harden MicroSD.get_microsd_dir() mkdir and screensaver.load_boot_logo_image()
against OSError; guard the microSD FIFO-thread I/O.
- Guard the numpy/pivideostream background preload so a missing optional module
(numpy can't build on the Luckfox uClibc toolchain) doesn't abort the thread.
- Add helpers/seedsigner_os.get_os_release() and is_running_from_microsd(); show
app version, seedsigner-os and app branch/commit/date, and Internal vs
Running-from-MicroSD in the System Info view.
- Tests: test_os_environment.py for the four detection branches; a boot-logo
storage-error resilience test.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The restart-command and microSD-removal tests previously simulated SeedSigner OS by setting Settings.HOSTNAME. Detection is now based on the computed Settings.OS_ENVIRONMENT, so patch that instead. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…oot.yml, add build-luckfox.yml The existing "Build" workflow only builds the Buildroot (Pi/La Frite) images via docker compose; running it with target=luckfox failed because opt/build.sh has no such target. Rename it to "Build Buildroot" to make that explicit. Add "Build Luckfox", which reuses the seedsigner-os build-luckfox reusable workflow to build a Luckfox image from the current app branch (defaults to the RV1103 Mini / SD_CARD combo). Runs on workflow_dispatch (pick hardware/boot/refs) and push to main; the ~1-2h build intentionally does not run on every PR. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Mini ties the ST7789 LCD CS to GND (no CE wiring), so it needs the driver's SPI_NO_CS / SPI mode 3 path (SCK idles high, avoiding boot-time SCK noise corrupting the command stream). The ST7789 driver already implements this when the display config has cs="disabled" (with a Luckfox EINVAL fallback that keeps mode 3), but FOX_22's io_config entry lacked the key, so it ran the default kernel-managed mode-0 path and the display stayed blank. Pro Max (FOX_40) wires CS to CE0 and works in mode 0, so it is left unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…oards Adds a REBOOT_LOADER option to PowerOptionsView, shown only when RUNTIME_PROFILE is a Luckfox Pico board (luckfox_22/40/pi). Selecting it reboots the device into Rockchip rockusb Loader mode so it can be re-flashed over USB (SocToolKit / rkdeveloptool) without the BOOT button. It issues the reboot(2) RESTART2 syscall with "loader" directly (busybox reboot ignores the mode arg), mirroring the rk-reboot helper. Adds RebootToLoaderView + RebootToLoaderScreen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
LargeButtonScreen only lays out 2 or 4 buttons (and its arrow-nav only handles the 4-button case), so the Luckfox 3-button Power menu (Restart / Power off / Reboot to flash mode) raised "LargeButtonScreen only supports 2 or 4 buttons" the moment Settings -> Power opened. Select the screen class by option count: keep LargeButtonScreen for the 2-button Pi/desktop menu (unchanged) and use the scrollable ButtonListScreen when the Luckfox reboot-to-loader option makes it 3. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The FOX_22 (Luckfox Pico Mini) display block carried "cs": "disabled" left over from the abandoned Mini display debugging. It broke test_io_config_profiles.py::test_existing_profiles_have_no_cs_field_by_default, which asserts no shipped profile declares a 'cs' field by default — failing the entire test and desktop CI matrices. Removing it is also the correct config: the Mini uses kernel-managed CE CS, and the field never fixed the blank screen. The SPI_NO_CS / Mode-3 / EINVAL-fallback support remains and stays covered by the synthetic-mapping ST7789/ST7735 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On Luckfox the SDK auto-mounts the card at /mnt/sdcard (not /mnt/microsd) and NAND/eMMC builds have no card, so settings never persisted. Add a resolver that treats both /mnt/microsd and /mnt/sdcard as the SeedSigner-OS data dir (a mounted card wins; else the first existing dir; else /mnt/microsd). Route MicroSD.get_microsd_dir(), MicroSD.is_inserted and Settings.SETTINGS_FILENAME through it, and makedirs the target before writing settings. Effect: - Luckfox SD_CARD: settings persist on the card at /mnt/sdcard. - Luckfox NAND/eMMC (no card): /mnt/sdcard is an empty mountpoint on the writable UBIFS rootfs, so settings persist there across reboots without a partition. - Raspberry Pi / La Frite: unchanged. Their root is stateless (wipes on reboot), so persistence still requires a physical card; /mnt/sdcard does not exist there, so the resolver returns /mnt/microsd and real-card detection / persistent- settings gating are byte-for-byte preserved. Adds tests/test_microsd_resolver.py covering both platform behaviors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…gnal Adds a button-free recovery gesture on Luckfox: holding KEY3 for ~5s on the Home screen reboots into rockusb Loader mode (same as the Power menu "Reboot to flash mode"); a short KEY3 tap still selects. - buttons.py: add is_pressed(key) — raw held-state read (no debounce/edge consumption) for hold polling. - screen.py: LargeButtonScreen gains optional long_press_key/ret_code/ms; the _run() loop discriminates tap-vs-hold outside the render lock. MainMenuScreen enables KEY3→RET_CODE__REBOOT_TO_LOADER only on Luckfox profiles. - view.py: MainMenuView routes RET_CODE__REBOOT_TO_LOADER → RebootToLoaderView, and on SeedSigner OS writes /tmp/seedsigner-ready (boot-watchdog signal) and resets the U-Boot bootcount once per boot (best-effort fw_setenv). Desktop/other boards are unchanged (long_press_key stays None; the ready signal is gated to is_seedsigner_os()). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Luckfox U-Boot boot-failover counter is now memory-backed (CONFIG_SYS_BOOTCOUNT_ADDR = GRF OS_REG scratch register 0xFF020218), not env-backed, because this Rockchip U-Boot 2017.09 fork has no CONFIG_BOOTCOUNT_LIMIT/_ENV Kconfig symbols. On reaching Home, clear the counter with `devmem 0xFF020218 32 0` instead of `fw_setenv bootcount 0` — this targets the real counter and writes no flash (the old fw_setenv wrote the mtd0 env on every Home visit and was ignored by the backend). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ct tests Two B11 (Tools_View refactor) regressions that only surfaced on hardware: 1. SEEDKEEPER_DIC_TYPE NameError (view secrets / load descriptors, all platforms): the pysatochip try-block in smartcard_views imported three modules that don't exist (pysatochip.satochip, pysatochip.exception, pysatochip.satochip_protocol_helper), and `except ImportError: pass` swallowed the whole block, so the valid JCconstants names never bound. Fix the import paths (UnexpectedSW12Error lives in pysatochip.CardConnector; format_sw_error is the local seedsigner.helpers.iso7816 helper), move logger above the block, and log any future ImportError instead of swallowing it. 2. card_get_ndef AttributeError: the KeycardSatochipConnector adapter exposed no NDEF surface. Add card_get_ndef / card_get_ndef_v2 / card_set_ndef backed by the Keycard's native NDEF data slot (GET DATA / STORE DATA), returning pysatochip-shaped tuples and surfacing the card's real SW on failure. (The SeedKeeper-side AttributeError was the seedsigner-os per-toolchain pysatochip version split, fixed OS-side by unifying on 0.6a.) Tests — this class of bug was invisible to the suite because sys.modules['pysatochip'] = MagicMock() auto-creates any name: - tests/test_pysatochip_contract.py: real-pysatochip contract checks in a pristine subprocess (constants bind in smartcard_views, connector has the NDEF methods the views call, view-style header decode), plus the adapter NDEF surface; skipped when real pysatochip isn't installed. - Keycard adapter NDEF unit tests (mock card): roundtrip + real-SW paths. - Hardware tests: SeedKeeper card_get_ndef/card_set_ndef roundtrip and list-secrets decoded with the real JCconstants dicts (mirroring ToolsSeedkeeperViewSecretsView); Keycard NDEF slot roundtrip. Full suite: 849 passed, 188 skipped, 1 xfailed; the 5 l10n failures are pre-existing on this machine (uncompiled translations). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…i & Lafrite" The Luckfox workflow could only pass hardware/boot/app-ref through to the reusable seedsigner-os build, so there was no way to request a dev image or any of the Luckfox-specific toggles from this repo. Add the full manual-dispatch surface, matching what the Pi/La Frite build already offers: - build_variant (non-dev | dev) — the hardened air-gapped image vs the serial-console + adb debug image - usb_mode (auto | host | gadget | otg), debug_network (auto | on | off), disable_uart2_console_debug (auto | true | false) — all default to auto, which follows build_variant - ALL option for hardware_type / boot_medium (builds every valid combo) - app-repo and release_tag, mirroring the Pi build's app-repo / upload-release inputs - job permission raised to contents: write so release_tag can upload Push-to-main behaviour is unchanged: a single non-dev Pico Mini / SD_CARD build. Also rename the Buildroot workflow to "Build Pi & Lafrite" (display name and run-name only; the filename stays so existing links and history are unaffected). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The workflow was renamed to "Build Pi & Lafrite" but could not build a La Frite image: `lafrite` was absent from the target input's description and from the "all" matrix expansion, so it was only reachable by typing the undocumented value by hand. Add lafrite to the "all" expansion and document it in the input. Note in the description that upstream ships only lafrite-smartcard and lafrite-smartcard-dev configs, so La Frite requires smartcard enabled (the workflow's default). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The app decided "am I on SeedSigner OS?" solely from the hostname
(
Settings.HOSTNAME == "seedsigner-os"), wired into ~18 sites. That is fragile —the same board can run either SeedSigner OS or a development OS (e.g. a
Raspberry Pi) — and it crashed the app on the Luckfox Pico: the wrong hostname
dropped the app into desktop mode, which ran
mkdir('/microsd')on a full rootfsand raised
OSError [Errno 28], escapingmain()(exit 1).What
Generalized OS-environment detection (
models/settings.py) — a newOS_ENVIRONMENT(seedsigner_os / dev_board / desktop), distinct from the board(
RUNTIME_PROFILE):/etc/seedsigner-os-release(shipped by seedsigner-os),HOSTNAME == "seedsigner-os"fallback (back-compat with existing images),/home/pi→ dev board (Raspberry Pi OS),New helpers
Settings.is_seedsigner_os()/is_dev_board()/is_desktop()replace every raw hostname /
/home/pi/is_desktop_mode()check(
settings.py,hardware/microsd.py,view.py,settings_views.py,gpg_views.py,smartcard_views.py). Also fixes the missinglc_lafriteentryin the platform-name maps.
Storage / command failures are handled, never crash — the boot-time
interstitials in
Controller.start()run before the main loop's exceptionhandling, so a failure there escaped
main(). They are now wrapped and routed tothe normal error flow.
MicroSD.get_microsd_dir(),screensaver.load_boot_logo_image()and the microSD FIFO thread are hardened against
OSError.numpy preload guard — the background
time_import('numpy')/pivideostreampreload (Pi-camera-only; numpy can't build on the Luckfox uClibctoolchain) no longer aborts the whole preload thread. QR scanning already works
without numpy (pyzbar decodes PIL images directly).
Build provenance in System Info —
helpers/seedsigner_os.get_os_release()andis_running_from_microsd(); the System Info view shows the app version, theseedsigner-os and app branch/commit/date, and "Internal" vs "Running from MicroSD".
Tests
tests/test_os_environment.py— the four detection branches.tests/test_custom_logo.py— boot-logo survives a storage error.test_flows_view.py/test_flows_settings.pyto patchOS_ENVIRONMENTinstead of
HOSTNAMEfor OS-mode simulation.Companion
The
/etc/seedsigner-os-releasemarker is produced by the seedsigner-os build(branch
merge-luckfox-pico-build). To build a Luckfox image with these appchanges, run
build-luckfox.ymlthere withseedsigner_branch=generalized-platform-detection.🤖 Generated with Claude Code