testsAndMisc/linux_configuration/features/lib/nc_remote_flash.sh
Krzysztof kuhy Rudnicki 4f2b0ccdf7 Dissolve linux_configuration/scripts/ and single_use/
Two levels of pure taxonomy carrying no information, and a 2-directory
cap leaves no room for them. Categories now sit directly under
linux_configuration/: features, fixes, utils, misc, fresh-install,
installers, diagnostics, gaming, boot_recovery, periodic_background, lib,
meta, tests. A script's own lib/ and tests/ are exempt from the cap, so
those stay put.

Depth violations 248 -> 139; roughly 40 of the remainder are inside
digital_wellbeing and hosts, deferred until the guard-lib repoint.

The moves are the easy half. What breaks is every path that counted
levels, and three classes of it had to be found by resolving paths rather
than reading diffs:

  - 52 relative `source` lines for lib/common.sh and lib/android.sh. The
    correct prefix differs per file: 40 were ../../ and 12 were ../../../,
    so a blanket replace would have fixed one set and broken the other.
  - REPO_ROOT in 7 fixes/lib/tests harnesses and setup_syncyomi_guard.sh,
    each walking up four levels from a directory now two levels shallower.
    This caused a real regression -- the fixes suite went red with
    "log_warn: command not found" -- caught by running it and comparing
    against the pre-move baseline worktree.
  - conftest.py's sys.path entries, which took the pytest gate down with
    14 collection errors, and the `# shellcheck source=` directives, which
    took shellcheck down with SC1091.

16 symlinks in $HOME broke, including the entire i3blocks bar. All
repointed, and cpu_monitor/memory/disk/time/volume were run to confirm
they emit real output rather than merely resolving.

Verified: 1798 tests pass at 100% coverage, 8 of 9 lib/tests suites pass
(the ninth needs the coverage jail, identically at baseline), pre-commit
is clean, and the two untracked secret configs are still ignored after
their gitignore paths changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
2026-08-23 18:53:30 +02:00

168 lines
5.1 KiB
Bash
Executable File

