4.1.4: QR sharing, venv tarball, flash script improvements
QR Channel Key Sharing: - Admin-only QR generator in about.html (was visible to all) - QR button for saved keys on account page - Fixed about() route missing channel status vars (bug) Pi Build Optimization: - Pre-built venv tarball support (39MB zstd, skips 20+ min compile) - setup.sh auto-detects and extracts tarball if present - Strip __pycache__/tests before tarball (295MB → 208MB) Flash Script Improvements: - flash-image.sh now uses config.json for headless WiFi setup - Consistent wipe prompt on both flash scripts - pull-image.sh re-enables auto-expand before shrinking Build Docs: - Added zstd and jq to pre-setup apt-get - Documented fast build option with pre-built venv 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -26,8 +26,8 @@ ssh admin@stegasoo.local
|
||||
# Take ownership of /opt (for pyenv, jpegio builds)
|
||||
sudo chown admin:admin /opt
|
||||
|
||||
# Install git (not included in Lite image)
|
||||
sudo apt-get update && sudo apt-get install -y git
|
||||
# Install git and zstd (not included in Lite image)
|
||||
sudo apt-get update && sudo apt-get install -y git zstd jq
|
||||
```
|
||||
|
||||
## Step 4: Clone & Run Setup
|
||||
@@ -39,7 +39,22 @@ cd stegasoo
|
||||
./rpi/setup.sh
|
||||
```
|
||||
|
||||
This takes ~15-20 minutes and installs:
|
||||
### Fast Build Option (with pre-built venv)
|
||||
|
||||
If you have `stegasoo-venv-pi-arm64.tar.zst` from a previous build:
|
||||
|
||||
```bash
|
||||
cd /opt
|
||||
git clone -b 4.1 https://github.com/adlee-was-taken/stegasoo.git stegasoo
|
||||
|
||||
# Copy pre-built venv (from your host machine)
|
||||
# On host: scp rpi/stegasoo-venv-pi-arm64.tar.zst admin@stegasoo.local:/opt/stegasoo/rpi/
|
||||
|
||||
cd stegasoo
|
||||
./rpi/setup.sh # Detects tarball, extracts instead of compiling (~2 min vs 20+)
|
||||
```
|
||||
|
||||
**Standard build** takes ~15-20 minutes and installs:
|
||||
- Python 3.12 via pyenv
|
||||
- jpegio (patched for ARM)
|
||||
- Stegasoo with web UI
|
||||
|
||||
@@ -8,9 +8,14 @@
|
||||
# Supports: .img, .img.zst, .img.xz, .img.gz, .img.zst.zip (GitHub release format)
|
||||
# If device is specified, skips auto-detection (useful for NVMe/large drives)
|
||||
#
|
||||
# Optional: Place config.json in same directory for headless WiFi setup
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
CONFIG_FILE="$SCRIPT_DIR/config.json"
|
||||
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
@@ -18,6 +23,28 @@ BLUE='\033[0;34m'
|
||||
BOLD='\033[1m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Load config if present (optional - for headless WiFi setup)
|
||||
HAS_CONFIG=false
|
||||
if [ -f "$CONFIG_FILE" ] && command -v jq &> /dev/null; then
|
||||
WIFI_SSID=$(jq -r '.wifiSSID // empty' "$CONFIG_FILE")
|
||||
WIFI_PASS=$(jq -r '.wifiPassword // empty' "$CONFIG_FILE")
|
||||
WIFI_COUNTRY=$(jq -r '.wifiCountry // "US"' "$CONFIG_FILE")
|
||||
PI_HOSTNAME=$(jq -r '.hostname // empty' "$CONFIG_FILE")
|
||||
if [ -n "$WIFI_SSID" ] && [ -n "$WIFI_PASS" ]; then
|
||||
HAS_CONFIG=true
|
||||
echo -e "${GREEN}Found config.json - will configure WiFi after flash${NC}"
|
||||
echo -e " WiFi: ${YELLOW}$WIFI_SSID${NC}"
|
||||
if [ -n "$PI_HOSTNAME" ]; then
|
||||
echo -e " Hostname: ${YELLOW}$PI_HOSTNAME${NC}"
|
||||
fi
|
||||
echo ""
|
||||
fi
|
||||
elif [ -f "$CONFIG_FILE" ]; then
|
||||
echo -e "${YELLOW}Note: config.json found but jq not installed (apt install jq)${NC}"
|
||||
echo -e "${YELLOW} WiFi will need to be configured manually after boot${NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Check for required tools
|
||||
for cmd in dd lsblk; do
|
||||
if ! command -v $cmd &> /dev/null; then
|
||||
@@ -222,6 +249,17 @@ if [ -n "$MOUNTED" ]; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Ask about wiping
|
||||
echo
|
||||
read -p "Wipe partition table first? (recommended if having issues) [y/N] " wipe_confirm
|
||||
if [[ "$wipe_confirm" =~ ^[Yy]$ ]]; then
|
||||
echo "Wiping partition table..."
|
||||
sudo wipefs -a "$SELECTED"
|
||||
sudo dd if=/dev/zero of="$SELECTED" bs=1M count=10 status=none
|
||||
sync
|
||||
echo " Wiped clean"
|
||||
fi
|
||||
|
||||
# Final confirmation
|
||||
echo -e "${RED}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${RED}║ WARNING: ALL DATA ON THIS DEVICE WILL BE DESTROYED! ║${NC}"
|
||||
@@ -284,6 +322,109 @@ echo ""
|
||||
echo -e "${GREEN}Syncing...${NC}"
|
||||
sync
|
||||
|
||||
# Inject WiFi config if config.json was loaded
|
||||
if [ "$HAS_CONFIG" = true ]; then
|
||||
echo ""
|
||||
echo -e "${GREEN}Configuring WiFi from config.json...${NC}"
|
||||
|
||||
# Wait for partitions to appear
|
||||
sleep 2
|
||||
partprobe "$SELECTED" 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
# Determine boot partition
|
||||
if [[ "$SELECTED" == *"nvme"* ]] || [[ "$SELECTED" == *"mmcblk"* ]]; then
|
||||
BOOT_PART="${SELECTED}p1"
|
||||
else
|
||||
BOOT_PART="${SELECTED}1"
|
||||
fi
|
||||
|
||||
if [ -b "$BOOT_PART" ]; then
|
||||
MOUNT_DIR=$(mktemp -d)
|
||||
if mount "$BOOT_PART" "$MOUNT_DIR" 2>/dev/null; then
|
||||
# Create firstrun.sh for WiFi setup
|
||||
cat > "$MOUNT_DIR/firstrun.sh" << 'EOFSCRIPT'
|
||||
#!/bin/bash
|
||||
set +e
|
||||
|
||||
# Set hostname if provided
|
||||
if [ -n "PLACEHOLDER_HOSTNAME" ] && [ "PLACEHOLDER_HOSTNAME" != "" ]; then
|
||||
CURRENT_HOSTNAME=$(cat /etc/hostname | tr -d " \t\n\r")
|
||||
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
|
||||
/usr/lib/raspberrypi-sys-mods/imager_custom set_hostname PLACEHOLDER_HOSTNAME
|
||||
else
|
||||
echo PLACEHOLDER_HOSTNAME >/etc/hostname
|
||||
sed -i "s/127.0.1.1.*$CURRENT_HOSTNAME/127.0.1.1\tPLACEHOLDER_HOSTNAME/g" /etc/hosts
|
||||
fi
|
||||
fi
|
||||
|
||||
# Configure WiFi
|
||||
if [ -f /usr/lib/raspberrypi-sys-mods/imager_custom ]; then
|
||||
/usr/lib/raspberrypi-sys-mods/imager_custom set_wlan 'PLACEHOLDER_SSID' 'PLACEHOLDER_WIFIPASS' 'PLACEHOLDER_COUNTRY'
|
||||
else
|
||||
# NetworkManager method (Trixie)
|
||||
cat >/etc/NetworkManager/system-connections/preconfigured.nmconnection <<'NMEOF'
|
||||
[connection]
|
||||
id=preconfigured
|
||||
type=wifi
|
||||
autoconnect=true
|
||||
|
||||
[wifi]
|
||||
mode=infrastructure
|
||||
ssid=PLACEHOLDER_SSID
|
||||
|
||||
[wifi-security]
|
||||
auth-alg=open
|
||||
key-mgmt=wpa-psk
|
||||
psk=PLACEHOLDER_WIFIPASS
|
||||
|
||||
[ipv4]
|
||||
method=auto
|
||||
|
||||
[ipv6]
|
||||
method=auto
|
||||
NMEOF
|
||||
chmod 600 /etc/NetworkManager/system-connections/preconfigured.nmconnection
|
||||
rfkill unblock wifi
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
rm -f /boot/firstrun.sh
|
||||
rm -f /boot/firmware/firstrun.sh
|
||||
sed -i 's| systemd.run.*||g' /boot/cmdline.txt 2>/dev/null
|
||||
sed -i 's| systemd.run.*||g' /boot/firmware/cmdline.txt 2>/dev/null
|
||||
exit 0
|
||||
EOFSCRIPT
|
||||
|
||||
# Replace placeholders
|
||||
sed -i "s/PLACEHOLDER_SSID/$WIFI_SSID/g" "$MOUNT_DIR/firstrun.sh"
|
||||
sed -i "s/PLACEHOLDER_WIFIPASS/$WIFI_PASS/g" "$MOUNT_DIR/firstrun.sh"
|
||||
sed -i "s/PLACEHOLDER_COUNTRY/$WIFI_COUNTRY/g" "$MOUNT_DIR/firstrun.sh"
|
||||
if [ -n "$PI_HOSTNAME" ]; then
|
||||
sed -i "s/PLACEHOLDER_HOSTNAME/$PI_HOSTNAME/g" "$MOUNT_DIR/firstrun.sh"
|
||||
else
|
||||
sed -i "s/PLACEHOLDER_HOSTNAME//g" "$MOUNT_DIR/firstrun.sh"
|
||||
fi
|
||||
chmod +x "$MOUNT_DIR/firstrun.sh"
|
||||
|
||||
# Update cmdline.txt to run firstrun.sh
|
||||
CMDLINE="$MOUNT_DIR/cmdline.txt"
|
||||
if [ -f "$CMDLINE" ]; then
|
||||
CURRENT=$(cat "$CMDLINE" | tr -d '\n' | sed 's| systemd.run.*||g')
|
||||
echo "$CURRENT systemd.run=/boot/firmware/firstrun.sh systemd.run_success_action=reboot systemd.unit=kernel-command-line.target" > "$CMDLINE"
|
||||
fi
|
||||
|
||||
umount "$MOUNT_DIR"
|
||||
echo -e " ${GREEN}✓${NC} WiFi configured for: $WIFI_SSID"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Could not mount boot partition"
|
||||
fi
|
||||
rmdir "$MOUNT_DIR" 2>/dev/null || true
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Boot partition not found"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════╗${NC}"
|
||||
echo -e "${GREEN}║ Flash Complete! ║${NC}"
|
||||
@@ -291,5 +432,11 @@ echo -e "${GREEN}╚════════════════════
|
||||
echo ""
|
||||
echo -e "You can now remove the SD card and boot your Raspberry Pi."
|
||||
echo ""
|
||||
echo -e "${YELLOW}Tip:${NC} On first boot, SSH in and the setup wizard will run automatically."
|
||||
if [ "$HAS_CONFIG" = true ]; then
|
||||
echo -e "${GREEN}WiFi pre-configured${NC} - Pi will connect to $WIFI_SSID on boot"
|
||||
echo -e "SSH: ${YELLOW}ssh admin@${PI_HOSTNAME:-stegasoo}.local${NC} (password: stegasoo)"
|
||||
else
|
||||
echo -e "${YELLOW}Tip:${NC} On first boot, the setup wizard will help configure WiFi."
|
||||
echo -e "${YELLOW}Tip:${NC} Or place config.json in rpi/ for headless setup next time."
|
||||
fi
|
||||
echo ""
|
||||
|
||||
@@ -168,12 +168,41 @@ fi
|
||||
DEV_SIZE=$(blockdev --getsize64 "$SELECTED")
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}[1/3]${NC} Copying image from $SELECTED..."
|
||||
echo -e "${GREEN}[1/4]${NC} Copying image from $SELECTED..."
|
||||
dd if="$SELECTED" bs=4M status=none | pv -s "$DEV_SIZE" > "$IMG_FILE"
|
||||
sync
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}[2/3]${NC} Shrinking image..."
|
||||
echo -e "${GREEN}[2/4]${NC} Re-enabling auto-expand for distribution..."
|
||||
# Mount the image and restore auto-expand service (may have been disabled during build)
|
||||
LOOP_DEV=$(losetup -f --show -P "$IMG_FILE")
|
||||
if [ -n "$LOOP_DEV" ]; then
|
||||
TEMP_MOUNT=$(mktemp -d)
|
||||
if mount "${LOOP_DEV}p2" "$TEMP_MOUNT" 2>/dev/null; then
|
||||
# Re-enable the resize service if the service file exists
|
||||
SERVICE_FILE="$TEMP_MOUNT/lib/systemd/system/rpi-resizerootfs.service"
|
||||
SERVICE_LINK="$TEMP_MOUNT/etc/systemd/system/multi-user.target.wants/rpi-resizerootfs.service"
|
||||
if [ -f "$SERVICE_FILE" ] && [ ! -L "$SERVICE_LINK" ]; then
|
||||
mkdir -p "$(dirname "$SERVICE_LINK")"
|
||||
ln -sf /lib/systemd/system/rpi-resizerootfs.service "$SERVICE_LINK"
|
||||
echo -e " ${GREEN}✓${NC} Auto-expand service re-enabled"
|
||||
elif [ -L "$SERVICE_LINK" ]; then
|
||||
echo -e " ${GREEN}✓${NC} Auto-expand already enabled"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Could not find resize service file"
|
||||
fi
|
||||
umount "$TEMP_MOUNT"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Could not mount rootfs, skipping auto-expand fix"
|
||||
fi
|
||||
rmdir "$TEMP_MOUNT" 2>/dev/null || true
|
||||
losetup -d "$LOOP_DEV"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠${NC} Could not create loop device, skipping auto-expand fix"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}[3/4]${NC} Shrinking image..."
|
||||
if command -v pishrink.sh &> /dev/null; then
|
||||
pishrink.sh "$IMG_FILE"
|
||||
elif [ -f "./pishrink.sh" ]; then
|
||||
@@ -187,11 +216,11 @@ fi
|
||||
|
||||
echo ""
|
||||
if [ "$SKIP_COMPRESS" = true ]; then
|
||||
echo -e "${GREEN}[3/3]${NC} Skipping compression (.img output)"
|
||||
echo -e "${GREEN}[4/4]${NC} Skipping compression (.img output)"
|
||||
FINAL_SIZE=$(du -h "$IMG_FILE" | awk '{print $1}')
|
||||
OUTPUT="$IMG_FILE"
|
||||
else
|
||||
echo -e "${GREEN}[3/3]${NC} Compressing with zstd..."
|
||||
echo -e "${GREEN}[4/4]${NC} Compressing with zstd..."
|
||||
pv "$IMG_FILE" | zstd -19 -T0 -q > "$OUTPUT"
|
||||
rm -f "$IMG_FILE"
|
||||
FINAL_SIZE=$(du -h "$OUTPUT" | awk '{print $1}')
|
||||
|
||||
107
rpi/setup.sh
107
rpi/setup.sh
@@ -135,6 +135,7 @@ sudo apt-get install -y \
|
||||
build-essential \
|
||||
git \
|
||||
curl \
|
||||
zstd \
|
||||
libssl-dev \
|
||||
zlib1g-dev \
|
||||
libbz2-dev \
|
||||
@@ -218,50 +219,84 @@ else
|
||||
cd "$INSTALL_DIR"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}[6/12]${NC} Creating Python virtual environment..."
|
||||
# Check for pre-built venv tarball (skips 20+ min compile time)
|
||||
PREBUILT_VENV="$INSTALL_DIR/rpi/stegasoo-venv-pi-arm64.tar.zst"
|
||||
PREBUILT_VENV_URL="${PREBUILT_VENV_URL:-}" # Optional: URL to download from
|
||||
|
||||
# Create venv with pyenv Python (not system Python)
|
||||
# Use pyenv which to get actual path (handles 3.12 -> 3.12.12 mapping)
|
||||
PYENV_PYTHON=$(pyenv which python)
|
||||
echo " Using Python: $PYENV_PYTHON"
|
||||
if [ ! -d "venv" ]; then
|
||||
"$PYENV_PYTHON" -m venv venv
|
||||
fi
|
||||
source venv/bin/activate
|
||||
if [ -f "$PREBUILT_VENV" ] || [ -n "$PREBUILT_VENV_URL" ]; then
|
||||
echo -e "${GREEN}[6/8]${NC} Installing pre-built Python environment..."
|
||||
|
||||
# Verify we're using the right Python
|
||||
VENV_PY=$(python --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1,2)
|
||||
echo " venv Python: $VENV_PY"
|
||||
# Download if URL provided and local file doesn't exist
|
||||
if [ ! -f "$PREBUILT_VENV" ] && [ -n "$PREBUILT_VENV_URL" ]; then
|
||||
echo " Downloading pre-built venv..."
|
||||
curl -L -o "$PREBUILT_VENV" "$PREBUILT_VENV_URL"
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}[7/12]${NC} Building jpegio for ARM..."
|
||||
# Extract pre-built venv (zstd compressed)
|
||||
echo " Extracting pre-built venv (this is much faster!)..."
|
||||
zstd -d "$PREBUILT_VENV" --stdout | tar -xf - -C "$INSTALL_DIR"
|
||||
|
||||
# Clone jpegio
|
||||
JPEGIO_DIR="/tmp/jpegio-build"
|
||||
rm -rf "$JPEGIO_DIR"
|
||||
git clone "$JPEGIO_REPO" "$JPEGIO_DIR"
|
||||
# Activate and verify
|
||||
source venv/bin/activate
|
||||
VENV_PY=$(python --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1,2)
|
||||
echo -e " ${GREEN}✓${NC} venv Python: $VENV_PY"
|
||||
|
||||
# Apply ARM64 patch
|
||||
if [ -f "$INSTALL_DIR/rpi/patches/jpegio/apply-patch.sh" ]; then
|
||||
bash "$INSTALL_DIR/rpi/patches/jpegio/apply-patch.sh" "$JPEGIO_DIR"
|
||||
# Install stegasoo package in editable mode (quick, no compile)
|
||||
echo -e "${GREEN}[7/8]${NC} Installing Stegasoo package..."
|
||||
pip install -e "." --quiet
|
||||
|
||||
# Adjust step numbers for rest of script
|
||||
STEP_OFFSET=-4
|
||||
else
|
||||
echo " Applying inline ARM64 patch..."
|
||||
sed -i "s/cargs.append('-m64')/pass # ARM64 fix/g" "$JPEGIO_DIR/setup.py"
|
||||
echo -e "${GREEN}[6/12]${NC} Creating Python virtual environment..."
|
||||
echo -e " ${YELLOW}Note: No pre-built venv found. Building from source (20+ min)${NC}"
|
||||
echo -e " ${YELLOW}To speed up future installs, add stegasoo-venv-pi-arm64.tar.gz to rpi/${NC}"
|
||||
|
||||
# Create venv with pyenv Python (not system Python)
|
||||
# Use pyenv which to get actual path (handles 3.12 -> 3.12.12 mapping)
|
||||
PYENV_PYTHON=$(pyenv which python)
|
||||
echo " Using Python: $PYENV_PYTHON"
|
||||
if [ ! -d "venv" ]; then
|
||||
"$PYENV_PYTHON" -m venv venv
|
||||
fi
|
||||
source venv/bin/activate
|
||||
|
||||
# Verify we're using the right Python
|
||||
VENV_PY=$(python --version 2>&1 | cut -d' ' -f2 | cut -d'.' -f1,2)
|
||||
echo " venv Python: $VENV_PY"
|
||||
|
||||
echo -e "${GREEN}[7/12]${NC} Building jpegio for ARM..."
|
||||
|
||||
# Clone jpegio
|
||||
JPEGIO_DIR="/tmp/jpegio-build"
|
||||
rm -rf "$JPEGIO_DIR"
|
||||
git clone "$JPEGIO_REPO" "$JPEGIO_DIR"
|
||||
|
||||
# Apply ARM64 patch
|
||||
if [ -f "$INSTALL_DIR/rpi/patches/jpegio/apply-patch.sh" ]; then
|
||||
bash "$INSTALL_DIR/rpi/patches/jpegio/apply-patch.sh" "$JPEGIO_DIR"
|
||||
else
|
||||
echo " Applying inline ARM64 patch..."
|
||||
sed -i "s/cargs.append('-m64')/pass # ARM64 fix/g" "$JPEGIO_DIR/setup.py"
|
||||
fi
|
||||
|
||||
cd "$JPEGIO_DIR"
|
||||
|
||||
# Build jpegio into venv
|
||||
pip install --upgrade pip setuptools wheel cython numpy
|
||||
pip install .
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
rm -rf "$JPEGIO_DIR"
|
||||
|
||||
echo -e "${GREEN}[8/12]${NC} Installing Stegasoo..."
|
||||
|
||||
# Install dependencies (jpegio already in venv, won't re-download)
|
||||
pip install -e ".[web]"
|
||||
|
||||
STEP_OFFSET=0
|
||||
fi
|
||||
|
||||
cd "$JPEGIO_DIR"
|
||||
|
||||
# Build jpegio into venv
|
||||
pip install --upgrade pip setuptools wheel cython numpy
|
||||
pip install .
|
||||
|
||||
cd "$INSTALL_DIR"
|
||||
rm -rf "$JPEGIO_DIR"
|
||||
|
||||
echo -e "${GREEN}[8/12]${NC} Installing Stegasoo..."
|
||||
|
||||
# Install dependencies (jpegio already in venv, won't re-download)
|
||||
pip install -e ".[web]"
|
||||
|
||||
echo -e "${GREEN}[9/12]${NC} Creating systemd service..."
|
||||
|
||||
# Create systemd service file
|
||||
|
||||
Reference in New Issue
Block a user