Files
stegasoo/rpi/pull-image.sh
Aaron D. Lee 1f40eeff9e Add option to skip compression in pull-image.sh
Use .img extension to skip zstd compression:
  ./pull-image.sh stegasoo.img

Use .img.zst to compress (default behavior):
  ./pull-image.sh stegasoo.img.zst

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 17:27:26 -05:00

208 lines
7.1 KiB
Bash
Executable File

#!/bin/bash
#
# Pull Stegasoo image from SD card
# Auto-detects SD card, copies with progress, shrinks, and compresses
#
# Usage: ./pull-image.sh [output-name] [device]
# Output will be: stegasoo-rpi-YYYYMMDD.img.zst (or custom name)
# Use .img extension to skip compression: ./pull-image.sh foo.img
#
# If device is specified, skips auto-detection (useful for large drives)
#
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
# Check for required tools
for cmd in dd pv zstd lsblk; do
if ! command -v $cmd &> /dev/null; then
echo -e "${RED}Error: $cmd is required but not installed.${NC}"
exit 1
fi
done
# Check for root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Error: Must run as root (sudo)${NC}"
exit 1
fi
# Output filename and optional device
if [ -n "$1" ]; then
OUTPUT="$1"
else
OUTPUT="stegasoo-rpi-$(date +%Y%m%d).img.zst"
fi
MANUAL_DEVICE="$2"
# Check if output ends in .img (skip compression) or .zst (compress)
SKIP_COMPRESS=false
if [[ "$OUTPUT" == *.img ]]; then
IMG_FILE="$OUTPUT"
SKIP_COMPRESS=true
elif [[ "$OUTPUT" == *.zst ]]; then
IMG_FILE="${OUTPUT%.zst}"
else
# No recognized extension, add .img.zst
IMG_FILE="${OUTPUT}.img"
OUTPUT="${OUTPUT}.img.zst"
fi
echo -e "${BLUE}"
echo "╔═══════════════════════════════════════════════════════════════╗"
echo "║ Stegasoo SD Card Image Puller ║"
echo "╚═══════════════════════════════════════════════════════════════╝"
echo -e "${NC}"
# Use manual device or auto-detect
if [ -n "$MANUAL_DEVICE" ]; then
# Manual device specified
if [ ! -b "$MANUAL_DEVICE" ]; then
echo -e "${RED}Error: $MANUAL_DEVICE is not a block device${NC}"
exit 1
fi
SELECTED="$MANUAL_DEVICE"
echo -e "Using specified device: ${YELLOW}$SELECTED${NC}"
echo ""
lsblk "$SELECTED" -o NAME,SIZE,TYPE,MODEL
echo ""
else
# Auto-detect SD card candidates
# Looking for: USB/removable, 8-128GB, not mounted as root filesystem
echo -e "${BOLD}Scanning for SD cards...${NC}"
echo ""
declare -a CANDIDATES
declare -a CANDIDATE_INFO
while IFS= read -r line; do
DEV=$(echo "$line" | awk '{print $1}')
SIZE=$(echo "$line" | awk '{print $2}')
TYPE=$(echo "$line" | awk '{print $3}')
TRAN=$(echo "$line" | awk '{print $4}')
MODEL=$(echo "$line" | awk '{print $5" "$6" "$7}' | xargs)
# Skip if it's the root filesystem
if mount | grep -q "^/dev/${DEV}[0-9]* on / "; then
continue
fi
# Skip if any partition is mounted as root
ROOT_DEV=$(mount | grep " on / " | awk '{print $1}' | sed 's/[0-9]*$//')
if [[ "/dev/$DEV" == "$ROOT_DEV" ]]; then
continue
fi
# Get size in bytes for reliable comparison
SIZE_BYTES=$(lsblk -b -d -o SIZE -n "/dev/$DEV" 2>/dev/null | tr -d ' ')
SIZE_GB_INT=$((SIZE_BYTES / 1073741824)) # 1024^3
# Check if size is in SD card range (8GB - 128GB)
if [ "$SIZE_GB_INT" -ge 8 ] && [ "$SIZE_GB_INT" -le 128 ]; then
CANDIDATES+=("/dev/$DEV")
CANDIDATE_INFO+=("$SIZE $TYPE ${TRAN:-???} $MODEL")
fi
done < <(lsblk -d -o NAME,SIZE,TYPE,TRAN,MODEL -n | grep "disk")
if [ ${#CANDIDATES[@]} -eq 0 ]; then
echo -e "${RED}No SD card candidates found.${NC}"
echo "Looking for USB/removable disks between 8GB and 128GB."
echo ""
echo "Available disks:"
lsblk -d -o NAME,SIZE,TYPE,TRAN,MODEL
echo ""
echo -e "${YELLOW}Tip: Specify device manually: $0 output.img.zst /dev/sdX${NC}"
exit 1
fi
echo -e "${GREEN}Found ${#CANDIDATES[@]} candidate(s):${NC}"
echo ""
for i in "${!CANDIDATES[@]}"; do
echo -e " ${BOLD}[$((i+1))]${NC} ${CANDIDATES[$i]} - ${CANDIDATE_INFO[$i]}"
done
echo ""
if [ ${#CANDIDATES[@]} -eq 1 ]; then
SELECTED="${CANDIDATES[0]}"
echo -e "Auto-selected: ${YELLOW}$SELECTED${NC}"
else
read -p "Select device [1-${#CANDIDATES[@]}]: " -r
if [[ ! $REPLY =~ ^[0-9]+$ ]] || [ "$REPLY" -lt 1 ] || [ "$REPLY" -gt ${#CANDIDATES[@]} ]; then
echo -e "${RED}Invalid selection.${NC}"
exit 1
fi
SELECTED="${CANDIDATES[$((REPLY-1))]}"
fi
fi
# Show partitions
echo ""
echo -e "${BOLD}Partitions on $SELECTED:${NC}"
lsblk "$SELECTED" -o NAME,SIZE,FSTYPE,LABEL,MOUNTPOINT
echo ""
# Final confirmation
echo -e "${RED}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${RED}║ WARNING: This will read the ENTIRE device: ║${NC}"
echo -e "${RED}$SELECTED${NC}"
echo -e "${RED}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "Output: ${YELLOW}$OUTPUT${NC}"
echo ""
read -p "Continue? [y/N] " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Aborted."
exit 1
fi
# Get device size for pv
DEV_SIZE=$(blockdev --getsize64 "$SELECTED")
echo ""
echo -e "${GREEN}[1/3]${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..."
if command -v pishrink.sh &> /dev/null; then
pishrink.sh "$IMG_FILE"
elif [ -f "./pishrink.sh" ]; then
bash ./pishrink.sh "$IMG_FILE"
elif [ -f "../pishrink.sh" ]; then
bash ../pishrink.sh "$IMG_FILE"
else
echo -e "${YELLOW}pishrink.sh not found, skipping shrink step.${NC}"
echo "Download from: https://github.com/Drewsif/PiShrink"
fi
echo ""
if [ "$SKIP_COMPRESS" = true ]; then
echo -e "${GREEN}[3/3]${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..."
pv "$IMG_FILE" | zstd -19 -T0 -q > "$OUTPUT"
rm -f "$IMG_FILE"
FINAL_SIZE=$(du -h "$OUTPUT" | awk '{print $1}')
fi
echo ""
echo -e "${GREEN}╔═══════════════════════════════════════════════════════════════╗${NC}"
echo -e "${GREEN}║ Image Complete! ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════╝${NC}"
echo ""
echo -e "Output: ${YELLOW}$OUTPUT${NC}"
echo -e "Size: ${YELLOW}$FINAL_SIZE${NC}"
echo ""