Some checks failed
CI / skinny-install (aco) (push) Successful in 1m18s
CI / skinny-install (api) (push) Successful in 40s
CI / skinny-install (bcda) (push) Successful in 35s
CI / skinny-install (bib) (push) Successful in 38s
CI / skinny-install (cli) (push) Successful in 46s
CI / skinny-install (conf) (push) Successful in 36s
CI / skinny-install (opps) (push) Successful in 38s
CI / skinny-install (pfs) (push) Successful in 47s
CI / skinny-install (rex) (push) Successful in 35s
Infra CI / notebooks (push) Successful in 3m17s
CI / lint-test (push) Failing after 3m30s
CI / skinny-install (bls) (push) Successful in 34s
CI / skinny-install (ccw) (push) Successful in 45s
CI / skinny-install (cms) (push) Successful in 32s
CI / skinny-install (perf) (push) Successful in 43s
Deploy / build-scan-report (push) Has been cancelled
Infra CI / docs (push) Failing after 20s
Infra CI / api (push) Successful in 16s
Infra CI / mc (push) Successful in 12s
Package Supply Chain / pkg-supply-chain (push) Successful in 1m27s
Infra CI / zotero (push) Successful in 6m10s
189 lines
6.7 KiB
Bash
Executable File
189 lines
6.7 KiB
Bash
Executable File
#!/bin/sh
|
|
#
|
|
# install-to-usb.sh — Install Alpine to Samsung Fit 128GB USB drive
|
|
# Run this AFTER booting from the custom ISO and running setup-alpine
|
|
#
|
|
# This script partitions the USB drive and installs Alpine in sys mode
|
|
# with a layout optimized for read-only operation.
|
|
#
|
|
set -euo pipefail
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${GREEN}[+]${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}[!]${NC} $*"; }
|
|
err() { echo -e "${RED}[!]${NC} $*"; exit 1; }
|
|
|
|
[ "$(id -u)" -eq 0 ] || err "Must run as root"
|
|
|
|
# Cleanup mounts on any failure
|
|
cleanup() {
|
|
echo -e "${YELLOW}[!]${NC} Cleaning up mounts..."
|
|
umount "${MOUNTPOINT}/boot/efi" 2>/dev/null || true
|
|
umount "${MOUNTPOINT}" 2>/dev/null || true
|
|
}
|
|
MOUNTPOINT="/mnt/usb-root"
|
|
trap cleanup EXIT
|
|
|
|
# Detect Samsung Fit USB drive
|
|
echo "Available block devices:"
|
|
echo ""
|
|
lsblk -d -o NAME,SIZE,MODEL,TRAN | grep -v "^loop"
|
|
echo ""
|
|
|
|
echo -n "Enter the USB drive device (e.g., sda): "
|
|
read -r USB_DEV
|
|
USB_DRIVE="/dev/${USB_DEV}"
|
|
|
|
[ -b "$USB_DRIVE" ] || err "${USB_DRIVE} is not a valid block device"
|
|
|
|
# Verify it's USB
|
|
TRAN=$(lsblk -d -n -o TRAN "$USB_DRIVE" 2>/dev/null || true)
|
|
if [ "$TRAN" != "usb" ]; then
|
|
warn "Device ${USB_DRIVE} transport is '${TRAN}', not 'usb'"
|
|
echo -n "Are you SURE this is the USB drive? [y/N] "
|
|
read -r confirm
|
|
[ "$confirm" = "y" ] || exit 1
|
|
fi
|
|
|
|
SIZE=$(lsblk -d -n -o SIZE "$USB_DRIVE")
|
|
log "Selected: ${USB_DRIVE} (${SIZE})"
|
|
echo ""
|
|
echo "THIS WILL ERASE ${USB_DRIVE} COMPLETELY."
|
|
echo -n "Continue? [y/N] "
|
|
read -r confirm
|
|
[ "$confirm" = "y" ] || exit 1
|
|
|
|
# -------------------------------------------------------------------
|
|
# Partition the USB drive
|
|
# -------------------------------------------------------------------
|
|
log "Partitioning ${USB_DRIVE}..."
|
|
|
|
# GPT partition table
|
|
sgdisk --zap-all "$USB_DRIVE"
|
|
sgdisk -n 1:0:+512M -t 1:EF00 -c 1:"EFI" "$USB_DRIVE"
|
|
sgdisk -n 2:0:+20G -t 2:8300 -c 2:"root" "$USB_DRIVE"
|
|
# Remaining space unused — 20GB is plenty for a read-only root
|
|
partprobe "$USB_DRIVE" 2>/dev/null || blockdev --rereadpt "$USB_DRIVE" || err "Failed to re-read partition table"
|
|
sleep 2
|
|
|
|
# Verify partition nodes appeared
|
|
wait_count=0
|
|
while [ $wait_count -lt 5 ]; do
|
|
if [ -b "${USB_DRIVE}1" ] || [ -b "${USB_DRIVE}p1" ]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
wait_count=$((wait_count + 1))
|
|
done
|
|
|
|
# Detect partition naming (sdX1 vs sdXp1)
|
|
if [ -b "${USB_DRIVE}1" ]; then
|
|
PART_EFI="${USB_DRIVE}1"
|
|
PART_ROOT="${USB_DRIVE}2"
|
|
elif [ -b "${USB_DRIVE}p1" ]; then
|
|
PART_EFI="${USB_DRIVE}p1"
|
|
PART_ROOT="${USB_DRIVE}p2"
|
|
else
|
|
err "Cannot find partitions on ${USB_DRIVE}"
|
|
fi
|
|
|
|
log "Formatting..."
|
|
mkfs.vfat -F 32 -n EFI "$PART_EFI"
|
|
mkfs.ext4 -L root -O ^has_journal "$PART_ROOT"
|
|
# No journal on USB flash — reduces write amplification significantly
|
|
|
|
# -------------------------------------------------------------------
|
|
# Install Alpine sys mode
|
|
# -------------------------------------------------------------------
|
|
log "Installing Alpine to USB..."
|
|
|
|
mkdir -p "${MOUNTPOINT}"
|
|
mount "$PART_ROOT" "${MOUNTPOINT}"
|
|
mkdir -p "${MOUNTPOINT}/boot/efi"
|
|
mount "$PART_EFI" "${MOUNTPOINT}/boot/efi"
|
|
|
|
# Use setup-disk to do the heavy lifting
|
|
BOOTLOADER=grub setup-disk -o "${MOUNTPOINT}" -k lts || err "setup-disk failed — check that you are running from the Alpine installer environment"
|
|
|
|
# Verify GRUB installed correctly; ensure fallback EFI path exists
|
|
if [ ! -f "${MOUNTPOINT}/boot/efi/EFI/BOOT/BOOTX64.EFI" ]; then
|
|
log "Creating fallback EFI boot path..."
|
|
mkdir -p "${MOUNTPOINT}/boot/efi/EFI/BOOT"
|
|
if [ -f "${MOUNTPOINT}/boot/efi/EFI/alpine/grubx64.efi" ]; then
|
|
cp "${MOUNTPOINT}/boot/efi/EFI/alpine/grubx64.efi" "${MOUNTPOINT}/boot/efi/EFI/BOOT/BOOTX64.EFI"
|
|
else
|
|
# Find any grub efi binary that was installed
|
|
grub_efi=$(find "${MOUNTPOINT}/boot/efi/EFI" -name "grubx64.efi" -print -quit 2>/dev/null)
|
|
if [ -n "$grub_efi" ]; then
|
|
cp "$grub_efi" "${MOUNTPOINT}/boot/efi/EFI/BOOT/BOOTX64.EFI"
|
|
else
|
|
warn "No grubx64.efi found — USB may not boot on all UEFI systems"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# -------------------------------------------------------------------
|
|
# Post-install tweaks on the new root
|
|
# -------------------------------------------------------------------
|
|
log "Applying read-only root configuration..."
|
|
|
|
# fstab will be configured by storage-setup.sh after first boot
|
|
# For now, set root to ro
|
|
if grep -q 'errors=remount-ro' "${MOUNTPOINT}/etc/fstab"; then
|
|
sed -i 's|errors=remount-ro|ro,noatime,discard,errors=remount-ro|' "${MOUNTPOINT}/etc/fstab"
|
|
else
|
|
# Fallback: find the root entry and add ro directly
|
|
if grep -q "$PART_ROOT" "${MOUNTPOINT}/etc/fstab"; then
|
|
sed -i "s|\(${PART_ROOT}.*\)defaults|\1ro,noatime,discard|" "${MOUNTPOINT}/etc/fstab"
|
|
else
|
|
err "Could not find root partition in fstab — root will not be read-only. Fix /etc/fstab manually."
|
|
fi
|
|
fi
|
|
# Verify ro is actually in the root mount options
|
|
grep -q '\sro[,\s]' "${MOUNTPOINT}/etc/fstab" || err "Failed to set root filesystem to read-only in fstab"
|
|
|
|
# Copy the storage setup script
|
|
if [ -f /root/storage-setup.sh ]; then
|
|
cp /root/storage-setup.sh "${MOUNTPOINT}/root/storage-setup.sh"
|
|
chmod +x "${MOUNTPOINT}/root/storage-setup.sh"
|
|
else
|
|
err "storage-setup.sh not found at /root/storage-setup.sh — persistent storage will not be configured. Place the file and re-run."
|
|
fi
|
|
|
|
# Enable tmpfs for volatile dirs in the installed system
|
|
cat >> "${MOUNTPOINT}/etc/fstab" << 'TMPFS'
|
|
|
|
# tmpfs mounts — keep USB drive read-only
|
|
tmpfs /tmp tmpfs nosuid,nodev,size=8G 0 0
|
|
tmpfs /run tmpfs nosuid,nodev,mode=0755,size=2G 0 0
|
|
tmpfs /var/log tmpfs nosuid,nodev,noexec,size=2G 0 0
|
|
tmpfs /var/tmp tmpfs nosuid,nodev,size=2G 0 0
|
|
TMPFS
|
|
|
|
# GRUB: add console for headless serial access
|
|
if [ -f "${MOUNTPOINT}/etc/default/grub" ]; then
|
|
sed -i 's|GRUB_CMDLINE_LINUX_DEFAULT=".*"|GRUB_CMDLINE_LINUX_DEFAULT="modules=sd-mod,usb-storage,ext4 quiet rootfstype=ext4 console=tty0 console=ttyS0,115200n8"|' \
|
|
"${MOUNTPOINT}/etc/default/grub"
|
|
# Rebuild grub config
|
|
chroot "${MOUNTPOINT}" grub-mkconfig -o /boot/grub/grub.cfg 2>/dev/null || \
|
|
warn "grub-mkconfig failed — may need to run manually after boot"
|
|
fi
|
|
|
|
# -------------------------------------------------------------------
|
|
# Cleanup (trap handles umount on failure; do it explicitly on success)
|
|
# -------------------------------------------------------------------
|
|
log "Unmounting..."
|
|
umount "${MOUNTPOINT}/boot/efi"
|
|
umount "${MOUNTPOINT}"
|
|
trap - EXIT
|
|
|
|
log ""
|
|
log "=== USB INSTALL COMPLETE ==="
|
|
log ""
|
|
log "Remove the ISO boot media, set BIOS to boot from USB, and power on."
|
|
log "After first boot, run: /root/storage-setup.sh"
|