setup: add answerfile system for fully automated installs
tui-install.sh:
- Reads /answerfile.json if present (ANSWERFILE_MODE)
- All dialog selections (components, DE, apps) sourced from file
- Hostname from answerfile gets MAC address suffix appended to
prevent conflicts when deploying one image to multiple machines
- Interactive hostname inputbox added to the normal TUI flow
- Colorway dialog added as final step; skipped if no colors differ
from defaults and no answerfile colors are set
- Answerfile mode: runs non-interactively, logs warnings on failure
generate-answerfile.sh (new):
- Dry-runs the full installer dialog flow (OS + dotfiles)
- Writes selections to ~/answerfile.json (or a given path)
- No software is installed; passwords are never written to the file
build.sh:
- New --preconf [FILE] flag embeds an answerfile into the ISO at
/answerfile.json; omitting the flag leaves the ISO clean
- Validates JSON with jq if available before embedding
- Reworked arg parsing to handle the new flag alongside OUT_DIR
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
parent
e25dd231cb
commit
b5a3b46c79
|
|
@ -1,10 +1,48 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
# build.sh — build the M-Archy Arch Linux ISO
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash build.sh [--preconf [FILE]] [OUT_DIR]
|
||||||
|
#
|
||||||
|
# --preconf Embed ~/answerfile.json into the ISO at /answerfile.json
|
||||||
|
# --preconf FILE Embed the specified answerfile instead
|
||||||
|
# OUT_DIR Output directory (default: ~/m-archy-out, or $OUT_DIR env)
|
||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
DOTFILES_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
DOTFILES_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
|
||||||
|
# ── Argument parsing ───────────────────────────────────────────────────────────
|
||||||
|
PRECONF_FILE=""
|
||||||
|
OUT_ARG=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--preconf)
|
||||||
|
# Optional next arg: a file path (doesn't start with -)
|
||||||
|
if [[ $# -gt 1 && "${2:0:1}" != "-" ]]; then
|
||||||
|
PRECONF_FILE="$2"; shift
|
||||||
|
else
|
||||||
|
PRECONF_FILE="$HOME/answerfile.json"
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--preconf=*)
|
||||||
|
PRECONF_FILE="${1#--preconf=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
echo "Unknown flag: $1" >&2; exit 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
OUT_ARG="$1"; shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
WORK_DIR="${WORK_DIR:-$HOME/m-archy-build}"
|
WORK_DIR="${WORK_DIR:-$HOME/m-archy-build}"
|
||||||
OUT_DIR="${1:-${OUT_DIR:-$HOME/m-archy-out}}"
|
OUT_DIR="${OUT_ARG:-${OUT_DIR:-$HOME/m-archy-out}}"
|
||||||
PROFILE="$WORK_DIR/profile"
|
PROFILE="$WORK_DIR/profile"
|
||||||
RELENG="/usr/share/archiso/configs/releng"
|
RELENG="/usr/share/archiso/configs/releng"
|
||||||
|
|
||||||
|
|
@ -15,6 +53,16 @@ fi
|
||||||
|
|
||||||
[[ -d "$RELENG" ]] || { echo "ERROR: $RELENG not found — is archiso installed?"; exit 1; }
|
[[ -d "$RELENG" ]] || { echo "ERROR: $RELENG not found — is archiso installed?"; exit 1; }
|
||||||
|
|
||||||
|
# Validate answerfile early if --preconf was given
|
||||||
|
if [[ -n "$PRECONF_FILE" ]]; then
|
||||||
|
[[ -f "$PRECONF_FILE" ]] \
|
||||||
|
|| { echo "ERROR: answerfile not found: $PRECONF_FILE"; exit 1; }
|
||||||
|
command -v jq &>/dev/null \
|
||||||
|
&& jq empty "$PRECONF_FILE" \
|
||||||
|
|| echo "Warning: jq not available — skipping answerfile JSON validation"
|
||||||
|
echo "Answerfile to embed: $PRECONF_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
rm -rf "$WORK_DIR"
|
rm -rf "$WORK_DIR"
|
||||||
mkdir -p "$WORK_DIR" "$OUT_DIR"
|
mkdir -p "$WORK_DIR" "$OUT_DIR"
|
||||||
|
|
||||||
|
|
@ -43,9 +91,19 @@ chmod 755 \
|
||||||
"$PROFILE/airootfs/usr/local/bin/install-arch" \
|
"$PROFILE/airootfs/usr/local/bin/install-arch" \
|
||||||
"$PROFILE/airootfs/root/installer/"*.sh
|
"$PROFILE/airootfs/root/installer/"*.sh
|
||||||
|
|
||||||
|
# ── Embed answerfile (--preconf) ───────────────────────────────────────────────
|
||||||
|
if [[ -n "$PRECONF_FILE" ]]; then
|
||||||
|
echo "Embedding answerfile: $PRECONF_FILE → /answerfile.json"
|
||||||
|
install -m 644 "$PRECONF_FILE" "$PROFILE/airootfs/answerfile.json"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Building ISO (this may take a while)..."
|
echo "Building ISO (this may take a while)..."
|
||||||
sudo mkarchiso -v -w "$WORK_DIR/mkarchiso" -o "$OUT_DIR" "$PROFILE"
|
sudo mkarchiso -v -w "$WORK_DIR/mkarchiso" -o "$OUT_DIR" "$PROFILE"
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "Done."
|
echo "Done."
|
||||||
ls -lh "$OUT_DIR/"*.iso 2>/dev/null || true
|
ls -lh "$OUT_DIR/"*.iso 2>/dev/null || true
|
||||||
|
|
||||||
|
if [[ -n "$PRECONF_FILE" ]]; then
|
||||||
|
echo "Answerfile embedded — automated install will activate on boot."
|
||||||
|
fi
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,377 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# generate-answerfile.sh — dry-run the M-Archy installer dialogs and write
|
||||||
|
# all selections to an answerfile.json. Nothing is installed.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash generate-answerfile.sh [OUTPUT_PATH]
|
||||||
|
# OUTPUT_PATH defaults to ~/answerfile.json
|
||||||
|
#
|
||||||
|
# The generated file can be placed at /answerfile.json on the installer USB
|
||||||
|
# (use build.sh --preconf to embed it automatically) so that the full
|
||||||
|
# install — base OS + dotfiles — runs without any manual input.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DOTFILES_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
OUTPUT="${1:-$HOME/answerfile.json}"
|
||||||
|
TMP_D="$(mktemp -d)"
|
||||||
|
trap 'rm -rf "$TMP_D"' EXIT
|
||||||
|
|
||||||
|
BACKTITLE="M-Archy Answerfile Generator"
|
||||||
|
|
||||||
|
# ── Dialog theme ──────────────────────────────────────────────────────────────
|
||||||
|
export DIALOGRC="$TMP_D/dialogrc"
|
||||||
|
cat > "$DIALOGRC" <<'EOF'
|
||||||
|
use_shadow = ON
|
||||||
|
use_colors = ON
|
||||||
|
screen_color = (BLACK,BLACK,ON)
|
||||||
|
shadow_color = (BLACK,BLACK,ON)
|
||||||
|
title_color = (MAGENTA,BLACK,ON)
|
||||||
|
border_color = (MAGENTA,BLACK,ON)
|
||||||
|
button_active_color = (BLACK,MAGENTA,ON)
|
||||||
|
button_inactive_color = (WHITE,BLACK,OFF)
|
||||||
|
button_key_active_color = (BLACK,CYAN,ON)
|
||||||
|
button_key_inactive_color = (CYAN,BLACK,ON)
|
||||||
|
button_label_active_color = (BLACK,MAGENTA,ON)
|
||||||
|
button_label_inactive_color = (WHITE,BLACK,OFF)
|
||||||
|
inputbox_color = (WHITE,BLACK,OFF)
|
||||||
|
inputbox_border_color = (MAGENTA,BLACK,ON)
|
||||||
|
menubox_color = (WHITE,BLACK,OFF)
|
||||||
|
menubox_border_color = (MAGENTA,BLACK,ON)
|
||||||
|
item_color = (WHITE,BLACK,OFF)
|
||||||
|
item_selected_color = (BLACK,MAGENTA,ON)
|
||||||
|
tag_color = (CYAN,BLACK,ON)
|
||||||
|
tag_selected_color = (BLACK,CYAN,ON)
|
||||||
|
tag_key_color = (CYAN,BLACK,ON)
|
||||||
|
tag_key_selected_color = (BLACK,CYAN,ON)
|
||||||
|
check_color = (WHITE,BLACK,OFF)
|
||||||
|
check_selected_color = (BLACK,MAGENTA,ON)
|
||||||
|
uarrow_color = (MAGENTA,BLACK,ON)
|
||||||
|
darrow_color = (MAGENTA,BLACK,ON)
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
require_dialog() {
|
||||||
|
command -v dialog &>/dev/null && return
|
||||||
|
echo "dialog not found — installing..."
|
||||||
|
sudo pacman -S --noconfirm dialog || { echo "Failed to install dialog."; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
require_jq() {
|
||||||
|
command -v jq &>/dev/null && return
|
||||||
|
echo "jq not found — installing..."
|
||||||
|
sudo pacman -S --noconfirm jq || { echo "Failed to install jq."; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
die() { clear; printf "\n Error: %s\n\n" "$1" >&2; exit 1; }
|
||||||
|
|
||||||
|
# json_str: emit a properly-quoted JSON string value (handles empty → null)
|
||||||
|
json_str() {
|
||||||
|
local v="$1"
|
||||||
|
if [[ -z "$v" ]]; then printf 'null'; else printf '"%s"' "$v"; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# json_bool: emit true/false from YES/NO or true/false input
|
||||||
|
json_bool() {
|
||||||
|
local v="${1,,}"
|
||||||
|
[[ "$v" == "yes" || "$v" == "true" ]] && echo "true" || echo "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Preflight ─────────────────────────────────────────────────────────────────
|
||||||
|
require_dialog
|
||||||
|
require_jq
|
||||||
|
|
||||||
|
# ── Welcome ───────────────────────────────────────────────────────────────────
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Answerfile Generator " \
|
||||||
|
--msgbox "\n\
|
||||||
|
This tool walks you through all installer dialogs\n\
|
||||||
|
and saves your choices to an answerfile.\n\
|
||||||
|
─────────────────────────────────────────────────\n\
|
||||||
|
\n\
|
||||||
|
NO software will be installed — this is a dry run.\n\
|
||||||
|
\n\
|
||||||
|
Output: $OUTPUT\n\
|
||||||
|
\n\
|
||||||
|
To use on a USB: build.sh --preconf $OUTPUT\n" 16 62
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# PART 1 — Base OS install options
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
# ── Drive ─────────────────────────────────────────────────────────────────────
|
||||||
|
AVAIL_DRIVES=$(lsblk -dn -o NAME,SIZE,MODEL 2>/dev/null | awk '{printf "%s \"%s %s\" off ", $1, $2, $3}' || true)
|
||||||
|
|
||||||
|
AF_DRIVE=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Target Drive " \
|
||||||
|
--inputbox "\n Enter the install drive (e.g. /dev/sda, /dev/nvme0n1).\n\n Available drives:\n$(lsblk -dn -o NAME,SIZE,MODEL 2>/dev/null | sed 's/^/ /')\n" \
|
||||||
|
16 64 "/dev/sda" \
|
||||||
|
3>&1 1>&2 2>&3) || AF_DRIVE=""
|
||||||
|
|
||||||
|
# ── Kernel ────────────────────────────────────────────────────────────────────
|
||||||
|
AF_KERNEL=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Kernel " \
|
||||||
|
--menu "Select kernel package:" 12 54 3 \
|
||||||
|
"linux" "Stable kernel" \
|
||||||
|
"linux-lts" "Long-term support kernel" \
|
||||||
|
"linux-zen" "Zen performance kernel" \
|
||||||
|
3>&1 1>&2 2>&3) || AF_KERNEL="linux"
|
||||||
|
|
||||||
|
# ── Hostname ──────────────────────────────────────────────────────────────────
|
||||||
|
AF_HOSTNAME=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Hostname " \
|
||||||
|
--inputbox "\n Hostname for the new system.\n\n Leave blank to keep default.\n\n Note: a MAC address suffix will be appended\n automatically when the answerfile is applied,\n preventing hostname conflicts across machines.\n" \
|
||||||
|
14 62 "" \
|
||||||
|
3>&1 1>&2 2>&3) || AF_HOSTNAME=""
|
||||||
|
|
||||||
|
# ── Username ──────────────────────────────────────────────────────────────────
|
||||||
|
AF_USERNAME=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Username " \
|
||||||
|
--inputbox "\n Name for the primary user account.\n" \
|
||||||
|
9 54 "" \
|
||||||
|
3>&1 1>&2 2>&3) || AF_USERNAME=""
|
||||||
|
|
||||||
|
# NOTE: passwords are intentionally NOT stored in the answerfile.
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Password " \
|
||||||
|
--msgbox "\n Passwords are NOT stored in the answerfile.\n\n You will be prompted for the user password\n at install time even in automated mode.\n" \
|
||||||
|
10 56
|
||||||
|
|
||||||
|
# ── Disk encryption ───────────────────────────────────────────────────────────
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Disk Encryption " \
|
||||||
|
--yesno "\n Enable LUKS2 disk encryption on the root partition?\n\n If yes, a backup LUKS key will be auto-generated\n and placed at /_LUKS_BACKUP_KEY in the new system.\n" \
|
||||||
|
11 62 && AF_ENCRYPT="true" || AF_ENCRYPT="false"
|
||||||
|
|
||||||
|
AF_FIDO2_ROOT="false"
|
||||||
|
AF_FIDO2_USER="false"
|
||||||
|
if [[ "$AF_ENCRYPT" == "true" ]]; then
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " FIDO2 Root Unlock " \
|
||||||
|
--yesno "\n Enable FIDO2 hardware key for LUKS root unlock?\n" \
|
||||||
|
8 56 && AF_FIDO2_ROOT="true" || AF_FIDO2_ROOT="false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " FIDO2 User Login " \
|
||||||
|
--yesno "\n Enable FIDO2 hardware key for user login (PAM)?\n" \
|
||||||
|
8 56 && AF_FIDO2_USER="true" || AF_FIDO2_USER="false"
|
||||||
|
|
||||||
|
# ── Run TUI installer after base install ──────────────────────────────────────
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Dotfiles Setup " \
|
||||||
|
--yesno "\n Automatically run the dotfiles TUI installer\n inside the chroot after base install completes?\n" \
|
||||||
|
9 58 && AF_RUN_TUI="true" || AF_RUN_TUI="false"
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
# PART 2 — Dotfiles / TUI installer options
|
||||||
|
# (only shown if run_tui is true)
|
||||||
|
# ═══════════════════════════════════════════════════════════════
|
||||||
|
AF_COMPONENTS=""
|
||||||
|
AF_DE="none"
|
||||||
|
AF_APPS=""
|
||||||
|
AF_COLOR_TEXT=""
|
||||||
|
AF_COLOR_BG=""
|
||||||
|
AF_COLOR_HIGHLIGHT=""
|
||||||
|
AF_COLOR_DARK=""
|
||||||
|
AF_COLOR_RED=""
|
||||||
|
|
||||||
|
if [[ "$AF_RUN_TUI" == "true" ]]; then
|
||||||
|
|
||||||
|
# ── Components ────────────────────────────────────────────────────────────
|
||||||
|
AF_COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Select Components " \
|
||||||
|
--checklist "Space toggles · Enter confirms · Esc quits" 15 68 4 \
|
||||||
|
"pkg" "Package managers yay · nvm · rust" on \
|
||||||
|
"core" "Core packages 100+ base system packages" on \
|
||||||
|
"svc" "Core services NetworkManager · cronie · fail2ban" on \
|
||||||
|
"shell" "Shell setup zsh · nvim · yazi · micro · starship" on \
|
||||||
|
3>&1 1>&2 2>&3) || AF_COMPONENTS=""
|
||||||
|
|
||||||
|
# ── DE ────────────────────────────────────────────────────────────────────
|
||||||
|
AF_DE=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Desktop Environment " \
|
||||||
|
--menu "Select a desktop environment · Esc / none to skip:" 20 70 8 \
|
||||||
|
"hyprland" "Hyprland — Wayland WM, full setup (primary)" \
|
||||||
|
"sway" "Sway — Wayland tiling WM" \
|
||||||
|
"kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
|
||||||
|
"gnome" "GNOME — modern Wayland DE" \
|
||||||
|
"cosmic" "COSMIC — Rust-built Wayland DE (System76)" \
|
||||||
|
"xfce" "XFCE — lightweight X11 DE" \
|
||||||
|
"lxqt" "LXQt — lightweight Qt X11 DE" \
|
||||||
|
"none" "Skip DE installation" \
|
||||||
|
3>&1 1>&2 2>&3) || AF_DE="none"
|
||||||
|
|
||||||
|
# ── Apps ──────────────────────────────────────────────────────────────────
|
||||||
|
AF_APPS=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Applications " \
|
||||||
|
--checklist "Optional applications — installed after base components:" 40 76 32 \
|
||||||
|
"ollama" "Ollama local LLM runner + API server" off \
|
||||||
|
"llama-cpp" "llama.cpp standalone inference CLI + server" off \
|
||||||
|
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \
|
||||||
|
"claude" "Claude Code Anthropic CLI via npm" off \
|
||||||
|
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \
|
||||||
|
"disk-recovery" "Disk Recovery ddrescue · f3" off \
|
||||||
|
"himalaya" "Himalaya terminal email client (AUR)" off \
|
||||||
|
"gnuplot" "Gnuplot scientific plotting" off \
|
||||||
|
"povray" "POV-Ray ray-tracing renderer" off \
|
||||||
|
"blender" "Blender 3D creation suite" off \
|
||||||
|
"toot" "toot Mastodon CLI client (AUR)" off \
|
||||||
|
"db-clients" "DB Clients pgcli · mycli" off \
|
||||||
|
"mysql" "MySQL / MariaDB mariadb server + setup" off \
|
||||||
|
"productivity" "Productivity taskwarrior · watson · jrnl" off \
|
||||||
|
"yt-dlp" "yt-dlp YouTube / media downloader" off \
|
||||||
|
"sox" "SoX audio processing toolkit" off \
|
||||||
|
"imagemagick" "ImageMagick image manipulation" off \
|
||||||
|
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \
|
||||||
|
"localtunnel" "LocalTunnel expose localhost via tunnel" off \
|
||||||
|
"butter" "butter btrfs snapshot backup (AUR)" off \
|
||||||
|
"tlp" "TLP laptop power management" off \
|
||||||
|
"steam" "Steam gaming platform" off \
|
||||||
|
"vesktop" "Vesktop Discord + Vencord theme" off \
|
||||||
|
"spotify" "Spotify launcher + Spicetify theming" off \
|
||||||
|
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \
|
||||||
|
"vintagestory" "Vintage Story survival game (AUR)" off \
|
||||||
|
"localsend" "LocalSend LAN file transfer (AUR)" off \
|
||||||
|
"croc" "croc cross-platform file transfer" off \
|
||||||
|
"onlyoffice" "OnlyOffice office suite (AUR)" off \
|
||||||
|
"wireshark" "Wireshark network packet analyser (GUI)" off \
|
||||||
|
"k8s" "Kubernetes tools kubectl · podman-desktop" off \
|
||||||
|
"docker" "Docker docker · docker-compose" off \
|
||||||
|
"podman" "Podman rootless containers · buildah" off \
|
||||||
|
"cockpit" "Cockpit web UI · machines · podman" off \
|
||||||
|
"ssh-server" "SSH server openssh · key-auth · enabled" off \
|
||||||
|
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \
|
||||||
|
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \
|
||||||
|
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \
|
||||||
|
"python" "Python tools pyright · pipx · pynvim" off \
|
||||||
|
"zfs" "ZFS zfs-dkms kernel module" off \
|
||||||
|
"wprs" "WPRS wprs-git (AUR)" off \
|
||||||
|
"chromium" "Chromium open-source browser (official)" off \
|
||||||
|
"firefox-browser" "Firefox Mozilla browser (official)" off \
|
||||||
|
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \
|
||||||
|
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \
|
||||||
|
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \
|
||||||
|
"min-browser" "Min minimal Electron browser (AUR)" off \
|
||||||
|
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \
|
||||||
|
"zed-ide" "Zed high-performance Rust IDE (official)" off \
|
||||||
|
"geany" "Geany lightweight IDE + plugins (official)" off \
|
||||||
|
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \
|
||||||
|
"kate" "Kate KDE advanced text editor (official)" off \
|
||||||
|
3>&1 1>&2 2>&3) || AF_APPS=""
|
||||||
|
|
||||||
|
# ── Colorway ──────────────────────────────────────────────────────────────
|
||||||
|
# Read defaults from repo colors.conf
|
||||||
|
declare -A _cdef
|
||||||
|
if [[ -f "$DOTFILES_DIR/colors.conf" ]]; then
|
||||||
|
while IFS='=' read -r k v; do
|
||||||
|
k="${k%%[[:space:]]*}"
|
||||||
|
[[ "$k" =~ ^[[:space:]]*# || -z "$k" ]] && continue
|
||||||
|
v="${v%%#*}"; v="${v//[[:space:]]/}"; v="${v^^}"
|
||||||
|
_cdef[$k]="$v"
|
||||||
|
done < "$DOTFILES_DIR/colors.conf"
|
||||||
|
fi
|
||||||
|
DEF_TEXT="${_cdef[COLOR_TEXT]:-D6ABAB}"
|
||||||
|
DEF_BG="${_cdef[COLOR_BG]:-1A1A1A}"
|
||||||
|
DEF_HIGHLIGHT="${_cdef[COLOR_HIGHLIGHT]:-E40046}"
|
||||||
|
DEF_DARK="${_cdef[COLOR_DARK]:-5018DD}"
|
||||||
|
DEF_RED="${_cdef[COLOR_RED]:-F50505}"
|
||||||
|
|
||||||
|
COLORWAY_RAW=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Colorway (optional) " \
|
||||||
|
--form "\n Customize theme colors — bare 6-digit hex, no #.\n Leave unchanged to omit colors from answerfile.\n" \
|
||||||
|
16 62 5 \
|
||||||
|
"COLOR_TEXT " 1 1 "$DEF_TEXT" 1 18 10 7 \
|
||||||
|
"COLOR_BG " 2 1 "$DEF_BG" 2 18 10 7 \
|
||||||
|
"COLOR_HIGHLIGHT " 3 1 "$DEF_HIGHLIGHT" 3 18 10 7 \
|
||||||
|
"COLOR_DARK " 4 1 "$DEF_DARK" 4 18 10 7 \
|
||||||
|
"COLOR_RED " 5 1 "$DEF_RED" 5 18 10 7 \
|
||||||
|
3>&1 1>&2 2>&3) || COLORWAY_RAW=""
|
||||||
|
|
||||||
|
if [[ -n "$COLORWAY_RAW" ]]; then
|
||||||
|
mapfile -t _cv <<< "$COLORWAY_RAW"
|
||||||
|
N_TEXT="${_cv[0]:-$DEF_TEXT}"
|
||||||
|
N_BG="${_cv[1]:-$DEF_BG}"
|
||||||
|
N_HIGHLIGHT="${_cv[2]:-$DEF_HIGHLIGHT}"
|
||||||
|
N_DARK="${_cv[3]:-$DEF_DARK}"
|
||||||
|
N_RED="${_cv[4]:-$DEF_RED}"
|
||||||
|
# Only save colors if any differ from defaults
|
||||||
|
if [[ "${N_TEXT^^}" != "$DEF_TEXT" || \
|
||||||
|
"${N_BG^^}" != "$DEF_BG" || \
|
||||||
|
"${N_HIGHLIGHT^^}" != "$DEF_HIGHLIGHT" || \
|
||||||
|
"${N_DARK^^}" != "$DEF_DARK" || \
|
||||||
|
"${N_RED^^}" != "$DEF_RED" ]]; then
|
||||||
|
AF_COLOR_TEXT="${N_TEXT^^}"
|
||||||
|
AF_COLOR_BG="${N_BG^^}"
|
||||||
|
AF_COLOR_HIGHLIGHT="${N_HIGHLIGHT^^}"
|
||||||
|
AF_COLOR_DARK="${N_DARK^^}"
|
||||||
|
AF_COLOR_RED="${N_RED^^}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Confirmation ──────────────────────────────────────────────────────────────
|
||||||
|
SUMMARY=""
|
||||||
|
[[ -n "$AF_DRIVE" ]] && SUMMARY+=" Drive: $AF_DRIVE\n"
|
||||||
|
[[ -n "$AF_KERNEL" ]] && SUMMARY+=" Kernel: $AF_KERNEL\n"
|
||||||
|
[[ -n "$AF_HOSTNAME" ]] && SUMMARY+=" Hostname: $AF_HOSTNAME (+ MAC suffix at deploy)\n"
|
||||||
|
[[ -n "$AF_USERNAME" ]] && SUMMARY+=" Username: $AF_USERNAME\n"
|
||||||
|
SUMMARY+=" Encrypt: $AF_ENCRYPT\n"
|
||||||
|
SUMMARY+=" FIDO2 root: $AF_FIDO2_ROOT / FIDO2 user: $AF_FIDO2_USER\n"
|
||||||
|
SUMMARY+=" Run TUI: $AF_RUN_TUI\n"
|
||||||
|
[[ -n "$AF_DE" && "$AF_DE" != "none" ]] && SUMMARY+=" DE: $AF_DE\n"
|
||||||
|
[[ -n "$AF_COLOR_TEXT" ]] && SUMMARY+=" Colors: custom\n"
|
||||||
|
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Confirm " \
|
||||||
|
--yesno "\n Save answerfile with these settings:\n\n${SUMMARY}\n Output: $OUTPUT\n\n Proceed?" \
|
||||||
|
22 66 || { clear; echo "Aborted."; exit 0; }
|
||||||
|
|
||||||
|
# ── Build JSON arrays from space-separated dialog output ──────────────────────
|
||||||
|
_words_to_json_array() {
|
||||||
|
local input="$1"
|
||||||
|
local first=1
|
||||||
|
printf '['
|
||||||
|
for w in $input; do
|
||||||
|
[[ $first -eq 0 ]] && printf ','
|
||||||
|
printf '"%s"' "$w"
|
||||||
|
first=0
|
||||||
|
done
|
||||||
|
printf ']'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Write answerfile ──────────────────────────────────────────────────────────
|
||||||
|
mkdir -p "$(dirname "$OUTPUT")"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf '{\n'
|
||||||
|
printf ' "_generated": "%s",\n' "$(date -Iseconds)"
|
||||||
|
printf ' "drive": %s,\n' "$(json_str "$AF_DRIVE")"
|
||||||
|
printf ' "kernel": %s,\n' "$(json_str "$AF_KERNEL")"
|
||||||
|
printf ' "hostname": %s,\n' "$(json_str "$AF_HOSTNAME")"
|
||||||
|
printf ' "username": %s,\n' "$(json_str "$AF_USERNAME")"
|
||||||
|
printf ' "encrypt": %s,\n' "$AF_ENCRYPT"
|
||||||
|
printf ' "fido2_root": %s,\n' "$AF_FIDO2_ROOT"
|
||||||
|
printf ' "fido2_user": %s,\n' "$AF_FIDO2_USER"
|
||||||
|
printf ' "run_tui": %s,\n' "$AF_RUN_TUI"
|
||||||
|
printf ' "components": %s,\n' "$(_words_to_json_array "$AF_COMPONENTS")"
|
||||||
|
printf ' "desktop_environment": %s,\n' "$(json_str "$AF_DE")"
|
||||||
|
printf ' "apps": %s' "$(_words_to_json_array "$AF_APPS")"
|
||||||
|
|
||||||
|
if [[ -n "$AF_COLOR_TEXT" ]]; then
|
||||||
|
printf ',\n "colors": {\n'
|
||||||
|
printf ' "COLOR_TEXT": "%s",\n' "$AF_COLOR_TEXT"
|
||||||
|
printf ' "COLOR_BG": "%s",\n' "$AF_COLOR_BG"
|
||||||
|
printf ' "COLOR_HIGHLIGHT": "%s",\n' "$AF_COLOR_HIGHLIGHT"
|
||||||
|
printf ' "COLOR_DARK": "%s",\n' "$AF_COLOR_DARK"
|
||||||
|
printf ' "COLOR_RED": "%s"\n' "$AF_COLOR_RED"
|
||||||
|
printf ' }'
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '\n}\n'
|
||||||
|
} > "$OUTPUT"
|
||||||
|
|
||||||
|
clear
|
||||||
|
printf "\n Answerfile saved to: %s\n\n" "$OUTPUT"
|
||||||
|
printf " To embed in ISO: bash setup/archiso/build.sh --preconf %s\n\n" "$OUTPUT"
|
||||||
|
|
@ -12,6 +12,10 @@ LOG="$HOME/dotfiles-install.log"
|
||||||
TMP_D="$(mktemp -d)"
|
TMP_D="$(mktemp -d)"
|
||||||
trap 'rm -rf "$TMP_D"' EXIT
|
trap 'rm -rf "$TMP_D"' EXIT
|
||||||
|
|
||||||
|
ANSWERFILE="${ANSWERFILE:-/answerfile.json}"
|
||||||
|
ANSWERFILE_MODE=false
|
||||||
|
[[ -f "$ANSWERFILE" ]] && ANSWERFILE_MODE=true
|
||||||
|
|
||||||
BACKTITLE="the_miro's Arch Dotfiles"
|
BACKTITLE="the_miro's Arch Dotfiles"
|
||||||
|
|
||||||
# ── Cyberqueer dialog theme ───────────────────────────────────────────────────
|
# ── Cyberqueer dialog theme ───────────────────────────────────────────────────
|
||||||
|
|
@ -56,6 +60,12 @@ require_dialog() {
|
||||||
sudo pacman -S --noconfirm dialog || { echo "Failed to install dialog."; exit 1; }
|
sudo pacman -S --noconfirm dialog || { echo "Failed to install dialog."; exit 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require_jq() {
|
||||||
|
command -v jq &>/dev/null && return
|
||||||
|
echo "jq not found — installing..."
|
||||||
|
sudo pacman -S --noconfirm jq || { echo "Failed to install jq."; exit 1; }
|
||||||
|
}
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
clear
|
clear
|
||||||
printf "\n Error: %s\n\n" "$1" >&2
|
printf "\n Error: %s\n\n" "$1" >&2
|
||||||
|
|
@ -79,10 +89,14 @@ run_module() {
|
||||||
bash "$script" 2>&1 | tee -a "$LOG" || rc=${PIPESTATUS[0]}
|
bash "$script" 2>&1 | tee -a "$LOG" || rc=${PIPESTATUS[0]}
|
||||||
|
|
||||||
if [[ $rc -ne 0 ]]; then
|
if [[ $rc -ne 0 ]]; then
|
||||||
dialog --backtitle "$BACKTITLE" \
|
if [[ $ANSWERFILE_MODE == true ]]; then
|
||||||
--title " Module Failed " \
|
printf "\n Warning: %s exited with code %d — continuing.\n" "$label" "$rc" | tee -a "$LOG"
|
||||||
--yesno "$label exited with code $rc.\n\nContinue anyway?" 8 54 \
|
else
|
||||||
|| { clear; exit 1; }
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Module Failed " \
|
||||||
|
--yesno "$label exited with code $rc.\n\nContinue anyway?" 8 54 \
|
||||||
|
|| { clear; exit 1; }
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,22 +162,63 @@ count_steps() {
|
||||||
[[ "$a" == *"kate"* ]] && TOTAL=$(( TOTAL + 1 ))
|
[[ "$a" == *"kate"* ]] && TOTAL=$(( TOTAL + 1 ))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ── Answerfile ────────────────────────────────────────────────────────────────
|
||||||
|
AF_HOSTNAME=""
|
||||||
|
AF_COMPONENTS=""
|
||||||
|
AF_DE="none"
|
||||||
|
AF_APPS=""
|
||||||
|
AF_COLOR_TEXT=""
|
||||||
|
AF_COLOR_BG=""
|
||||||
|
AF_COLOR_HIGHLIGHT=""
|
||||||
|
AF_COLOR_DARK=""
|
||||||
|
AF_COLOR_RED=""
|
||||||
|
|
||||||
|
load_answerfile() {
|
||||||
|
require_jq
|
||||||
|
AF_HOSTNAME=$(jq -r '.hostname // ""' "$ANSWERFILE")
|
||||||
|
AF_COMPONENTS=$(jq -r '(.components // []) | join(" ")' "$ANSWERFILE")
|
||||||
|
AF_DE=$(jq -r '.desktop_environment // "none"' "$ANSWERFILE")
|
||||||
|
AF_APPS=$(jq -r '(.apps // []) | join(" ")' "$ANSWERFILE")
|
||||||
|
AF_COLOR_TEXT=$(jq -r '.colors.COLOR_TEXT // ""' "$ANSWERFILE")
|
||||||
|
AF_COLOR_BG=$(jq -r '.colors.COLOR_BG // ""' "$ANSWERFILE")
|
||||||
|
AF_COLOR_HIGHLIGHT=$(jq -r '.colors.COLOR_HIGHLIGHT // ""' "$ANSWERFILE")
|
||||||
|
AF_COLOR_DARK=$(jq -r '.colors.COLOR_DARK // ""' "$ANSWERFILE")
|
||||||
|
AF_COLOR_RED=$(jq -r '.colors.COLOR_RED // ""' "$ANSWERFILE")
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── MAC address helper ────────────────────────────────────────────────────────
|
||||||
|
get_mac_suffix() {
|
||||||
|
local mac
|
||||||
|
mac=$(ip link show 2>/dev/null \
|
||||||
|
| awk '/^[0-9]+: [^l][^o]/{iface=1} iface && /link\/ether/{print $2; iface=0; exit}')
|
||||||
|
printf '%s' "${mac//:/}"
|
||||||
|
}
|
||||||
|
|
||||||
# ── Preflight ─────────────────────────────────────────────────────────────────
|
# ── Preflight ─────────────────────────────────────────────────────────────────
|
||||||
[[ $EUID -eq 0 ]] && die "Run as your normal user (not root)."
|
[[ $EUID -eq 0 ]] && die "Run as your normal user (not root)."
|
||||||
command -v pacman &>/dev/null || die "pacman not found — Arch Linux required."
|
command -v pacman &>/dev/null || die "pacman not found — Arch Linux required."
|
||||||
|
|
||||||
require_dialog
|
require_dialog
|
||||||
|
|
||||||
|
if $ANSWERFILE_MODE; then
|
||||||
|
load_answerfile
|
||||||
|
printf "Answerfile mode: %s\n" "$ANSWERFILE" | tee -a "$LOG"
|
||||||
|
fi
|
||||||
|
|
||||||
if ! ping -c1 -W3 archlinux.org &>/dev/null; then
|
if ! ping -c1 -W3 archlinux.org &>/dev/null; then
|
||||||
dialog --backtitle "$BACKTITLE" \
|
if $ANSWERFILE_MODE; then
|
||||||
--title " No Network Detected " \
|
printf "Warning: no internet connection detected.\n" | tee -a "$LOG"
|
||||||
--msgbox "\n No internet connection found.\n\n nmtui will open so you can configure networking.\n Close nmtui when done to continue the installer.\n" 11 58
|
else
|
||||||
nmtui
|
|
||||||
if ! ping -c1 -W3 archlinux.org &>/dev/null; then
|
|
||||||
dialog --backtitle "$BACKTITLE" \
|
dialog --backtitle "$BACKTITLE" \
|
||||||
--title " Still Offline " \
|
--title " No Network Detected " \
|
||||||
--yesno "\n Still no internet connection.\n\n Packages cannot be downloaded without network access.\n\n Continue anyway?" 11 58 \
|
--msgbox "\n No internet connection found.\n\n nmtui will open so you can configure networking.\n Close nmtui when done to continue the installer.\n" 11 58
|
||||||
|| { clear; echo "Aborted — no network."; exit 1; }
|
nmtui
|
||||||
|
if ! ping -c1 -W3 archlinux.org &>/dev/null; then
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Still Offline " \
|
||||||
|
--yesno "\n Still no internet connection.\n\n Packages cannot be downloaded without network access.\n\n Continue anyway?" 11 58 \
|
||||||
|
|| { clear; echo "Aborted — no network."; exit 1; }
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -171,9 +226,10 @@ fi
|
||||||
printf "Dotfiles install: %s\nDotfiles dir: %s\n" "$(date)" "$DOTFILES_DIR" >> "$LOG"
|
printf "Dotfiles install: %s\nDotfiles dir: %s\n" "$(date)" "$DOTFILES_DIR" >> "$LOG"
|
||||||
|
|
||||||
# ── Welcome ───────────────────────────────────────────────────────────────────
|
# ── Welcome ───────────────────────────────────────────────────────────────────
|
||||||
dialog --backtitle "$BACKTITLE" \
|
if ! $ANSWERFILE_MODE; then
|
||||||
--title " Welcome " \
|
dialog --backtitle "$BACKTITLE" \
|
||||||
--msgbox "\n\
|
--title " Welcome " \
|
||||||
|
--msgbox "\n\
|
||||||
the_miro's Arch dotfiles installer\n\
|
the_miro's Arch dotfiles installer\n\
|
||||||
Cyberqueer · Wayland · Hyprland\n\
|
Cyberqueer · Wayland · Hyprland\n\
|
||||||
─────────────────────────────────────────\n\
|
─────────────────────────────────────────\n\
|
||||||
|
|
@ -182,159 +238,197 @@ dialog --backtitle "$BACKTITLE" \
|
||||||
\n\
|
\n\
|
||||||
Source: $DOTFILES_DIR\n\
|
Source: $DOTFILES_DIR\n\
|
||||||
Log: $LOG\n" 14 62
|
Log: $LOG\n" 14 62
|
||||||
|
|
||||||
# ── Component selection ───────────────────────────────────────────────────────
|
|
||||||
COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
|
|
||||||
--title " Select Components " \
|
|
||||||
--checklist "Space toggles · Enter confirms · Esc quits" 15 68 4 \
|
|
||||||
"pkg" "Package managers yay · nvm · rust" on \
|
|
||||||
"core" "Core packages 100+ base system packages" on \
|
|
||||||
"svc" "Core services NetworkManager · cronie · fail2ban" on \
|
|
||||||
"shell" "Shell setup zsh · nvim · yazi · micro · starship" on \
|
|
||||||
3>&1 1>&2 2>&3) || { clear; echo "Aborted."; exit 0; }
|
|
||||||
|
|
||||||
# ── DE selection ──────────────────────────────────────────────────────────────
|
|
||||||
DE=$(dialog --backtitle "$BACKTITLE" \
|
|
||||||
--title " Desktop Environment " \
|
|
||||||
--menu "Select a desktop environment · Esc / none to skip:" 20 70 8 \
|
|
||||||
"hyprland" "Hyprland — Wayland WM, full setup (primary)" \
|
|
||||||
"sway" "Sway — Wayland tiling WM" \
|
|
||||||
"kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
|
|
||||||
"gnome" "GNOME — modern Wayland DE" \
|
|
||||||
"cosmic" "COSMIC — Rust-built Wayland DE (System76)" \
|
|
||||||
"xfce" "XFCE — lightweight X11 DE" \
|
|
||||||
"lxqt" "LXQt — lightweight Qt X11 DE" \
|
|
||||||
"none" "Skip DE installation" \
|
|
||||||
3>&1 1>&2 2>&3) || DE="none"
|
|
||||||
|
|
||||||
# ── Apps selection ────────────────────────────────────────────────────────────
|
|
||||||
SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \
|
|
||||||
--title " Applications " \
|
|
||||||
--checklist "Optional applications — installed after base components:" 40 76 32 \
|
|
||||||
"ollama" "Ollama local LLM runner + API server" off \
|
|
||||||
"llama-cpp" "llama.cpp standalone inference CLI + server" off \
|
|
||||||
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \
|
|
||||||
"claude" "Claude Code Anthropic CLI via npm" off \
|
|
||||||
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \
|
|
||||||
"disk-recovery" "Disk Recovery ddrescue · f3" off \
|
|
||||||
"himalaya" "Himalaya terminal email client (AUR)" off \
|
|
||||||
"gnuplot" "Gnuplot scientific plotting" off \
|
|
||||||
"povray" "POV-Ray ray-tracing renderer" off \
|
|
||||||
"blender" "Blender 3D creation suite" off \
|
|
||||||
"toot" "toot Mastodon CLI client (AUR)" off \
|
|
||||||
"db-clients" "DB Clients pgcli · mycli" off \
|
|
||||||
"mysql" "MySQL / MariaDB mariadb server + setup" off \
|
|
||||||
"productivity" "Productivity taskwarrior · watson · jrnl" off \
|
|
||||||
"yt-dlp" "yt-dlp YouTube / media downloader" off \
|
|
||||||
"sox" "SoX audio processing toolkit" off \
|
|
||||||
"imagemagick" "ImageMagick image manipulation" off \
|
|
||||||
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \
|
|
||||||
"localtunnel" "LocalTunnel expose localhost via tunnel" off \
|
|
||||||
"butter" "butter btrfs snapshot backup (AUR)" off \
|
|
||||||
"tlp" "TLP laptop power management" off \
|
|
||||||
"steam" "Steam gaming platform" off \
|
|
||||||
"vesktop" "Vesktop Discord + Vencord theme" off \
|
|
||||||
"spotify" "Spotify launcher + Spicetify theming" off \
|
|
||||||
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \
|
|
||||||
"vintagestory" "Vintage Story survival game (AUR)" off \
|
|
||||||
"localsend" "LocalSend LAN file transfer (AUR)" off \
|
|
||||||
"croc" "croc cross-platform file transfer" off \
|
|
||||||
"onlyoffice" "OnlyOffice office suite (AUR)" off \
|
|
||||||
"wireshark" "Wireshark network packet analyser (GUI)" off \
|
|
||||||
"k8s" "Kubernetes tools kubectl · podman-desktop" off \
|
|
||||||
"docker" "Docker docker · docker-compose" off \
|
|
||||||
"podman" "Podman rootless containers · buildah" off \
|
|
||||||
"cockpit" "Cockpit web UI · machines · podman" off \
|
|
||||||
"ssh-server" "SSH server openssh · key-auth · enabled" off \
|
|
||||||
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \
|
|
||||||
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \
|
|
||||||
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \
|
|
||||||
"python" "Python tools pyright · pipx · pynvim" off \
|
|
||||||
"zfs" "ZFS zfs-dkms kernel module" off \
|
|
||||||
"wprs" "WPRS wprs-git (AUR)" off \
|
|
||||||
\
|
|
||||||
"chromium" "Chromium open-source browser (official)" off \
|
|
||||||
"firefox-browser" "Firefox Mozilla browser (official)" off \
|
|
||||||
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \
|
|
||||||
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \
|
|
||||||
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \
|
|
||||||
"min-browser" "Min minimal Electron browser (AUR)" off \
|
|
||||||
\
|
|
||||||
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \
|
|
||||||
"zed-ide" "Zed high-performance Rust IDE (official)" off \
|
|
||||||
"geany" "Geany lightweight IDE + plugins (official)" off \
|
|
||||||
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \
|
|
||||||
"kate" "Kate KDE advanced text editor (official)" off \
|
|
||||||
3>&1 1>&2 2>&3) || SELECTED_APPS=""
|
|
||||||
|
|
||||||
# ── Confirmation ──────────────────────────────────────────────────────────────
|
|
||||||
SUMMARY=""
|
|
||||||
[[ "$COMPONENTS" == *"pkg"* ]] && SUMMARY+=" ✦ Package managers (yay, nvm, rust)\n"
|
|
||||||
[[ "$COMPONENTS" == *"core"* ]] && SUMMARY+=" ✦ Core packages\n"
|
|
||||||
[[ "$COMPONENTS" == *"svc"* ]] && SUMMARY+=" ✦ Core services\n"
|
|
||||||
[[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+=" ✦ Shell setup\n"
|
|
||||||
[[ "$DE" != "none" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
|
|
||||||
|
|
||||||
if [[ -n "$SELECTED_APPS" ]]; then
|
|
||||||
SUMMARY+="\n Applications:\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"ollama"* ]] && SUMMARY+=" ✦ Ollama\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"llama-cpp"* ]] && SUMMARY+=" ✦ llama.cpp\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"open-webui"* ]] && SUMMARY+=" ✦ Open WebUI\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"claude"* ]] && SUMMARY+=" ✦ Claude Code\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+=" ✦ Networking CLI (nmap, nethogs, mitmproxy, httpie)\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"disk-recovery"* ]] && SUMMARY+=" ✦ Disk Recovery (ddrescue, f3)\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"himalaya"* ]] && SUMMARY+=" ✦ Himalaya\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"gnuplot"* ]] && SUMMARY+=" ✦ Gnuplot\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"povray"* ]] && SUMMARY+=" ✦ POV-Ray\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"blender"* ]] && SUMMARY+=" ✦ Blender\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"toot"* ]] && SUMMARY+=" ✦ toot\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"db-clients"* ]] && SUMMARY+=" ✦ DB Clients (pgcli, mycli)\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"mysql"* ]] && SUMMARY+=" ✦ MySQL / MariaDB\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"productivity"* ]] && SUMMARY+=" ✦ Productivity (taskwarrior, watson, jrnl)\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"yt-dlp"* ]] && SUMMARY+=" ✦ yt-dlp\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"sox"* ]] && SUMMARY+=" ✦ SoX\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"imagemagick"* ]] && SUMMARY+=" ✦ ImageMagick\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"ffmpeg"* ]] && SUMMARY+=" ✦ FFmpeg extras\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"localtunnel"* ]] && SUMMARY+=" ✦ LocalTunnel\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"butter"* ]] && SUMMARY+=" ✦ butter (btrfs backup)\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"tlp"* ]] && SUMMARY+=" ✦ TLP\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"steam"* ]] && SUMMARY+=" ✦ Steam\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"vesktop"* ]] && SUMMARY+=" ✦ Vesktop + Vencord theme\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"spotify"* ]] && SUMMARY+=" ✦ Spotify + Spicetify\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"prism"* ]] && SUMMARY+=" ✦ PrismLauncher\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"vintagestory"* ]] && SUMMARY+=" ✦ Vintage Story\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"localsend"* ]] && SUMMARY+=" ✦ LocalSend\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"croc"* ]] && SUMMARY+=" ✦ croc\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"onlyoffice"* ]] && SUMMARY+=" ✦ OnlyOffice\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"wireshark"* ]] && SUMMARY+=" ✦ Wireshark\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"k8s"* ]] && SUMMARY+=" ✦ Kubernetes tools\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"docker"* ]] && SUMMARY+=" ✦ Docker + Compose\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"podman"* ]] && SUMMARY+=" ✦ Podman (rootless) + Buildah\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"cockpit"* ]] && SUMMARY+=" ✦ Cockpit web UI\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"ssh-server"* ]] && SUMMARY+=" ✦ SSH server (openssh, key auth)\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && SUMMARY+=" ✦ FreeIPA Client\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && SUMMARY+=" ✦ FreeIPA Server\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && SUMMARY+=" ✦ FreeIPA Image Builder\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"python"* ]] && SUMMARY+=" ✦ Python tools\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"zfs"* ]] && SUMMARY+=" ✦ ZFS\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"wprs"* ]] && SUMMARY+=" ✦ WPRS\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"chromium"* ]] && SUMMARY+=" ✦ Chromium\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && SUMMARY+=" ✦ Firefox\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"zen-browser"* ]] && SUMMARY+=" ✦ Zen Browser\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"nyxt"* ]] && SUMMARY+=" ✦ Nyxt\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"librewolf"* ]] && SUMMARY+=" ✦ LibreWolf\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"min-browser"* ]] && SUMMARY+=" ✦ Min Browser\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"vscodium"* ]] && SUMMARY+=" ✦ VSCodium\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"zed-ide"* ]] && SUMMARY+=" ✦ Zed IDE\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"geany"* ]] && SUMMARY+=" ✦ Geany\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && SUMMARY+=" ✦ Code::Blocks\n"
|
|
||||||
[[ "$SELECTED_APPS" == *"kate"* ]] && SUMMARY+=" ✦ Kate\n"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dialog --backtitle "$BACKTITLE" \
|
# ── Hostname ──────────────────────────────────────────────────────────────────
|
||||||
--title " Confirm Installation " \
|
HOSTNAME_SET=""
|
||||||
--yesno "\n Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \
|
if $ANSWERFILE_MODE; then
|
||||||
24 62 || { clear; echo "Aborted."; exit 0; }
|
if [[ -n "$AF_HOSTNAME" ]]; then
|
||||||
|
MAC=$(get_mac_suffix)
|
||||||
|
HOSTNAME_SET="${AF_HOSTNAME}-${MAC}"
|
||||||
|
printf "Hostname (from answerfile + MAC): %s\n" "$HOSTNAME_SET" | tee -a "$LOG"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
HOSTNAME_INPUT=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Hostname " \
|
||||||
|
--inputbox "\n Hostname for this machine (leave blank to keep default).\n" 9 54 "" \
|
||||||
|
3>&1 1>&2 2>&3) || HOSTNAME_INPUT=""
|
||||||
|
HOSTNAME_SET="$HOSTNAME_INPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$HOSTNAME_SET" ]]; then
|
||||||
|
sudo hostnamectl set-hostname "$HOSTNAME_SET" 2>/dev/null \
|
||||||
|
|| echo "$HOSTNAME_SET" | sudo tee /etc/hostname > /dev/null
|
||||||
|
printf "Hostname set: %s\n" "$HOSTNAME_SET" >> "$LOG"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Component selection ───────────────────────────────────────────────────────
|
||||||
|
if $ANSWERFILE_MODE; then
|
||||||
|
COMPONENTS="$AF_COMPONENTS"
|
||||||
|
else
|
||||||
|
COMPONENTS=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Select Components " \
|
||||||
|
--checklist "Space toggles · Enter confirms · Esc quits" 15 68 4 \
|
||||||
|
"pkg" "Package managers yay · nvm · rust" on \
|
||||||
|
"core" "Core packages 100+ base system packages" on \
|
||||||
|
"svc" "Core services NetworkManager · cronie · fail2ban" on \
|
||||||
|
"shell" "Shell setup zsh · nvim · yazi · micro · starship" on \
|
||||||
|
3>&1 1>&2 2>&3) || { clear; echo "Aborted."; exit 0; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── DE selection ──────────────────────────────────────────────────────────────
|
||||||
|
if $ANSWERFILE_MODE; then
|
||||||
|
DE="$AF_DE"
|
||||||
|
else
|
||||||
|
DE=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Desktop Environment " \
|
||||||
|
--menu "Select a desktop environment · Esc / none to skip:" 20 70 8 \
|
||||||
|
"hyprland" "Hyprland — Wayland WM, full setup (primary)" \
|
||||||
|
"sway" "Sway — Wayland tiling WM" \
|
||||||
|
"kde-plasma" "KDE Plasma — feature-rich Wayland/X11 DE" \
|
||||||
|
"gnome" "GNOME — modern Wayland DE" \
|
||||||
|
"cosmic" "COSMIC — Rust-built Wayland DE (System76)" \
|
||||||
|
"xfce" "XFCE — lightweight X11 DE" \
|
||||||
|
"lxqt" "LXQt — lightweight Qt X11 DE" \
|
||||||
|
"none" "Skip DE installation" \
|
||||||
|
3>&1 1>&2 2>&3) || DE="none"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Apps selection ────────────────────────────────────────────────────────────
|
||||||
|
if $ANSWERFILE_MODE; then
|
||||||
|
SELECTED_APPS="$AF_APPS"
|
||||||
|
else
|
||||||
|
SELECTED_APPS=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Applications " \
|
||||||
|
--checklist "Optional applications — installed after base components:" 40 76 32 \
|
||||||
|
"ollama" "Ollama local LLM runner + API server" off \
|
||||||
|
"llama-cpp" "llama.cpp standalone inference CLI + server" off \
|
||||||
|
"open-webui" "Open WebUI browser UI for Ollama / LLM backends" off \
|
||||||
|
"claude" "Claude Code Anthropic CLI via npm" off \
|
||||||
|
"networking-cli" "Networking CLI nmap · nethogs · mitmproxy · httpie" off \
|
||||||
|
"disk-recovery" "Disk Recovery ddrescue · f3" off \
|
||||||
|
"himalaya" "Himalaya terminal email client (AUR)" off \
|
||||||
|
"gnuplot" "Gnuplot scientific plotting" off \
|
||||||
|
"povray" "POV-Ray ray-tracing renderer" off \
|
||||||
|
"blender" "Blender 3D creation suite" off \
|
||||||
|
"toot" "toot Mastodon CLI client (AUR)" off \
|
||||||
|
"db-clients" "DB Clients pgcli · mycli" off \
|
||||||
|
"mysql" "MySQL / MariaDB mariadb server + setup" off \
|
||||||
|
"productivity" "Productivity taskwarrior · watson · jrnl" off \
|
||||||
|
"yt-dlp" "yt-dlp YouTube / media downloader" off \
|
||||||
|
"sox" "SoX audio processing toolkit" off \
|
||||||
|
"imagemagick" "ImageMagick image manipulation" off \
|
||||||
|
"ffmpeg" "FFmpeg extras thumbnailer · GStreamer codecs" off \
|
||||||
|
"localtunnel" "LocalTunnel expose localhost via tunnel" off \
|
||||||
|
"butter" "butter btrfs snapshot backup (AUR)" off \
|
||||||
|
"tlp" "TLP laptop power management" off \
|
||||||
|
"steam" "Steam gaming platform" off \
|
||||||
|
"vesktop" "Vesktop Discord + Vencord theme" off \
|
||||||
|
"spotify" "Spotify launcher + Spicetify theming" off \
|
||||||
|
"prism" "PrismLauncher Minecraft launcher (Flatpak)" off \
|
||||||
|
"vintagestory" "Vintage Story survival game (AUR)" off \
|
||||||
|
"localsend" "LocalSend LAN file transfer (AUR)" off \
|
||||||
|
"croc" "croc cross-platform file transfer" off \
|
||||||
|
"onlyoffice" "OnlyOffice office suite (AUR)" off \
|
||||||
|
"wireshark" "Wireshark network packet analyser (GUI)" off \
|
||||||
|
"k8s" "Kubernetes tools kubectl · podman-desktop" off \
|
||||||
|
"docker" "Docker docker · docker-compose" off \
|
||||||
|
"podman" "Podman rootless containers · buildah" off \
|
||||||
|
"cockpit" "Cockpit web UI · machines · podman" off \
|
||||||
|
"ssh-server" "SSH server openssh · key-auth · enabled" off \
|
||||||
|
"freeipa-client" "FreeIPA Client sssd + ipa-client-install + enrollment" off \
|
||||||
|
"freeipa-server" "FreeIPA Server interactive server setup + client gen" off \
|
||||||
|
"freeipa-image" "FreeIPA Image OCI/LXC/Proxmox LXC builder + Keycloak" off \
|
||||||
|
"python" "Python tools pyright · pipx · pynvim" off \
|
||||||
|
"zfs" "ZFS zfs-dkms kernel module" off \
|
||||||
|
"wprs" "WPRS wprs-git (AUR)" off \
|
||||||
|
\
|
||||||
|
"chromium" "Chromium open-source browser (official)" off \
|
||||||
|
"firefox-browser" "Firefox Mozilla browser (official)" off \
|
||||||
|
"zen-browser" "Zen Browser Firefox-based privacy browser (AUR)" off \
|
||||||
|
"nyxt" "Nyxt keyboard-driven browser (AUR)" off \
|
||||||
|
"librewolf" "LibreWolf hardened Firefox fork (AUR)" off \
|
||||||
|
"min-browser" "Min minimal Electron browser (AUR)" off \
|
||||||
|
\
|
||||||
|
"vscodium" "VSCodium telemetry-free VS Code (AUR)" off \
|
||||||
|
"zed-ide" "Zed high-performance Rust IDE (official)" off \
|
||||||
|
"geany" "Geany lightweight IDE + plugins (official)" off \
|
||||||
|
"codeblocks" "Code::Blocks C/C++ IDE (official)" off \
|
||||||
|
"kate" "Kate KDE advanced text editor (official)" off \
|
||||||
|
3>&1 1>&2 2>&3) || SELECTED_APPS=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Confirmation (interactive mode only) ──────────────────────────────────────
|
||||||
|
if ! $ANSWERFILE_MODE; then
|
||||||
|
SUMMARY=""
|
||||||
|
[[ -n "$HOSTNAME_SET" ]] && SUMMARY+=" ✦ Hostname: $HOSTNAME_SET\n"
|
||||||
|
[[ "$COMPONENTS" == *"pkg"* ]] && SUMMARY+=" ✦ Package managers (yay, nvm, rust)\n"
|
||||||
|
[[ "$COMPONENTS" == *"core"* ]] && SUMMARY+=" ✦ Core packages\n"
|
||||||
|
[[ "$COMPONENTS" == *"svc"* ]] && SUMMARY+=" ✦ Core services\n"
|
||||||
|
[[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+=" ✦ Shell setup\n"
|
||||||
|
[[ "$DE" != "none" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
|
||||||
|
|
||||||
|
if [[ -n "$SELECTED_APPS" ]]; then
|
||||||
|
SUMMARY+="\n Applications:\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"ollama"* ]] && SUMMARY+=" ✦ Ollama\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"llama-cpp"* ]] && SUMMARY+=" ✦ llama.cpp\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"open-webui"* ]] && SUMMARY+=" ✦ Open WebUI\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"claude"* ]] && SUMMARY+=" ✦ Claude Code\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"networking-cli"* ]] && SUMMARY+=" ✦ Networking CLI (nmap, nethogs, mitmproxy, httpie)\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"disk-recovery"* ]] && SUMMARY+=" ✦ Disk Recovery (ddrescue, f3)\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"himalaya"* ]] && SUMMARY+=" ✦ Himalaya\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"gnuplot"* ]] && SUMMARY+=" ✦ Gnuplot\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"povray"* ]] && SUMMARY+=" ✦ POV-Ray\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"blender"* ]] && SUMMARY+=" ✦ Blender\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"toot"* ]] && SUMMARY+=" ✦ toot\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"db-clients"* ]] && SUMMARY+=" ✦ DB Clients (pgcli, mycli)\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"mysql"* ]] && SUMMARY+=" ✦ MySQL / MariaDB\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"productivity"* ]] && SUMMARY+=" ✦ Productivity (taskwarrior, watson, jrnl)\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"yt-dlp"* ]] && SUMMARY+=" ✦ yt-dlp\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"sox"* ]] && SUMMARY+=" ✦ SoX\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"imagemagick"* ]] && SUMMARY+=" ✦ ImageMagick\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"ffmpeg"* ]] && SUMMARY+=" ✦ FFmpeg extras\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"localtunnel"* ]] && SUMMARY+=" ✦ LocalTunnel\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"butter"* ]] && SUMMARY+=" ✦ butter (btrfs backup)\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"tlp"* ]] && SUMMARY+=" ✦ TLP\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"steam"* ]] && SUMMARY+=" ✦ Steam\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"vesktop"* ]] && SUMMARY+=" ✦ Vesktop + Vencord theme\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"spotify"* ]] && SUMMARY+=" ✦ Spotify + Spicetify\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"prism"* ]] && SUMMARY+=" ✦ PrismLauncher\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"vintagestory"* ]] && SUMMARY+=" ✦ Vintage Story\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"localsend"* ]] && SUMMARY+=" ✦ LocalSend\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"croc"* ]] && SUMMARY+=" ✦ croc\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"onlyoffice"* ]] && SUMMARY+=" ✦ OnlyOffice\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"wireshark"* ]] && SUMMARY+=" ✦ Wireshark\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"k8s"* ]] && SUMMARY+=" ✦ Kubernetes tools\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"docker"* ]] && SUMMARY+=" ✦ Docker + Compose\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"podman"* ]] && SUMMARY+=" ✦ Podman (rootless) + Buildah\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"cockpit"* ]] && SUMMARY+=" ✦ Cockpit web UI\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"ssh-server"* ]] && SUMMARY+=" ✦ SSH server (openssh, key auth)\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"freeipa-client"* ]] && SUMMARY+=" ✦ FreeIPA Client\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"freeipa-server"* ]] && SUMMARY+=" ✦ FreeIPA Server\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"freeipa-image"* ]] && SUMMARY+=" ✦ FreeIPA Image Builder\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"python"* ]] && SUMMARY+=" ✦ Python tools\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"zfs"* ]] && SUMMARY+=" ✦ ZFS\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"wprs"* ]] && SUMMARY+=" ✦ WPRS\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"chromium"* ]] && SUMMARY+=" ✦ Chromium\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"firefox-browser"* ]] && SUMMARY+=" ✦ Firefox\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"zen-browser"* ]] && SUMMARY+=" ✦ Zen Browser\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"nyxt"* ]] && SUMMARY+=" ✦ Nyxt\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"librewolf"* ]] && SUMMARY+=" ✦ LibreWolf\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"min-browser"* ]] && SUMMARY+=" ✦ Min Browser\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"vscodium"* ]] && SUMMARY+=" ✦ VSCodium\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"zed-ide"* ]] && SUMMARY+=" ✦ Zed IDE\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"geany"* ]] && SUMMARY+=" ✦ Geany\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && SUMMARY+=" ✦ Code::Blocks\n"
|
||||||
|
[[ "$SELECTED_APPS" == *"kate"* ]] && SUMMARY+=" ✦ Kate\n"
|
||||||
|
fi
|
||||||
|
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Confirm Installation " \
|
||||||
|
--yesno "\n Components to install:\n\n${SUMMARY}\n Log: $LOG\n\n Proceed?" \
|
||||||
|
24 62 || { clear; echo "Aborted."; exit 0; }
|
||||||
|
fi
|
||||||
|
|
||||||
count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"
|
count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"
|
||||||
|
|
||||||
|
|
@ -410,10 +504,85 @@ fi
|
||||||
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && run_module "Code::Blocks" "$APPS/codeblocks.sh"
|
[[ "$SELECTED_APPS" == *"codeblocks"* ]] && run_module "Code::Blocks" "$APPS/codeblocks.sh"
|
||||||
[[ "$SELECTED_APPS" == *"kate"* ]] && run_module "Kate" "$APPS/kate.sh"
|
[[ "$SELECTED_APPS" == *"kate"* ]] && run_module "Kate" "$APPS/kate.sh"
|
||||||
|
|
||||||
# ── Done ──────────────────────────────────────────────────────────────────────
|
# ── Colorway (final step) ─────────────────────────────────────────────────────
|
||||||
dialog --backtitle "$BACKTITLE" \
|
# Read defaults from repo colors.conf for pre-population
|
||||||
--title " Done " \
|
declare -A _cdef
|
||||||
--msgbox "\n All selected components installed.\n\n Log: $LOG\n\n A reboot may be required for all changes to take effect.\n" 12 58
|
if [[ -f "$DOTFILES_DIR/colors.conf" ]]; then
|
||||||
|
while IFS='=' read -r k v; do
|
||||||
|
k="${k%%[[:space:]]*}"
|
||||||
|
[[ "$k" =~ ^[[:space:]]*# || -z "$k" ]] && continue
|
||||||
|
v="${v%%#*}"; v="${v//[[:space:]]/}"; v="${v^^}"
|
||||||
|
_cdef[$k]="$v"
|
||||||
|
done < "$DOTFILES_DIR/colors.conf"
|
||||||
|
fi
|
||||||
|
DEF_TEXT="${_cdef[COLOR_TEXT]:-D6ABAB}"
|
||||||
|
DEF_BG="${_cdef[COLOR_BG]:-1A1A1A}"
|
||||||
|
DEF_HIGHLIGHT="${_cdef[COLOR_HIGHLIGHT]:-E40046}"
|
||||||
|
DEF_DARK="${_cdef[COLOR_DARK]:-5018DD}"
|
||||||
|
DEF_RED="${_cdef[COLOR_RED]:-F50505}"
|
||||||
|
|
||||||
clear
|
_write_colors_conf() {
|
||||||
printf "\n Done. Log: %s\n\n" "$LOG"
|
local out="$1" t="$2" b="$3" h="$4" d="$5" r="$6"
|
||||||
|
printf 'COLOR_TEXT=%s\nCOLOR_BG=%s\nCOLOR_HIGHLIGHT=%s\nCOLOR_DARK=%s\nCOLOR_RED=%s\n' \
|
||||||
|
"${t^^}" "${b^^}" "${h^^}" "${d^^}" "${r^^}" > "$out"
|
||||||
|
}
|
||||||
|
|
||||||
|
if $ANSWERFILE_MODE; then
|
||||||
|
# Apply colors from answerfile if any are set
|
||||||
|
if [[ -n "$AF_COLOR_TEXT$AF_COLOR_BG$AF_COLOR_HIGHLIGHT$AF_COLOR_DARK$AF_COLOR_RED" ]]; then
|
||||||
|
TMP_COLORS="$TMP_D/colors.conf"
|
||||||
|
_write_colors_conf "$TMP_COLORS" \
|
||||||
|
"${AF_COLOR_TEXT:-$DEF_TEXT}" \
|
||||||
|
"${AF_COLOR_BG:-$DEF_BG}" \
|
||||||
|
"${AF_COLOR_HIGHLIGHT:-$DEF_HIGHLIGHT}" \
|
||||||
|
"${AF_COLOR_DARK:-$DEF_DARK}" \
|
||||||
|
"${AF_COLOR_RED:-$DEF_RED}"
|
||||||
|
printf "Applying colorway from answerfile...\n" | tee -a "$LOG"
|
||||||
|
bash "$DOTFILES_DIR/apply-theme.sh" "$TMP_COLORS" 2>&1 | tee -a "$LOG" || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Interactive: show color form dialog
|
||||||
|
COLORWAY_RAW=$(dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Colorway (optional) " \
|
||||||
|
--form "\n Customize theme colors — bare 6-digit hex, no #.\n Leave unchanged to skip colorway setup.\n" \
|
||||||
|
16 62 5 \
|
||||||
|
"COLOR_TEXT " 1 1 "$DEF_TEXT" 1 18 10 7 \
|
||||||
|
"COLOR_BG " 2 1 "$DEF_BG" 2 18 10 7 \
|
||||||
|
"COLOR_HIGHLIGHT " 3 1 "$DEF_HIGHLIGHT" 3 18 10 7 \
|
||||||
|
"COLOR_DARK " 4 1 "$DEF_DARK" 4 18 10 7 \
|
||||||
|
"COLOR_RED " 5 1 "$DEF_RED" 5 18 10 7 \
|
||||||
|
3>&1 1>&2 2>&3) || COLORWAY_RAW=""
|
||||||
|
|
||||||
|
if [[ -n "$COLORWAY_RAW" ]]; then
|
||||||
|
mapfile -t _cv <<< "$COLORWAY_RAW"
|
||||||
|
N_TEXT="${_cv[0]:-$DEF_TEXT}"
|
||||||
|
N_BG="${_cv[1]:-$DEF_BG}"
|
||||||
|
N_HIGHLIGHT="${_cv[2]:-$DEF_HIGHLIGHT}"
|
||||||
|
N_DARK="${_cv[3]:-$DEF_DARK}"
|
||||||
|
N_RED="${_cv[4]:-$DEF_RED}"
|
||||||
|
|
||||||
|
if [[ "${N_TEXT^^}" != "$DEF_TEXT" || \
|
||||||
|
"${N_BG^^}" != "$DEF_BG" || \
|
||||||
|
"${N_HIGHLIGHT^^}" != "$DEF_HIGHLIGHT" || \
|
||||||
|
"${N_DARK^^}" != "$DEF_DARK" || \
|
||||||
|
"${N_RED^^}" != "$DEF_RED" ]]; then
|
||||||
|
TMP_COLORS="$TMP_D/colors.conf"
|
||||||
|
_write_colors_conf "$TMP_COLORS" "$N_TEXT" "$N_BG" "$N_HIGHLIGHT" "$N_DARK" "$N_RED"
|
||||||
|
clear
|
||||||
|
printf "\n\033[1;35m Applying colorway...\033[0m\n\n"
|
||||||
|
bash "$DOTFILES_DIR/apply-theme.sh" "$TMP_COLORS" 2>&1 | tee -a "$LOG" || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Done ──────────────────────────────────────────────────────────────────────
|
||||||
|
if $ANSWERFILE_MODE; then
|
||||||
|
printf "\nDone. Log: %s\n" "$LOG"
|
||||||
|
else
|
||||||
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
--title " Done " \
|
||||||
|
--msgbox "\n All selected components installed.\n\n Log: $LOG\n\n A reboot may be required for all changes to take effect.\n" 12 58
|
||||||
|
|
||||||
|
clear
|
||||||
|
printf "\n Done. Log: %s\n\n" "$LOG"
|
||||||
|
fi
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue