titan-iac/scripts/styx_prep.sh

196 lines
6.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# === CONFIG ===
STYX_USER="styx"
STYX_PASS="TempPass#123" # change at first login
STYX_HOSTNAME="styx"
SSH_PUBKEY="" # e.g., 'ssh-ed25519 AAAA... your@host' (optional)
# === helpers ===
require_root() {
if [[ $EUID -ne 0 ]]; then exec sudo -E "$0" "$@"; fi
}
ensure_binfmt_arm64() {
# If binfmt for arm64 isn't registered, register it via Docker (idempotent).
if [[ ! -e /proc/sys/fs/binfmt_misc/qemu-aarch64 ]]; then
command -v docker >/dev/null || { echo "Docker required to register binfmt (sudo pacman -S docker)"; exit 1; }
sudo systemctl enable --now docker >/dev/null 2>&1 || true
sudo docker run --rm --privileged tonistiigi/binfmt --install arm64
fi
}
find_parts() {
BOOT=$(lsblk -o LABEL,PATH -nr | awk '$1=="system-boot"{print $2}' | head -n1)
ROOT=$(lsblk -o LABEL,PATH -nr | awk '$1=="writable"{print $2}' | head -n1)
if [[ -z "${BOOT:-}" || -z "${ROOT:-}" ]]; then
echo "Could not find 'system-boot'/'writable' on any device."
lsblk -o NAME,SIZE,FSTYPE,LABEL,PATH -nr
exit 1
fi
}
mount_parts() {
mkdir -p /mnt/pi-boot /mnt/pi-root
mount "$ROOT" /mnt/pi-root
mount "$BOOT" /mnt/pi-boot
# Bind only what we need (avoid /run to prevent postinst fights)
for d in dev dev/pts proc sys; do mount --bind "/$d" "/mnt/pi-root/$d"; done
# Ubuntu images use a resolv.conf symlink—replace with a real file
if [[ -L /mnt/pi-root/etc/resolv.conf || ! -e /mnt/pi-root/etc/resolv.conf ]]; then
rm -f /mnt/pi-root/etc/resolv.conf
cat /etc/resolv.conf > /mnt/pi-root/etc/resolv.conf
fi
}
prep_chroot() {
# Block service starts inside chroot (no systemd there)
cat >/mnt/pi-root/usr/sbin/policy-rc.d <<'EOF'
#!/bin/sh
exit 101
EOF
chmod +x /mnt/pi-root/usr/sbin/policy-rc.d
# All the work happens inside the ARM64 rootfs
CHCMD=$(cat <<'EOS'
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC
# Ensure sbin is in PATH so user/group tools work
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
apt-get update
apt-get -y full-upgrade
# Remove snaps and keep them gone (Ubuntu for Pi ships with snaps)
apt-get -y purge snapd || true
rm -rf /snap /var/snap /var/lib/snapd /home/*/snap || true
mkdir -p /etc/apt/preferences.d
printf 'Package: snapd\nPin: release *\nPin-Priority: -10\n' > /etc/apt/preferences.d/nosnap.pref
# Ensure user/group tools exist
apt-get install -y passwd adduser || true
getent group i2c >/dev/null || /usr/sbin/groupadd i2c
# Base packages
BASE_PKGS="openssh-server git i2c-tools python3-smbus python3-pil zbar-tools qrencode lm-sensors"
apt-get install -y $BASE_PKGS
# ------- OLED (Luma) -------
# Prefer distro package; fall back to pip if not present in this release
if ! dpkg -s python3-luma.oled >/dev/null 2>&1; then
apt-get update
if ! apt-get install -y python3-luma.oled; then
apt-get install -y python3-pip
pip3 install --no-input --break-system-packages luma.oled
fi
fi
# ------- Camera apps -------
# Ubuntu renamed libcamera-apps -> rpicam-apps for Raspberry Pi.
# Try in order; tolerate absence (the box might be display-only).
apt-get update
if ! apt-get install -y rpicam-apps; then
apt-get install -y libcamera-apps || apt-get install -y libcamera-tools || true
fi
# Enable SSH on boot (no systemctl in chroot)
mkdir -p /etc/systemd/system/multi-user.target.wants
ln -sf /lib/systemd/system/ssh.service /etc/systemd/system/multi-user.target.wants/ssh.service
# Create user and set password
if ! id -u STYX_USER >/dev/null 2>&1; then
/usr/sbin/useradd -m -s /bin/bash -G sudo,video,i2c STYX_USER
fi
echo 'STYX_USER:STYX_PASS' | /usr/sbin/chpasswd
# Optional: preload SSH key
if [ -n 'SSH_PUBKEY' ] && echo 'SSH_PUBKEY' | grep -q 'ssh-'; then
install -d -m700 /home/STYX_USER/.ssh
echo 'SSH_PUBKEY' >> /home/STYX_USER/.ssh/authorized_keys
chmod 600 /home/STYX_USER/.ssh/authorized_keys
chown -R STYX_USER:STYX_USER /home/STYX_USER/.ssh
fi
# Freenove code
git clone https://github.com/Freenove/Freenove_Computer_Case_Kit_for_Raspberry_Pi.git /opt/freenove || true
# Hostname
echo 'STYX_HOSTNAME' > /etc/hostname
if grep -q '^127\.0\.1\.1' /etc/hosts; then
sed -i 's/^127\.0\.1\.1.*/127.0.1.1\tSTYX_HOSTNAME/' /etc/hosts
else
echo -e '127.0.1.1\tSTYX_HOSTNAME' >> /etc/hosts
fi
apt-get clean
EOS
)
# Inject config values safely
CHCMD="${CHCMD//STYX_USER/${STYX_USER}}"
CHCMD="${CHCMD//STYX_PASS/${STYX_PASS}}"
CHCMD="${CHCMD//STYX_HOSTNAME/${STYX_HOSTNAME}}"
CHCMD="${CHCMD//SSH_PUBKEY/${SSH_PUBKEY}}"
chroot /mnt/pi-root /bin/bash -lc "$CHCMD"
}
install_service_host() {
# Systemd unit for the Freenove example app
mkdir -p /mnt/pi-root/etc/systemd/system/multi-user.target.wants
cat >/mnt/pi-root/etc/systemd/system/freenove-case.service <<'SERVICE'
[Unit]
Description=Freenove Case OLED/Fans/LEDs
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 /opt/freenove/Code/application.py
Restart=on-failure
[Install]
WantedBy=multi-user.target
SERVICE
ln -sf /etc/systemd/system/freenove-case.service \
/mnt/pi-root/etc/systemd/system/multi-user.target.wants/freenove-case.service || true
}
boot_tweaks() {
# Enable I2C and set DSI panel on the BOOT partition
grep -q 'dtparam=i2c_arm=on' /mnt/pi-boot/config.txt || echo 'dtparam=i2c_arm=on' >> /mnt/pi-boot/config.txt
# Append kernel cmdline only once
if ! grep -q 'DSI-1:800x480@60D' /mnt/pi-boot/cmdline.txt 2>/dev/null; then
sed -i '1 s#$# video=DSI-1:800x480@60D video=HDMI-A-1:off video=HDMI-A-2:off#' /mnt/pi-boot/cmdline.txt || true
fi
}
cleanup() {
rm -f /mnt/pi-root/usr/sbin/policy-rc.d || true
for d in dev/pts dev proc sys; do umount -lf "/mnt/pi-root/$d" 2>/dev/null || true; done
umount -lf /mnt/pi-boot 2>/dev/null || true
umount -lf /mnt/pi-root 2>/dev/null || true
sync || true
}
main() {
require_root
ensure_binfmt_arm64
find_parts
trap 'echo "ERROR at line $LINENO" >&2; cleanup' ERR INT
mount_parts
prep_chroot
install_service_host
boot_tweaks
cleanup
echo "✅ Done. Move the NVMe to the Pi and boot."
echo " Login: user '${STYX_USER}' pass '${STYX_PASS}' (change with 'passwd')."
echo " Quick checks on the Pi:"
echo " sudo i2cdetect -y 1"
echo " rpicam-still -n -o test.jpg # (if rpicam-apps installed)"
echo " libcamera-still -n -o test.jpg # (if legacy libcamera-apps installed)"
echo " systemctl status freenove-case"
}
main "$@"