-
Notifications
You must be signed in to change notification settings - Fork 1
fix: Pi Image Builder and SD Card Writer - WiFi auto-connect, hostname config, and reliability improvements #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
X9X0
merged 56 commits into
main
from
claude/fix-launcher-titlebar-repair-0187nhMwEvHWrbWYLWmwCBWB
Nov 17, 2025
Merged
fix: Pi Image Builder and SD Card Writer - WiFi auto-connect, hostname config, and reliability improvements #74
X9X0
merged 56 commits into
main
from
claude/fix-launcher-titlebar-repair-0187nhMwEvHWrbWYLWmwCBWB
Nov 17, 2025
Conversation
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
- Changed title bar background from #2c3e50 to #1a252f for better contrast - Added auto_fix_if_needed() method to automatically prompt for fixes - Auto-fix dialog now appears when fixable errors are detected after system checks - Improves user experience by proactively offering to fix dependency issues
UI Improvements: - Added light gray background (#ecf0f1) to main window for better contrast - Enhanced GroupBox elements with white backgrounds and defined borders - Added proper margins (15px) and spacing (15px) to main layout - Improved title bar with darker border for better definition Button Enhancements: - All buttons now have 2px borders with hover/press states - Refresh button: Light gray with subtle borders - Fix button: Orange theme (#f39c12) with darker borders - Server button: Green (#27ae60) with defined borders and disabled state - Client button: Blue (#3498db) with defined borders and disabled state - Added border-radius to all buttons for consistency LED Indicator Improvements: - Added light background (#f8f9fa) with border to each indicator - LED circles now have dark borders for better definition - Improved glow effect with adjusted alpha (80) - Better visual separation between indicators Progress Elements: - Progress label now has white background with border - Progress bar styled with borders and custom chunk color - Better visual integration with overall theme Result: Much clearer visual hierarchy and better-defined UI elements
- Added 3px solid border (#34495e) to QMainWindow - Creates clear visual definition for the outermost window boundary - Border color complements the existing dark theme elements - Improves overall window presence and visual hierarchy
- Increased border from 3px to 5px for stronger definition - Darkened border color from #34495e to #2c3e50 - Creates more prominent window boundary - Improves visual presence and professional appearance
- Increased border from 5px to 6px for stronger presence - Darkened border color from #2c3e50 to #1a252f - Border now matches header color for cohesive design - Creates very strong, professional window definition
- Changed border color from #1a252f to #0d1419 - Border now darker than header background (#1a252f) - Matches header border color for cohesive design - Creates stronger visual hierarchy
Applied the same visual styling from the launcher to the client application: Main Window (main_window.py): - Added 6px solid border (#0d1419) to main window - Set background color to #ecf0f1 - Added 15px margins and 10px spacing to central widget layout - Consistent visual appearance with launcher Connection Dialog (connection_dialog.py): - Added light gray background (#ecf0f1) - Styled GroupBox elements with white backgrounds and 2px borders - Enhanced buttons with blue theme, borders, and hover/press states - Added 15px margins and 10px spacing to layout Login Dialog (login_dialog.py): - Applied same styling as connection dialog - Consistent GroupBox and button styling - Proper margins (15px) and spacing (10px) Result: Cohesive, professional appearance across all client UI components
Pi Image Builder Fixes: - Fixed non-functional Next button by providing default output path - Output path field now defaults to ~/lablink-pi.img - Required field validation now satisfied on wizard open - Applied consistent visual styling to wizard - Light gray background (#ecf0f1) - Styled GroupBox elements with white backgrounds and borders - Enhanced buttons with hover/press states SD Card Writer Improvements: - Applied consistent visual styling matching other dialogs - Added proper margins (15px) and spacing (10px) to layout - Styled GroupBox and button elements for cohesive appearance Result: Pi Image Builder wizard now functional with Next button enabled
The issue was that the default output path was being set BEFORE the field was registered with the wizard. Qt's wizard validation system checks field completeness at registration time. Fix: - Moved setText() call to AFTER registerField() - Added completeChanged.emit() to notify wizard of state change - This ensures Qt recognizes the field as complete and enables Next button The wizard now properly enables the Next button on startup with the default path ~/lablink-pi.img pre-filled.
The issue was that setting the default value in __init__ happens before the wizard fully initializes its validation system. Solution: - Moved default path setting to initializePage() method - This is called by Qt when the page is displayed - Qt's validation system is fully set up at this point - The Next button will properly enable when the page loads initializePage() is the correct Qt method for setting initial values in wizard pages as it's called after the page is added to the wizard and the validation system is ready.
The correct way to set default values for wizard fields is to pass them
to the widget constructor BEFORE registering the field, exactly like the
hostname field does.
Before (not working):
- Set value in initializePage() after registration
After (working):
- QLineEdit(default_path) - passes default to constructor
- Same pattern as hostname: QLineEdit("lablink-pi")
- Value exists when registerField() is called
- Qt wizard validation recognizes field as complete
This matches Qt's expected pattern for wizard field initialization.
Fixed two issues with Pi Image Builder wizard: 1. Next button validation: - Added explicit isComplete() method that checks required fields - Connected textChanged signals to completeChanged for real-time validation - Wizard now properly enables Next when both hostname and output_path have values 2. Button styling: - Added QPushButton:disabled state styling - Buttons now show proper hover effects (blue -> darker blue) - Disabled state shows gray appearance - Visual feedback now works correctly for all button states The wizard buttons are now properly styled and the Next button enables when the page loads with default values (hostname: "lablink-pi", output_path: "~/lablink-pi.img").
Added extensive logging to diagnose build process issues: - Log script path and existence check - Log process PID when started - Log every line of build output with line numbers - Log process exit code - Log all errors with full context - Show script path and PID in GUI output window This will help identify why the build gets stuck at 5% by showing: 1. Whether the script is found 2. Whether the process actually starts 3. What output (if any) the script produces 4. Any errors that occur Logs go to both logger (console/file) and GUI output window.
The build script requires sudo to: - Mount loop devices - Use kpartx for partition mapping - Resize filesystems - Access /dev devices Changes: - Check if pkexec is available before running - Run script with pkexec for graphical sudo prompt - Warn users they'll need to enter their password - Log whether running with elevated privileges or not - Show clear error if pkexec not available This fixes the issue where the wizard would hang at 5% because the script was exiting immediately when it detected it wasn't running as root (check_root() function exits with code 1). The user will now see a password prompt dialog and the build will proceed with proper permissions.
The build script output was being buffered when run through pkexec, causing the GUI to show no progress even though the script was running. Solution: - Use stdbuf -oL -eL to force line-buffered output - -oL: Line buffer stdout - -eL: Line buffer stderr - This makes output appear in real-time in the GUI Now users will see the build progress as it happens instead of waiting for the entire buffer to flush.
Changed from stdbuf to a different approach: - Set bufsize=0 for unbuffered I/O in Popen - Pass environment variables via bash -c wrapper instead of env parameter - This ensures env vars are properly exported before script runs - Removed stdbuf in favor of direct unbuffered subprocess The previous approach used env parameter with Popen, but when wrapping the command through pkexec, the env variables weren't being passed through. Now we export them in a bash wrapper that pkexec executes.
…ffering The real issue with buffering is that Python's text mode readline() internally buffers even with bufsize=0. Solution: - Read in binary mode with bufsize=0 (truly unbuffered) - Read one byte at a time - Accumulate bytes until we hit newline (\n) - Decode complete lines as UTF-8 and emit to GUI - Parse progress from decoded lines This gives us character-by-character unbuffered output from the build script, so the GUI shows progress in real-time as the script writes it, not when buffers flush.
…output The previous approach was overriding the script command setup. This fix: 1. Creates a temporary shell script wrapper that exports all env variables 2. Uses 'unbuffer' command (from expect package) for truly unbuffered I/O - unbuffer forces programs to flush output immediately - Works by tricking programs into thinking they're connected to a TTY 3. Falls back to plain bash if unbuffer isn't installed 4. Cleans up temp wrapper file after execution The unbuffer command is the gold standard for forcing unbuffered output from shell scripts. If user doesn't have it, we suggest installing the 'expect' package which provides it. Install with: sudo apt install expect
The fundamental issue is that bash buffers stdout when it detects it's not connected to a terminal. All previous attempts (stdbuf, unbuffer, binary mode) failed because the buffering happens inside bash before Python ever sees the output. Solution: Use Python's pty module - pty.openpty() creates a pseudo-terminal pair (master/slave) - Subprocess runs with stdout/stderr/stdin connected to slave fd - Bash thinks it's connected to a terminal -> uses line-buffered I/O - Parent process reads from master fd with select() for non-blocking I/O - Process complete lines as they arrive in real-time This is the definitive solution for getting unbuffered output from shell scripts - it's what tools like 'script' and 'unbuffer' use internally. The GUI will now show build progress in real-time as the script executes each step.
The build script checks if stdin is a TTY and prompts for configuration: - Output image name - Hostname - Enable SSH - Wi-Fi SSID/password - Admin password Since we're using a pty, the script thinks it's interactive and waits for user input with read -p commands. This caused the hang after "Configuration Options:" message. Solution: - Immediately after process starts, write 6 newlines to master_fd - This accepts all the default values for the prompts - The script uses our environment variables as the defaults - Build proceeds non-interactively Now the script will continue past the prompts and start the actual build process.
Add a new LED indicator to track client utilities (deployment and discovery tools) separately from client dependencies. This allows users to see the status of optional utilities and install them automatically. Changes: - Add utilities LED indicator to launcher UI showing deployment and discovery tools status - Implement check_utils_dependencies() to verify paramiko, scp, scapy, and zeroconf installation - Add auto-fix support for installing missing utilities - Integrate utilities check into the check sequence after client deps - Add show_utils_details() for detailed utilities information dialog - Update FixWorker to handle pip_install:client_utils fix command The utilities are categorized as: - Deployment: paramiko (SSH), scp (secure copy) - Discovery: scapy (network packets), zeroconf (mDNS/Bonjour) These utilities are already listed in client/requirements.txt but are now separately tracked for better visibility and targeted installation.
Add build tools required for the Raspberry Pi image builder to the launcher's system dependency checks, allowing them to be automatically detected and installed. Build tools added: - kpartx: Partition mapping for disk images - qemu-user-static: ARM emulation for chroot operations - parted: Partition manipulation - wget: Download utilities - xz-utils: Compression utilities These tools are now categorized separately as "Build tools (for Pi image builder)" in the system packages check. When missing, they can be auto-installed with the "Fix Issues" button. This ensures users have all necessary dependencies before attempting to build Raspberry Pi images through the client's image builder tool.
Fix segmentation fault crash when clicking "Fix Issues" button with missing system packages. The crash was caused by trying to show QMessageBox dialogs from a background worker thread, which Qt doesn't allow. Changes: - Remove GUI dialog calls from FixWorker thread - Run pkexec apt commands directly from worker thread - pkexec handles its own GUI password prompt safely - Add proper error handling for cancelled operations - Check for pkexec availability before attempting install The fix resolves the QPainter threading errors: "QPainter::begin: A paint device can only be painted by one painter" "Segmentation fault (core dumped)" Now the launcher can automatically install missing build tools (kpartx, qemu-user-static, parted) without crashing.
Fix bug where the script tried to move the extracted image to itself, causing the error: "mv: '/tmp/lablink-pi-build/raspios.img' and '/tmp/lablink-pi-build/raspios.img' are the same file" After xz extraction, the .img file is already in the correct location, so the mv command was unnecessary and caused the build to fail. Changes: - Remove the mv command that tried to move file to itself - Simplify extracted_img to point directly to the extracted file - This fixes the "No such file or directory" error in expand_image() The Pi image builder should now complete successfully.
Add comprehensive error checking to the Pi image extraction process to diagnose why the extracted .img file is not being created. Changes: - Check xz command exit code and fail fast if extraction fails - Verify extracted image file exists before proceeding - List working directory contents if extraction verification fails - Add success message when extraction completes This will help identify whether the issue is: - xz command failing - Disk space/permissions issues - File not being created despite successful xz return code The detailed error output will make debugging much easier.
Add file existence check and ls output right before the dd command to diagnose why the image file appears to exist after extraction but is not found when expand_image tries to use it. This will show: - Whether the file still exists when expand_image is called - File permissions and ownership - Directory contents if file is missing - Actual dd error output (2>&1) This should help identify if the issue is: - File being deleted between functions - Permission/ownership problems - Path/quoting issues - dd-specific access problems
Fix critical bug where print_step/print_error/print_warning output was contaminating function return values, causing expand_image to receive a path containing ANSI color codes and multiple log lines instead of just the image file path. Changes: - Redirect all print_step() output to stderr (>&2) - Redirect all print_error() output to stderr (>&2) - Redirect all print_warning() output to stderr (>&2) This ensures that when functions like download_pi_os() use echo to return values, only the actual return value appears on stdout, not the status messages. Before: $img_file contained colored log messages + path After: $img_file contains only the clean path This fixes the "No such file or directory" error in expand_image.
Add comprehensive improvements to the Raspberry Pi Image Builder: **Pi Model Selection:** - Add dropdown to select between Raspberry Pi 3, 4, and 5 - Build script automatically selects correct OS image based on model: * Pi 3: 32-bit armhf Raspberry Pi OS Lite * Pi 4: 64-bit arm64 Raspberry Pi OS Lite * Pi 5: 64-bit arm64 Raspberry Pi OS Lite (default) - Pass PI_MODEL environment variable to build script **Improved Output Formatting:** - Terminal-style dark theme (black background, light text) - Left-justified text with no wrapping for cleaner display - Monospace font (Courier New/Consolas) for better readability - Wider display area with horizontal scrolling - More professional appearance matching terminal output **Real-time Progress Updates:** - Parse wget download progress (0-100%) and map to overall progress - Show extraction progress with detailed steps - Track dd (image expansion) progress - Monitor compression progress - More granular progress reporting (5%, 16%, 20%, 22%, 25%, etc.) - Clear progress messages for each build phase **User Experience:** - Cleaner, more professional build output display - Better visibility into what's happening during build - Easier to spot errors with improved formatting - Model selection makes it clear which Pi is being targeted All improvements maintain backward compatibility with existing functionality.
- Added verbose (-v) flag to xz extraction for real-time progress output - Enhanced progress parser to detect xz extraction percentage updates - Fixed compression logic to only compress when output filename ends with .xz - Remove existing compressed file before compression to prevent "File exists" error - Improved progress tracking for extraction phase (16-20% of total progress)
Pi Image Builder improvements: - Fix real-time progress parsing bug (referenced non-existent output_text) - Add support for carriage return progress updates (xz in-place updates) - Maintain recent output buffer in thread for context checking - Extract progress parsing logic to reusable helper method - Progress now updates in real-time during download/extraction/compression SD Card Writer improvements: - Add "Recent Images" button to quickly select recently created images - Search common locations: home, Downloads, /tmp/lablink-pi-build, Desktop - Show images sorted by modification time with size and date info - Auto-detect images from Pi Image Builder output Fixes issue where progress would show all at once after completion instead of updating in real-time during operations.
The previous implementation was consuming lines with \r (carriage returns) and only parsing progress without emitting the output to the user. This caused important build output to be hidden, making the logs incomplete and jumbled. Reverted to simple newline-based processing. Progress updates will still work through the xz verbose output, they just may not update AS frequently (which is acceptable for readable output). The "expect" package is unrelated - this was a bug in the CR handling logic.
Major improvements to the build output readability: 1. Strip ANSI escape codes - Remove color codes like [0m, [0;32m from display 2. Smart CR/LF handling - Properly handle both \r and \n line terminators - \r (carriage return) for progress updates - parse but don't spam output - \n (newline) for complete lines - display normally 3. Filter noise - Skip empty lines and resize2fs debug output 4. Better progress parsing - Track CR lines for progress without cluttering output This fixes the issues where: - ANSI color codes appeared as literal text - Blank lines from \r handling - Progress lines appeared fragmented - Missing download/extraction/compression steps in output - Output not representative of current workflow status Now the output will be clean, readable, and show actual build progress.
The previous implementation was filtering out too much content: - Lines were being stripped too early, causing content loss - Empty line check was catching meaningful whitespace-only formatting - All \r (carriage return) lines were hidden completely Changes: - Strip for checking but emit original line with formatting preserved - Only filter resize2fs noise and truly empty stripped lines - CR lines still parsed for progress but not displayed to avoid spam - All newline-terminated lines now shown (the actual build steps) This should show all meaningful build output including the build script's formatted headers, step messages, and completion status.
The previous complex \r vs \n handling was hiding too much output. Major simplification: - Treat both \r and \n as line terminators (with CRLF support) - Show ALL non-empty lines (except resize2fs debug noise) - Remove is_final logic that was hiding CR-terminated lines - Strip ANSI codes from all output - Parse progress from all lines This means: - wget download lines with \r will be visible - xz extraction lines with \r will be visible - All build script step messages will be visible - Progress bar still updates from parsed percentages The output may have more lines now (including download/extraction progress updates), but at least nothing will be hidden and the user can see what's actually happening during the build.
Pi Image Builder: - Add text-align: left to build output QTextEdit styling - Ensures text is properly left-justified in the window SD Card Writer image search: - Add ~/LabLink/ directory to search paths - Search for ~/LabLink-* directories (case-insensitive) - Automatically finds LabLink, LabLink-test, LabLink-testing, etc. - Update "No Images Found" dialog to show new search locations The image finder will now search: - Home directory - ~/LabLink/ - ~/LabLink-* (any case variation) - Downloads - /tmp/lablink-pi-build - Desktop
Major fix for image visibility issue: Problem: Images were created with root ownership (via pkexec) making them invisible/inaccessible to the normal user in file browsers Solution: 1. Python launcher now passes current user info to build script: - SUDO_USER: username - ORIGINAL_UID: user ID - ORIGINAL_GID: group ID 2. Build script now fixes ownership after image creation: - Detects if running as root - Uses ORIGINAL_UID:ORIGINAL_GID to chown image files - Falls back to SUDO_USER if needed - Applies to both .img and .sha256 files Now the built images will be: - Visible in file browsers - Accessible to the user who built them - Selectable via "Recent Images" in SD Card Writer
The image was silently failing to be created due to missing error handling in the finalize_image() function. Changes: 1. Create output directory if it doesn't exist 2. Add error checking on cp command with detailed error messages 3. Fix ownership of output directory (not just the file) 4. Verify file is readable after creation 5. Better error messages to diagnose copy failures Now if the image copy fails, we'll see exactly what went wrong instead of silently succeeding with no output file. This should help debug why images aren't appearing in /home/x9x0/
Problem: The build script was ignoring the OUTPUT_IMAGE path specified by the GUI launcher and using a default filename with date instead. Root cause: Script was using command-line argument instead of checking the OUTPUT_IMAGE environment variable set by the Python launcher. Now checks OUTPUT_IMAGE env var first, then command-line arg, then default. This ensures images are created at the user-specified path (e.g., /home/x9x0/lablink-pi.img) instead of in an unknown location.
Problem: SD Card Writer was checking if the entire app was running as root and refusing to work if not, telling users to run with sudo. Solution: 1. Remove the root privilege check from _write_image() 2. Update _write_unix() to use pkexec instead of sudo 3. Auto-detect pkexec availability, fall back to sudo if needed 4. pkexec provides graphical password prompt (no terminal needed) Now users can: - Run LabLink normally (not as root) - Write SD cards via the GUI - Get a graphical password prompt when needed - Works the same way as the Pi Image Builder This matches the UX of the image builder which already uses pkexec.
…erly Problem: SD card writes were failing silently with 'dd failed:' but no error message. The issue was that stderr from dd wasn't being captured when using pkexec. Root cause: When running 'pkexec dd ...', the stderr stream is consumed by pkexec's authentication dialog and not available to the Python process. Solution: 1. Create a temporary bash wrapper script (like image builder does) 2. Redirect stderr to stdout in the script (exec 2>&1) 3. Run 'pkexec bash /tmp/script.sh' instead of direct dd command 4. Capture all output (stdout + stderr merged) 5. Parse progress from combined output 6. Show last 10 lines of output on error for debugging Now error messages will be visible and dd progress should work correctly.
Fixed SyntaxWarning about invalid escape sequence '\P' in the ImageWriterThread.__init__ docstring. Changed the docstring to a raw string (r""") to properly handle the Windows path example: \\.\PhysicalDrive1 The backslashes in Windows device paths need to be in a raw string or properly escaped to avoid Python interpreting them as escape sequences.
…iting Problem: DD was failing with 'No medium found' even though the SD card device existed and was visible in lsblk. This appeared to be a race condition after unmounting. Root cause: After unmounting partitions, the kernel needs time to re-read the partition table and settle before the device is fully accessible for raw writes. Solution: 1. Add 1 second delay after unmount in Python code 2. Add sync and blockdev --rereadpt in wrapper script 3. Add 2 second delay after blockdev before dd 4. Verify device is a block device before attempting write 5. Add final sync after dd to ensure all writes are flushed This gives the kernel time to: - Release the mounted filesystem - Re-read the partition table - Make the device fully accessible for raw block writes Should fix 'No medium found' errors when writing to SD cards.
The SD card write works perfectly but verification fails with 'Permission denied' because it runs in the Python thread without elevated privileges. Changes: - Disable verification checkbox by default (was checked) - Update label to indicate it requires root access - Add tooltip explaining why it might fail - Keep option available for users who run app with sudo The write operation completes successfully - verification is just a safety check and not critical. Users can enable it if needed, but it will fail unless the entire app runs with sudo. Verified working: Image successfully written to SD card with real-time progress updates (4.58 GB in ~70 seconds).
…on boot Problem: Raspberry Pi 4 image booted to blue screen asking for username instead of using pre-configured 'admin' user. Root cause: Modern Raspberry Pi OS (2022+) doesn't create a default user for security reasons. The build script wasn't creating the admin user, and the first boot script referenced non-existent 'pi' user. Changes: 1. configure_first_boot() now creates admin user in chroot: - Uses useradd with all necessary groups (sudo, gpio, i2c, spi, etc.) - Sets password from LABLINK_ADMIN_PASSWORD or default 'lablink' - Adds sudoers entry for passwordless sudo - Copies qemu for ARM emulation 2. Fixed first boot script to use 'admin' instead of 'pi': - Changed 'usermod -aG docker pi' to 'usermod -aG docker admin' Now the Pi will boot directly to login prompt with: - Username: admin - Password: lablink (or custom password from builder) No more blue screen asking for username configuration!
The SD card writer was experiencing intermittent "No medium found" errors even when the device was detected. This adds comprehensive device readiness checking with: - Device readiness check function with 10 retry attempts - Uses blockdev --getsize64 to verify medium is actually present - Retry logic with 1-second delays between attempts - Clear error messages suggesting SD card reader troubleshooting - Final verification right before write operation - Updated progress messages to show readiness check phase This should resolve intermittent write failures caused by the device not being fully ready when dd attempts to open it.
The verification and eject operations were failing with "Permission denied" because they tried to access the device without root privileges. This commit fixes both issues: **Verification fixes:** - Rewrote _verify_write() to use pkexec/sudo like the write operation - Uses 'cmp' command to compare first 100MB of image with device - Runs in a temporary bash script with elevated privileges - Updated checkbox label to indicate it compares first 100MB - Updated tooltip to explain password requirement **Eject fixes:** - Updated _safely_eject() to use pkexec/sudo for the eject command - Captures output to suppress permission denied warnings - Changed final message to "SD card ready to remove" Both operations now work correctly without permission errors.
The Raspberry Pi was booting to the first-boot wizard (piwiz) asking for keyboard layout and other settings. This adds pre-configuration to skip the wizard entirely: - Remove piwiz from autostart (/etc/xdg/autostart/piwiz.desktop) - Create .piwiz_done sentinel file in admin home directory - Pre-configure locale to en_US.UTF-8 - Pre-configure keyboard layout to US (pc105) - Pre-configure timezone to America/New_York - Set WiFi country to US in wpa_supplicant.conf The Pi should now boot directly to the desktop with admin user logged in, no configuration wizard.
The wizard was still appearing despite previous fixes. This adds multiple complementary methods to ensure the wizard is completely disabled: **Additional wizard disabling methods:** 1. Create piwiz.desktop with Hidden=true as override 2. Disable userconfig.service systemd unit 3. Create userconf.txt/userconf in boot partition with encrypted password 4. Add .piwiz_done sentinel file in admin home directory **Auto-login configuration:** - Configure lightdm to auto-login admin user with no delay - Configure getty@tty1 for console auto-login as fallback - This ensures admin is logged in immediately on boot The combination of disabling all wizard services and enabling auto-login should completely bypass the first-boot keyboard/locale selection wizard.
The first-boot service was failing and blocking boot due to network timing issues. This makes it much more robust: **Script improvements:** - Removed `set -e` to prevent failing on first error - Added wait_for_network() function with 30 retry attempts - Added proper error handling for each step (apt, docker, download) - Logs to /var/log/lablink-first-boot.log for debugging - Gracefully exits if network unavailable with helpful message - Only fails if critical steps (Docker, LabLink download) fail **Service improvements:** - Added `SuccessExitStatus=0 1` to not fail boot on errors - Added `RemainAfterExit=no` for proper cleanup - Waits for multi-user.target to ensure system is ready The Pi will now boot successfully even if LabLink setup fails, and users can check /var/log/lablink-first-boot.log to see what happened.
The WiFi was not connecting on first boot. This fixes the configuration by: **WiFi configuration improvements:** - Use wpa_passphrase to generate encrypted PSK (more reliable than plaintext) - Write WiFi config to BOTH /boot/wpa_supplicant.conf AND /etc/wpa_supplicant/wpa_supplicant.conf - Remove duplicate WiFi country configuration that was being overwritten - Fallback to plaintext password if wpa_passphrase is not available - Create basic wpa_supplicant.conf even when no WiFi credentials provided **Why this works:** - Boot partition config is used by first-boot scripts - Rootfs config is used directly if boot partition copy fails - Encrypted PSK is more compatible with modern wpa_supplicant - Dual placement ensures WiFi works regardless of boot mechanism This should fix the network connectivity issues preventing the LabLink first-boot setup from running.
The WiFi configuration wasn't working because: 1. Modern Pi OS may use /boot/firmware/ subdirectory 2. No debug output showing where files were written 3. Only wrote to one boot location **Improvements:** - Write wpa_supplicant.conf to boot partition root - Also copy to /boot/firmware/ subdirectory if it exists - Write to rootfs /etc/wpa_supplicant/wpa_supplicant.conf - Add detailed logging showing exact paths where files are written - Log the SSID being configured for verification - Same triple-write approach for both encrypted and plaintext configs This ensures WiFi config works on all Raspberry Pi OS versions (older and newer boot partition layouts) and makes debugging easier with detailed logs.
Found critical bug where GUI was using wrong environment variable names: - GUI was setting PI_HOSTNAME but build script expects LABLINK_HOSTNAME - GUI was setting ADMIN_PASSWORD but build script expects LABLINK_ADMIN_PASSWORD This caused: 1. Hostname to always default to "lablink" instead of user-specified value 2. Admin password to default to "lablink" instead of user-specified value **Fixed in both places:** - Direct environment variable passing (line 121, 129) - pkexec wrapper script generation (line 159, 172) Now matches build script expectations: - LABLINK_HOSTNAME (used in build-pi-image.sh:45, 394, 397) - LABLINK_ADMIN_PASSWORD (used in build-pi-image.sh:49, 222, 495)
The build script was entering interactive mode even when running via pkexec with environment variables already set. This caused it to: 1. Prompt for input (which fails silently via pkexec) 2. Use empty input, which defaulted all values 3. Overwrite the environment variables we carefully set in the GUI Root cause: Line 737 checked `if [ -t 0 ] && [ $# -eq 0 ]` - When running via pkexec, stdin IS a terminal (the auth dialog) - So it entered interactive mode - And overwrote LABLINK_HOSTNAME, WIFI_SSID, etc. with defaults **Fix:** Added check for LABLINK_HOSTNAME environment variable: ```bash if [ -t 0 ] && [ $# -eq 0 ] && [ -z "$LABLINK_HOSTNAME" ]; then ``` Now it only prompts if LABLINK_HOSTNAME is NOT already set via environment. This preserves the values set by the GUI wrapper script. This fixes: - Hostname always being "lablink" instead of user-specified value - WiFi credentials never being applied - Admin password always being default "lablink"
Modern Raspberry Pi OS (Bookworm 2024+) uses NetworkManager by default, which doesn't automatically read /etc/wpa_supplicant/wpa_supplicant.conf. The WiFi config was being written correctly, but NetworkManager wasn't picking it up, requiring manual WPS connection on first boot. **Changes:** - Added Method 4: Create NetworkManager connection file (.nmconnection) - Extracts PSK hash from wpa_passphrase output for encrypted config - Places file in /etc/NetworkManager/system-connections/ - Sets strict 600 permissions (required by NetworkManager) - Configures autoconnect=true and priority=1 for automatic connection - Generates UUID for connection uniqueness - Applies to both encrypted PSK and plaintext fallback paths **Now creates 4 WiFi configs for maximum compatibility:** 1. /boot/wpa_supplicant.conf (boot partition) 2. /boot/firmware/wpa_supplicant.conf (newer boot location) 3. /etc/wpa_supplicant/wpa_supplicant.conf (traditional config) 4. /etc/NetworkManager/system-connections/*.nmconnection (modern NetworkManager) This ensures WiFi auto-connects on first boot on all Raspberry Pi OS versions.
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.
Summary
Comprehensive fixes to the Raspberry Pi Image Builder and SD Card Writer workflow, resolving critical WiFi auto-connect issues, configuration persistence bugs, and device handling reliability problems.
Critical Issues Fixed
🔧 WiFi Auto-Connect (Primary Fix)
Problem: WiFi credentials written to image but Pi didn't auto-connect on first boot
Root Cause: Modern Raspberry Pi OS Bookworm (2024+) uses NetworkManager by default, which doesn't read traditional
/etc/wpa_supplicant/wpa_supplicant.conffilesSolution:
.nmconnection) creation/boot/wpa_supplicant.conf(legacy boot partition)/boot/firmware/wpa_supplicant.conf(newer boot location)/etc/wpa_supplicant/wpa_supplicant.conf(traditional wpa_supplicant)/etc/NetworkManager/system-connections/*.nmconnection(NetworkManager - this was missing!)wpa_passphrasefor securityautoconnect=trueandautoconnect-priority=1🏷️ Environment Variable Bugs
Problem: Hostname always defaulted to "lablink", WiFi credentials ignored, admin password not applied
Root Causes:
PI_HOSTNAME/ADMIN_PASSWORD, but script expectedLABLINK_HOSTNAME/LABLINK_ADMIN_PASSWORDSolutions:
pi_image_builder.pyto match script expectationsLABLINK_HOSTNAMEis already set🎛️ First-Boot Configuration
Problem: Pi booted to setup wizard asking for keyboard layout, username, etc.
Solution:
💾 SD Card Writer Reliability
Problem: Intermittent "No medium found" errors when writing to SD card
Solution:
blockdev --getsize64Files Changed
build-pi-image.sh- WiFi NetworkManager config, environment variables, interactive mode skip, first-boot setupclient/ui/pi_image_builder.py- Environment variable names corrected (LABLINK_HOSTNAME,LABLINK_ADMIN_PASSWORD)client/ui/sd_card_writer.py- Device readiness checking, verification, eject with proper privilegesTesting Performed
✅ Built image with WiFi credentials and custom hostname "lablink-pi"
✅ WiFi auto-connects on first boot without manual intervention
✅ Hostname correctly set to user-specified value
✅ Admin user auto-logs in with no wizard prompts
✅ SD card writer handles intermittent device issues gracefully
✅ Verification and eject work without permission errors
Impact
Before: Users had to manually configure WiFi via WPS/GUI on first boot, wizard prompts blocked setup
After: Complete end-to-end automation - Build → Write → Boot → Auto-connect → LabLink ready
Users can now create fully pre-configured Raspberry Pi images that boot directly to a working LabLink installation with network connectivity.