507 lines
15 KiB
Bash
Executable File
507 lines
15 KiB
Bash
Executable File
#!/bin/bash
|
|
# SamiOS Automated Installer
|
|
# Partitions disk, formats, pacstraps base system, installs bootloader, creates user
|
|
# Usage: samios install [options]
|
|
set -e
|
|
|
|
# ── Canonical version source ────────────────────────────────────────────
|
|
# Walk up to find ./VERSION, fall back to /etc/samios-version, fall back
|
|
# to a placeholder. This guarantees the installer and the CLI it emits
|
|
# agree on the version string without any duplicate hardcoding.
|
|
_VERSION_FILE=""
|
|
_d="$(cd "$(dirname "$0")" && pwd)"
|
|
while [ "$_d" != "/" ]; do
|
|
if [ -f "$_d/VERSION" ]; then
|
|
_VERSION_FILE="$_d/VERSION"
|
|
break
|
|
fi
|
|
_d="$(dirname "$_d")"
|
|
done
|
|
if [ -z "$_VERSION_FILE" ] && [ -r /etc/samios-version ]; then
|
|
VERSION="$(head -n1 /etc/samios-version)"
|
|
elif [ -n "$_VERSION_FILE" ]; then
|
|
VERSION="$(head -n1 "$_VERSION_FILE")"
|
|
# Remember the file so install_samios_cli can mirror it onto the new
|
|
# system at /etc/samios-version.
|
|
_EMIT_VERSION_FILE="$_VERSION_FILE"
|
|
else
|
|
VERSION="0.0.0-unknown"
|
|
fi
|
|
unset _d _VERSION_FILE
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
log() { echo -e "${BLUE}[SamiOS]${NC} $*"; }
|
|
ok() { echo -e "${GREEN}✓${NC} $*"; }
|
|
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
|
|
err() { echo -e "${RED}✗${NC} $*" >&2; }
|
|
|
|
# ============================================================
|
|
# Pre-flight checks
|
|
# ============================================================
|
|
check_root() {
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
err "This script must be run as root"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
check_uefi() {
|
|
if [ -d /sys/firmware/efi/efivars ]; then
|
|
UEFI=true
|
|
ok "UEFI mode detected"
|
|
else
|
|
UEFI=false
|
|
ok "BIOS mode detected"
|
|
fi
|
|
}
|
|
|
|
detect_disks() {
|
|
log "Detecting available disks..."
|
|
mapfile -t DISKS < <(lsblk -d -n -p -o NAME | grep -v "loop\|sr\|fd")
|
|
|
|
echo ""
|
|
echo "Available disks:"
|
|
for i in "${!DISKS[@]}"; do
|
|
SIZE=$(lsblk -d -n -o SIZE "${DISKS[$i]}" 2>/dev/null)
|
|
MODEL=$(lsblk -d -n -o MODEL "${DISKS[$i]}" 2>/dev/null)
|
|
echo " [$i] ${DISKS[$i]} - ${SIZE} ${MODEL}"
|
|
done
|
|
echo ""
|
|
}
|
|
|
|
select_disk() {
|
|
if [ -n "$TARGET_DISK" ]; then
|
|
return
|
|
fi
|
|
read -p "Select target disk [0-$(( ${#DISKS[@]} - 1 ))]: " DISK_IDX
|
|
TARGET_DISK="${DISKS[$DISK_IDX]}"
|
|
|
|
echo ""
|
|
warn "WARNING: This will ERASE ALL DATA on ${TARGET_DISK}"
|
|
read -p "Type 'YES' to confirm: " CONFIRM
|
|
if [ "$CONFIRM" != "YES" ]; then
|
|
err "Installation cancelled"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# ============================================================
|
|
# Partitioning
|
|
# ============================================================
|
|
partition_disk() {
|
|
log "Partitioning ${TARGET_DISK}..."
|
|
|
|
# Wipe all partition tables
|
|
sgdisk --zap-all "$TARGET_DISK"
|
|
|
|
if [ "$UEFI" = true ]; then
|
|
# UEFI: GPT with EFI System Partition + root
|
|
sgdisk -n 1:0:+1G -t 1:ef00 -c 1:"EFI System" "$TARGET_DISK"
|
|
sgdisk -n 2:0:0 -t 2:8300 -c 2:"SamiOS Root" "$TARGET_DISK"
|
|
partprobe "$TARGET_DISK"
|
|
sleep 2
|
|
|
|
# Determine partition names
|
|
if [[ "$TARGET_DISK" == *"nvme"* ]] || [[ "$TARGET_DISK" == *"mmcblk"* ]]; then
|
|
EFI_PART="${TARGET_DISK}p1"
|
|
ROOT_PART="${TARGET_DISK}p2"
|
|
else
|
|
EFI_PART="${TARGET_DISK}1"
|
|
ROOT_PART="${TARGET_DISK}2"
|
|
fi
|
|
|
|
# Format
|
|
mkfs.fat -F32 "$EFI_PART"
|
|
mkfs.ext4 -F -L samios "$ROOT_PART"
|
|
|
|
ok "UEFI partitions created"
|
|
else
|
|
# BIOS: MBR with single root + boot
|
|
sgdisk -n 1:0:0 -t 1:8300 -c 1:"SamiOS" "$TARGET_DISK"
|
|
partprobe "$TARGET_DISK"
|
|
sleep 2
|
|
|
|
if [[ "$TARGET_DISK" == *"nvme"* ]] || [[ "$TARGET_DISK" == *"mmcblk"* ]]; then
|
|
ROOT_PART="${TARGET_DISK}p1"
|
|
else
|
|
ROOT_PART="${TARGET_DISK}1"
|
|
fi
|
|
|
|
mkfs.ext4 -F -L samios "$ROOT_PART"
|
|
ok "BIOS partition created"
|
|
fi
|
|
}
|
|
|
|
# ============================================================
|
|
# Mount and pacstrap
|
|
# ============================================================
|
|
mount_and_install() {
|
|
log "Mounting filesystems..."
|
|
|
|
MOUNT_POINT="/mnt"
|
|
mount "$ROOT_PART" "$MOUNT_POINT"
|
|
|
|
if [ "$UEFI" = true ]; then
|
|
mkdir -p "$MOUNT_POINT/boot/efi"
|
|
mount "$EFI_PART" "$MOUNT_POINT/boot/efi"
|
|
fi
|
|
|
|
log "Installing base system (this takes a few minutes)..."
|
|
|
|
# Install base system
|
|
pacstrap "$MOUNT_POINT" base base-devel linux linux-firmware
|
|
|
|
# Generate fstab
|
|
genfstab -U "$MOUNT_POINT" > "$MOUNT_POINT/etc/fstab"
|
|
|
|
ok "Base system installed"
|
|
}
|
|
|
|
# ============================================================
|
|
# System configuration
|
|
# ============================================================
|
|
configure_system() {
|
|
log "Configuring system..."
|
|
|
|
MOUNT_POINT="/mnt"
|
|
|
|
# Hostname
|
|
echo "samios" > "$MOUNT_POINT/etc/hostname"
|
|
|
|
# Hosts
|
|
cat > "$MOUNT_POINT/etc/hosts" << 'HOSTS'
|
|
127.0.0.1 localhost
|
|
::1 localhost
|
|
127.0.1.1 samios.localdomain samios
|
|
HOSTS
|
|
|
|
# Timezone
|
|
arch-chroot "$MOUNT_POINT" ln -sf /usr/share/zoneinfo/America/New_York /etc/localtime
|
|
arch-chroot "$MOUNT_POINT" hwclock --systohc
|
|
|
|
# Locale
|
|
sed -i 's/^#en_US.UTF-8/en_US.UTF-8/' "$MOUNT_POINT/etc/locale.gen"
|
|
arch-chroot "$MOUNT_POINT" locale-gen
|
|
echo "LANG=en_US.UTF-8" > "$MOUNT_POINT/etc/locale.conf"
|
|
|
|
# Keymap
|
|
echo "KEYMAP=us" > "$MOUNT_POINT/etc/vconsole.conf"
|
|
|
|
# Install essential packages
|
|
arch-chroot "$MOUNT_POINT" pacman -S --noconfirm \
|
|
networkmanager \
|
|
sudo \
|
|
grub \
|
|
efibootmgr \
|
|
os-prober \
|
|
nano \
|
|
vim \
|
|
less \
|
|
man-db \
|
|
man-pages \
|
|
git \
|
|
curl \
|
|
wget \
|
|
bash-completion \
|
|
reflector \
|
|
openssh
|
|
|
|
# Enable NetworkManager
|
|
arch-chroot "$MOUNT_POINT" systemctl enable NetworkManager
|
|
|
|
# Enable SSH (optional)
|
|
arch-chroot "$MOUNT_POINT" systemctl enable sshd
|
|
|
|
ok "System configured"
|
|
}
|
|
|
|
# ============================================================
|
|
# Bootloader
|
|
# ============================================================
|
|
install_bootloader() {
|
|
log "Installing bootloader..."
|
|
|
|
MOUNT_POINT="/mnt"
|
|
|
|
if [ "$UEFI" = true ]; then
|
|
arch-chroot "$MOUNT_POINT" grub-install --target=x86_64-efi \
|
|
--efi-directory=/boot/efi --bootloader-id=SamiOS
|
|
else
|
|
arch-chroot "$MOUNT_POINT" grub-install --target=i386-pc "$TARGET_DISK"
|
|
fi
|
|
|
|
# Configure GRUB
|
|
arch-chroot "$MOUNT_POINT" grub-mkconfig -o /boot/grub/grub.cfg
|
|
|
|
ok "Bootloader installed"
|
|
}
|
|
|
|
# ============================================================
|
|
# User creation
|
|
# ============================================================
|
|
create_user() {
|
|
log "Creating user account..."
|
|
|
|
MOUNT_POINT="/mnt"
|
|
|
|
INTERACTIVE_USER="${INSTALL_USER:-sami}"
|
|
|
|
if [ -z "$INSTALL_PASSWORD" ]; then
|
|
read -s -p "Enter password for ${INTERACTIVE_USER}: " USER_PASS
|
|
echo ""
|
|
read -s -p "Confirm password: " USER_PASS_CONFIRM
|
|
echo ""
|
|
if [ "$USER_PASS" != "$USER_PASS_CONFIRM" ]; then
|
|
err "Passwords do not match"
|
|
exit 1
|
|
fi
|
|
else
|
|
USER_PASS="$INSTALL_PASSWORD"
|
|
fi
|
|
|
|
# Create user
|
|
arch-chroot "$MOUNT_POINT" useradd -m -G wheel,storage,power,network -s /bin/bash "$INTERACTIVE_USER"
|
|
|
|
# Set password
|
|
echo "$INTERACTIVE_USER:$USER_PASS" | arch-chroot "$MOUNT_POINT" chpasswd
|
|
|
|
# Enable sudo for wheel group
|
|
sed -i 's/^# %wheel ALL=(ALL:ALL) ALL/%wheel ALL=(ALL:ALL) ALL/' "$MOUNT_POINT/etc/sudoers"
|
|
|
|
# Set root password
|
|
echo "root:$USER_PASS" | arch-chroot "$MOUNT_POINT" chpasswd
|
|
|
|
ok "User ${INTERACTIVE_USER} created"
|
|
}
|
|
|
|
# ============================================================
|
|
# SamiOS CLI tool
|
|
# ============================================================
|
|
# shellcheck disable=SC2120 # MOUNT_POINT arrives as a global from earlier steps
|
|
install_samios_cli() {
|
|
log "Installing SamiOS CLI tool..."
|
|
|
|
# Tolerate being called with no args; default to /mnt as every other
|
|
# step in the installer does. shellcheck SC2120 sees no args because
|
|
# MOUNT_POINT is a global from earlier steps.
|
|
local MOUNT_POINT="${1:-${MOUNT_POINT:-/mnt}}"
|
|
|
|
cat > "$MOUNT_POINT/usr/local/bin/samios" << 'SAMIOS_CLI'
|
|
#!/bin/bash
|
|
# SamiOS CLI - installed by samios-installer.sh
|
|
# No embedded VERSION literal: walks up to /etc/samios-version at runtime.
|
|
set -e
|
|
|
|
_lookup_version() {
|
|
local d
|
|
d="$(cd "$(dirname "$0")" && pwd)"
|
|
while [ "$d" != "/" ]; do
|
|
if [ -f "$d/VERSION" ]; then
|
|
head -n1 "$d/VERSION"
|
|
return 0
|
|
fi
|
|
d="$(dirname "$d")"
|
|
done
|
|
if [ -r /etc/samios-version ]; then
|
|
head -n1 /etc/samios-version
|
|
return 0
|
|
fi
|
|
echo "0.0.0-unknown"
|
|
}
|
|
VERSION="$(_lookup_version)"
|
|
unset -f _lookup_version
|
|
|
|
cmd_help() {
|
|
cat << EOF
|
|
SamiOS CLI v${VERSION}
|
|
|
|
Commands:
|
|
version Show version
|
|
status Show system status
|
|
update Update system packages
|
|
install <pkg> Install a package
|
|
remove <pkg> Remove a package
|
|
services List running services
|
|
fonts Check fonts and policy
|
|
help Show this help
|
|
EOF
|
|
}
|
|
|
|
cmd_version() {
|
|
echo "SamiOS v${VERSION}"
|
|
echo "Kernel: $(uname -r)"
|
|
echo "Architecture: $(uname -m)"
|
|
}
|
|
|
|
cmd_status() {
|
|
echo "=== SamiOS System Status ==="
|
|
echo "Version: ${VERSION}"
|
|
echo "Hostname: $(hostname)"
|
|
echo "Kernel: $(uname -r)"
|
|
echo "Uptime: $(uptime -p)"
|
|
echo
|
|
echo "=== Disk Usage ==="
|
|
df -h / | tail -n 1
|
|
echo
|
|
echo "=== Memory ==="
|
|
free -h | grep "Mem:"
|
|
echo
|
|
echo "=== Network ==="
|
|
ip -br addr show | grep -v "lo" 2>/dev/null
|
|
}
|
|
|
|
cmd_update() { sudo pacman -Syu --noconfirm; }
|
|
cmd_install() { [ -z "$1" ] && echo "Usage: samios install <package>" && exit 1; sudo pacman -S --noconfirm "$1"; }
|
|
cmd_remove() { [ -z "$1" ] && echo "Usage: samios remove <package>" && exit 1; sudo pacman -R --noconfirm "$1"; }
|
|
cmd_services() { systemctl list-units --type=service --state=running; }
|
|
cmd_fonts() {
|
|
echo "=== Installed Fonts ==="
|
|
fc-list | wc -l
|
|
echo "fonts installed"
|
|
echo
|
|
echo "=== Font Policy ==="
|
|
echo "Excluded: fonts with '7777' in name (personal branding)"
|
|
if fc-list | grep -iq "7777"; then
|
|
echo "WARNING: Found 7777 fonts:"
|
|
fc-list | grep -i "7777"
|
|
else
|
|
echo "OK: No forbidden fonts"
|
|
fi
|
|
}
|
|
|
|
case "${1:-}" in
|
|
version) cmd_version ;;
|
|
status) cmd_status ;;
|
|
update) cmd_update ;;
|
|
install) shift; cmd_install "$@" ;;
|
|
remove) shift; cmd_remove "$@" ;;
|
|
services) cmd_services ;;
|
|
fonts) cmd_fonts ;;
|
|
help|--help|-h|"") cmd_help ;;
|
|
*) echo "Unknown: $1"; exit 1 ;;
|
|
esac
|
|
SAMIOS_CLI
|
|
|
|
chmod +x "$MOUNT_POINT/usr/local/bin/samios"
|
|
|
|
# Mirror the canonical VERSION file so the installed CLI's walk-up
|
|
# lookup resolves /etc/samios-version as a stable, non-relative anchor.
|
|
# The heredoc above writes a CLI with NO embedded VERSION literal —
|
|
# it walks up at runtime and reads from this file. This is the
|
|
# single-source-of-truth invariant: the literal exists ONLY in
|
|
# /etc/samios-version on disk, never inside any script.
|
|
if [ -n "$_EMIT_VERSION_FILE" ] && [ -f "$_EMIT_VERSION_FILE" ]; then
|
|
cp "$_EMIT_VERSION_FILE" "$MOUNT_POINT/etc/samios-version"
|
|
else
|
|
printf '%s\n' "$VERSION" > "$MOUNT_POINT/etc/samios-version"
|
|
fi
|
|
chmod 644 "$MOUNT_POINT/etc/samios-version"
|
|
|
|
# MOTD
|
|
cat > "$MOUNT_POINT/etc/motd" << 'MOTD'
|
|
|
|
___ ___ ___ ___ ___
|
|
/\ \ /\__\ /\ \ /\__\ /\ \
|
|
/::\ \ /::| | /::\ \ /:/ / /::\ \
|
|
/:/\:\ \ /:|:| | /:/\:\ \ /:/ / /:/\:\ \
|
|
/:\ \:\ \ /:/|:| |__ /::\~\:\ \ /:/ / ___ /::\~\:\ \
|
|
/:/\:\ \:\__\/:/ |:| /\__\ /:/\:\ \:\__\ /:/__/ /\__\ /:/\:\ \:\__\
|
|
\/__\:\/:/ /\/ |:|/:/ / \/_|::\/:/ / \:\ \ /:/ / \:\~\:\ \/__/
|
|
\::/ / |:/:/ / |:|::/ / \:\ /:/ / \:\ \:\__\
|
|
/:/ / |::/ / |:|\/__/ \:\/:/ / \:\ \/__/
|
|
/:/ / /:/ / |:| | \::/ / \:\__\
|
|
\/__/ \/__/ \|__| \/__/ \/__/
|
|
|
|
Welcome to SamiOS! Type 'samios help' to get started.
|
|
|
|
MOTD
|
|
|
|
# Bashrc welcome
|
|
cat > "$MOUNT_POINT/etc/bash.bashrc" << 'BASHRC'
|
|
# SamiOS system-wide bashrc
|
|
export PS1='\[\033[01;34m\]sami\[\033[01;34m\]@samios\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
|
|
|
|
alias ls='ls --color=auto'
|
|
alias ll='ls -la'
|
|
alias la='ls -A'
|
|
alias l='ls -CF'
|
|
alias update='sudo pacman -Syu'
|
|
alias install='sudo pacman -S'
|
|
alias remove='sudo pacman -R'
|
|
|
|
# SamiOS CLI in PATH
|
|
export PATH="/usr/local/bin:$PATH"
|
|
BASHRC
|
|
|
|
ok "SamiOS CLI and configs installed"
|
|
}
|
|
|
|
# ============================================================
|
|
# Cleanup and finish
|
|
# ============================================================
|
|
finish_install() {
|
|
log "Finalizing installation..."
|
|
|
|
MOUNT_POINT="/mnt"
|
|
|
|
# Unmount
|
|
umount -R "$MOUNT_POINT"
|
|
|
|
echo ""
|
|
echo "============================================"
|
|
echo " SamiOS Installation Complete! "
|
|
echo "============================================"
|
|
echo ""
|
|
echo " Disk: $TARGET_DISK"
|
|
echo " User: ${INSTALL_USER:-sami}"
|
|
echo " Bootloader: $([ "$UEFI" = true ] && echo "GRUB (UEFI)" || echo "GRUB (BIOS)")"
|
|
echo ""
|
|
echo " Next steps:"
|
|
echo " 1. Reboot: systemctl reboot"
|
|
echo " 2. Log in as ${INSTALL_USER:-sami}"
|
|
echo " 3. Run: samios status"
|
|
echo " 4. Install desktop: samios install plasma"
|
|
echo ""
|
|
}
|
|
|
|
# ============================================================
|
|
# Main
|
|
# ============================================================
|
|
main() {
|
|
echo ""
|
|
echo "============================================"
|
|
echo " SamiOS Installer v${VERSION} "
|
|
echo "============================================"
|
|
echo ""
|
|
|
|
check_root
|
|
check_uefi
|
|
detect_disks
|
|
select_disk
|
|
partition_disk
|
|
mount_and_install
|
|
configure_system
|
|
install_bootloader
|
|
create_user
|
|
install_samios_cli
|
|
finish_install
|
|
}
|
|
|
|
# Parse args
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--disk) TARGET_DISK="$2"; shift 2 ;;
|
|
--user) INSTALL_USER="$2"; shift 2 ;;
|
|
--password) INSTALL_PASSWORD="$2"; shift 2 ;;
|
|
--help|-h) echo "Usage: samios install [--disk /dev/sdX] [--user username] [--password pass]"; exit 0 ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
|
|
main
|