#!/bin/bash
# Flashing from a remote laptop, and executing it there.
#
# Sourced by setup_nextcloud_raspberry.sh; split out to keep nc_remote.sh under the
# 250-line cap. Sourced rather than run, so it inherits the caller's
# strict mode and the variables defined above the source line.
phase_flash_remote() {
log_info "=== Phase 1B: Flash Raspberry Pi OS to SD Card on Remote Laptop ==="
# Discover and select remote laptop
discover_remote_laptop
# Set up SSH key authentication
setup_ssh_key_to_remote "$REMOTE_LAPTOP_IP" "$REMOTE_LAPTOP_USER"
local remote="${REMOTE_LAPTOP_USER}@${REMOTE_LAPTOP_IP}"
# Check for SD card on remote laptop
log_info "Checking for SD card on remote laptop..."
echo "Block devices on ${remote}:"
ssh "$remote" "lsblk -d -o NAME,SIZE,TYPE,RM,TRAN,MODEL" || true
echo
# Auto-detect SD card on remote laptop
log_info "Auto-detecting SD card on remote laptop..."
local sd_device
sd_device=$(ssh "$remote" "lsblk -d -o NAME,RM,TRAN | grep -E '1.*(usb|mmc)' | awk '{print \"/dev/\"\$1}' | head -1" 2>/dev/null || true)
if [[ -z $sd_device ]]; then
die "No SD card detected on remote laptop. Please insert an SD card and try again."
fi
# Get size for confirmation
local sd_info
# shellcheck disable=SC2029 # Intentional client-side expansion
sd_info=$(ssh "$remote" "lsblk -d -o NAME,SIZE,MODEL $sd_device 2>/dev/null | tail -1" || true)
log_success "Auto-detected SD card: $sd_device ($sd_info)"
SD_CARD_DEVICE="$sd_device"
# Verify device exists on remote
# shellcheck disable=SC2029 # Intentional client-side expansion
if ! ssh "$remote" "[[ -b '$SD_CARD_DEVICE' ]]" 2>/dev/null; then
die "Device $SD_CARD_DEVICE does not exist on remote laptop"
fi
# Auto-generate Pi password if not set
auto_generate_pi_password
log_success "Pi user '$PI_USER' password: $PI_PASSWORD"
# Generate encrypted password locally
local encrypted_password
encrypted_password=$(echo "$PI_PASSWORD" | openssl passwd -6 -stdin)
# Save config now so password is stored
save_config
# Copy this script to remote laptop
log_info "Copying script to remote laptop..."
scp "$0" "${remote}:/tmp/setup_nextcloud_raspberry.sh"
# Execute flash on remote laptop
log_info "Executing flash on remote laptop..."
log_warning "This will ERASE ALL DATA on ${SD_CARD_DEVICE} on the remote laptop!"
log_info "Proceeding automatically in 5 seconds... (Ctrl+C to cancel)"
sleep 5
# Run the flash process on remote laptop
# We pass the pre-encrypted password to avoid interactive prompts
# Using -tt to force TTY allocation even without local tty
ssh -tt "$remote" "sudo SD_CARD_DEVICE='$SD_CARD_DEVICE' PI_USER='$PI_USER' PI_HOSTNAME='$PI_HOSTNAME' bash /tmp/setup_nextcloud_raspberry.sh flash-remote-execute '$encrypted_password'"
log_success "Phase 1B complete!"
echo
log_info "Next steps:"
log_info "1. Remove SD card from the laptop and insert into Raspberry Pi 5"
log_info "2. Connect the Pi to power and network"
log_info "3. Wait 2-3 minutes for first boot"
log_info "4. Run: ./setup_nextcloud_raspberry.sh configure (on Pi) or all-remote"
}
# This is called on the remote laptop by phase_flash_remote
phase_flash_remote_execute() {
check_root
local encrypted_password="${1:-}"
log_info "=== Executing Flash on Remote Laptop ==="
if [[ -z $SD_CARD_DEVICE ]]; then
die "SD_CARD_DEVICE not set"
fi
# Download and flash
local image_path
image_path=$(download_raspberry_pi_os)
# Unmount any mounted partitions
log_info "Unmounting partitions on $SD_CARD_DEVICE..."
for partition in "${SD_CARD_DEVICE}"*; do
if mountpoint -q "$partition" 2>/dev/null || mount | grep -q "$partition"; then
umount "$partition" 2>/dev/null || true
fi
done
log_info "Flashing image to SD card..."
dd if="$image_path" of="$SD_CARD_DEVICE" bs=4M status=progress conv=fsync
sync
log_success "Image flashed successfully!"
# Configure headless boot
log_info "Configuring headless boot..."
sleep 2
partprobe "$SD_CARD_DEVICE" 2>/dev/null || true
sleep 2
# Mount boot partition
local boot_partition
if [[ -b "${SD_CARD_DEVICE}1" ]]; then
boot_partition="${SD_CARD_DEVICE}1"
elif [[ -b "${SD_CARD_DEVICE}p1" ]]; then
boot_partition="${SD_CARD_DEVICE}p1"
else
die "Could not find boot partition"
fi
local boot_mount="/tmp/rpi-boot"
mkdir -p "$boot_mount"
mount "$boot_partition" "$boot_mount"
# Enable SSH
touch "$boot_mount/ssh"
log_success "SSH enabled"
# Create userconf.txt for first user
if [[ -n $encrypted_password ]]; then
echo "${PI_USER}:${encrypted_password}" >"$boot_mount/userconf.txt"
log_success "User '$PI_USER' configured"
fi
# Set hostname on root partition
local root_partition
if [[ -b "${SD_CARD_DEVICE}2" ]]; then
root_partition="${SD_CARD_DEVICE}2"
elif [[ -b "${SD_CARD_DEVICE}p2" ]]; then
root_partition="${SD_CARD_DEVICE}p2"
fi
if [[ -n $root_partition ]]; then
local root_mount="/tmp/rpi-root"
mkdir -p "$root_mount"
mount "$root_partition" "$root_mount"
echo "$PI_HOSTNAME" >"$root_mount/etc/hostname"
sed -i "s/raspberrypi/$PI_HOSTNAME/g" "$root_mount/etc/hosts"
log_success "Hostname set to '$PI_HOSTNAME'"
umount "$root_mount"
fi
umount "$boot_mount"
sync
log_success "SD card configured for headless boot!"
}