fix(installer): flush stray tty input, split shell rc's from shell setup

The first checklist (Select Components) could silently auto-confirm its
defaults, swallowing the user's toggle: tui-install.sh runs right after the
long, mostly-unattended base install, and keystrokes buffered during that
wait were consumed by the script's very first read. Drain the tty before the
first prompt.

Also split the "shell" component into "shell" (tools/plugins/programs) and
a new "shellrc" (personal .bashrc/.zshrc/.vimrc), so the shell environment
can be installed without overwriting the user's rc files. shell-setup.sh
reads MARCHY_DEPLOY_SHELL_RC to gate the rc symlinks and now also deploys
.vimrc, which /etc/skel sync already expected but nothing ever created.
Removes the separate, easily-desynced "Shell Config for New Users" prompt
in favor of deriving /etc/skel behavior from the same shellrc choice, and
mirrors the removal in generate-answerfile.sh.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
feat/astal-menu
Amir Alexander Abdelbaki 2026-07-03 14:22:03 +02:00
parent 01c58905b2
commit 2ffedd076e
3 changed files with 62 additions and 42 deletions

View File

@ -12,7 +12,8 @@
# ║ user — must NOT run as root. ║
# ║ ║
# ║ WHAT GETS DEPLOYED: ║
# ║ ~/.bashrc, ~/.zshrc — symlinks to Dotfiles repo ║
# ║ ~/.bashrc, ~/.zshrc, ~/.vimrc — symlinks to Dotfiles repo, unless ║
# ║ MARCHY_DEPLOY_SHELL_RC=0 (defaults to 1 outside the TUI installer) ║
# ║ ~/.config/starship.toml — symlink to repo ║
# ║ ~/.config/nvim/ — symlink to repo/nvim/ ║
# ║ ~/.config/micro/ — copy from repo/micro/ (plugin state needs cp)║
@ -160,9 +161,18 @@ if [[ -e "$HOME/.config" && ! -w "$HOME/.config" ]]; then
sudo chown -R "$(id -un):$(id -gn)" "$HOME/.config" 2>/dev/null || true
fi
# Shell init files — symlink so edits in the repo apply immediately
ln -sf ~/Dotfiles/.bashrc ~/.bashrc
ln -sf ~/Dotfiles/.zshrc ~/.zshrc
# Shell init files — symlink so edits in the repo apply immediately.
# MARCHY_DEPLOY_SHELL_RC lets the caller install the shell tools/plugins below
# (zsh, oh-my-zsh, neovim, yazi, micro, starship, …) without overwriting the
# user's existing .bashrc/.zshrc/.vimrc with the_miro's personal versions.
# Defaults to deploying them when run standalone (outside the TUI installer).
if [[ "${MARCHY_DEPLOY_SHELL_RC:-1}" == "1" ]]; then
ln -sf ~/Dotfiles/.bashrc ~/.bashrc
ln -sf ~/Dotfiles/.zshrc ~/.zshrc
ln -sf ~/Dotfiles/.vimrc ~/.vimrc
else
skip "Personal shell rc's not selected — leaving .bashrc/.zshrc/.vimrc as-is."
fi
# Starship prompt config — symlink into ~/.config/
ln -sf ~/Dotfiles/starship.toml ~/.config/starship.toml

View File

@ -201,7 +201,6 @@ dialog --backtitle "$BACKTITLE" \
AF_COMPONENTS=""
AF_DE="none"
AF_APPS=""
AF_SHELL_RC="dotfiles"
AF_COLOR_TEXT=""
AF_COLOR_BG=""
AF_COLOR_HIGHLIGHT=""
@ -324,14 +323,6 @@ if [[ "$AF_RUN_TUI" == "true" ]]; then
3>&1 1>&2 2>&3) || AF_APPS=""
# END GENERATED MODULES: module-checklist
# ── Shell RC preference ───────────────────────────────────────────────────
AF_SHELL_RC=$(dialog --backtitle "$BACKTITLE" \
--title " Shell Config for New Users " \
--menu "\n Should new users on this machine inherit the dotfiles' rc files?\n (Controls what gets copied to /etc/skel)\n" 13 68 2 \
"dotfiles" "Use the_miro's .zshrc / .bashrc / .vimrc from dotfiles" \
"defaults" "Skip — use system defaults for new users" \
3>&1 1>&2 2>&3) || AF_SHELL_RC="defaults"
# ── Colorway ──────────────────────────────────────────────────────────────
# Read defaults from repo colors.conf
declare -A _cdef
@ -393,7 +384,6 @@ 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"
SUMMARY+=" Shell rc: $AF_SHELL_RC\n"
[[ -n "$AF_COLOR_TEXT" ]] && SUMMARY+=" Colors: custom\n"
dialog --backtitle "$BACKTITLE" \
@ -435,8 +425,7 @@ mkdir -p "$(dirname "$OUTPUT")"
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,\n' "$(_words_to_json_array "$AF_APPS")"
printf ' "shell_rc": %s' "$(json_str "$AF_SHELL_RC")"
printf ' "apps": %s' "$(_words_to_json_array "$AF_APPS")"
if [[ -n "$AF_COLOR_TEXT" ]]; then
printf ',\n "colors": {\n'

View File

@ -257,6 +257,18 @@ log_sep() {
printf "\n══════════════════════════════════\n %s\n %s\n" "$1" "$(date)" >> "$LOG"
}
flush_tty_input() {
# Discard any keystrokes queued on the terminal before the first prompt.
# This script normally runs right after the long, mostly-unattended base
# install (partitioning, pacstrap, …); impatient Enter presses or other
# stray input typed during that wait sit in the tty's input buffer and
# would otherwise be consumed by the very first `read` here — making the
# first checklist appear to auto-confirm its defaults before the user can
# toggle anything. A short non-blocking drain loop clears the queue.
local junk
while read -r -t 0.05 junk </dev/tty 2>/dev/null; do :; done
}
run_module() {
local label="$1" script="$2"
STEP=$(( STEP + 1 ))
@ -307,13 +319,14 @@ count_steps() {
# run_module() can display accurate [N/TOTAL] progress from the first step.
# Each glob test mirrors the corresponding run_module() call below; they
# must stay in sync whenever a new module is added or removed.
local c="$1" de="$2" a="${3:-}"
local c=" $1 " de="$2" a="${3:-}"
TOTAL=0
# Base components: each keyword maps to exactly one module script.
[[ "$c" == *"pkg"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$c" == *"core"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$c" == *"svc"* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$c" == *"shell"* ]] && TOTAL=$(( TOTAL + 1 ))
# Padded with spaces so "shell" doesn't false-match the "shellrc" tag.
[[ "$c" == *" pkg "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$c" == *" core "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$c" == *" svc "* ]] && TOTAL=$(( TOTAL + 1 ))
[[ "$c" == *" shell "* ]] && TOTAL=$(( TOTAL + 1 ))
# A non-"none" DE selection always installs exactly one DE module.
[[ "$de" != "none" ]] && TOTAL=$(( TOTAL + 1 ))
# Optional app modules: one word-boundary check per app, one increment per match.
@ -410,7 +423,6 @@ count_steps() {
AF_COMPONENTS=""
AF_DE="none"
AF_APPS=""
AF_SHELL_RC="dotfiles"
AF_COLOR_TEXT=""
AF_COLOR_BG=""
AF_COLOR_HIGHLIGHT=""
@ -427,7 +439,6 @@ load_answerfile() {
AF_COMPONENTS=$(jq -r '(.components // []) | join(" ")' "$ANSWERFILE")
AF_DE=$(jq -r '.desktop_environment // "none"' "$ANSWERFILE")
AF_APPS=$(jq -r '(.apps // []) | join(" ")' "$ANSWERFILE")
AF_SHELL_RC=$(jq -r '.shell_rc // "dotfiles"' "$ANSWERFILE")
# Color values are optional; an empty string means "keep the repo default".
AF_COLOR_TEXT=$(jq -r '.colors.COLOR_TEXT // ""' "$ANSWERFILE")
AF_COLOR_BG=$(jq -r '.colors.COLOR_BG // ""' "$ANSWERFILE")
@ -484,6 +495,7 @@ printf "Dotfiles install: %s\nDotfiles dir: %s\n" "$(date)" "$DOTFILES_DIR" >
# ── Welcome ───────────────────────────────────────────────────────────────────
if ! $ANSWERFILE_MODE; then
flush_tty_input
ui_msgbox " Welcome " "\
the_miro's Arch dotfiles installer\n\
Cyberqueer · Wayland · Hyprland\n\
@ -503,13 +515,17 @@ if $ANSWERFILE_MODE; then
COMPONENTS="$AF_COMPONENTS"
else
# ui_checklist takes tag/desc/state triplets and echoes the selected tags.
# All four components are pre-selected ("on") because they form the expected base.
# All five components are pre-selected ("on") because they form the expected base.
# "shellrc" is split out from "shell" so the shell tools/plugins/programs can be
# installed without symlinking the personal .bashrc/.zshrc/.vimrc over them —
# it only has an effect when "shell" is also selected.
# Entering 'q' returns exit code 1; the '|| { ...; exit 0; }' treats that as a clean abort.
COMPONENTS=$(ui_checklist " Select Components " "Optional base components — all enabled by default:" \
"pkg" "pkg Package managers yay · nvm · rust" on \
"core" "core Core packages 100+ base system packages" on \
"svc" "svc Core services NetworkManager · cronie · fail2ban" on \
"shell" "shell Shell setup zsh · nvim · yazi · micro · starship" on) \
"shell" "shell Shell setup zsh · nvim · yazi · micro · starship" on \
"shellrc" "shellrc Personal shell rc's .bashrc/.zshrc/.vimrc (needs shell)" on) \
|| { clear; echo "Aborted."; exit 0; }
fi
@ -624,13 +640,13 @@ else
fi
# ── Shell RC preference ───────────────────────────────────────────────────────
if $ANSWERFILE_MODE; then
SHELL_RC="$AF_SHELL_RC"
# Derived from the "shellrc" component rather than asked separately: it only takes
# effect when "shell" also runs, and whether new users (via /etc/skel) inherit the
# rc files should follow the same choice as the current user's deployment.
if [[ " $COMPONENTS " == *" shell "* && " $COMPONENTS " == *" shellrc "* ]]; then
SHELL_RC="dotfiles"
else
SHELL_RC=$(ui_menu " Shell Config for New Users " \
"Should new users on this machine inherit the dotfiles' rc files?\n (Controls what gets copied to /etc/skel)" \
"dotfiles" "Use the_miro's .zshrc / .bashrc / .vimrc from dotfiles" \
"defaults" "Skip — use system defaults for new users") || SHELL_RC="defaults"
SHELL_RC="defaults"
fi
# ── Confirmation (interactive mode only) ──────────────────────────────────────
@ -638,13 +654,14 @@ if ! $ANSWERFILE_MODE; then
# Build a human-readable summary of everything that will be installed so the
# user can review the full list before any changes are made to the system.
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"
[[ " $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 (plugins & programs)\n"
[[ "$SHELL_RC" == "dotfiles" ]] && SUMMARY+=" ✦ Personal shell rc's (.bashrc/.zshrc/.vimrc)\n"
[[ "$DE" != "none" && "$DE" != "" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
[[ "$SHELL_RC" == "dotfiles" ]] && SUMMARY+=" ✦ Shell rc files → /etc/skel (dotfiles)\n" \
|| SUMMARY+=" ✦ Shell rc files → /etc/skel (system defaults)\n"
[[ "$SHELL_RC" == "dotfiles" ]] && SUMMARY+=" ✦ Shell rc's → /etc/skel for new users (dotfiles)\n" \
|| SUMMARY+=" ✦ Shell rc's → /etc/skel for new users (system defaults)\n"
if [[ -n "$SELECTED_APPS" ]]; then
SUMMARY+="\n Applications:\n"
@ -741,12 +758,16 @@ fi
count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"
# ── Installation: base components ─────────────────────────────────────────────
# Each guard uses glob matching against the space-separated COMPONENTS string.
# Each guard uses padded-space glob matching against the COMPONENTS string so
# "shell" doesn't false-match the "shellrc" tag.
# Order matters: package managers must be installed before packages that need yay/rust.
[[ "$COMPONENTS" == *"pkg"* ]] && run_module "Package Managers" "$MODULES/package-managers.sh"
[[ "$COMPONENTS" == *"core"* ]] && run_module "Core Packages" "$MODULES/core-packages.sh"
[[ "$COMPONENTS" == *"svc"* ]] && run_module "Core Services" "$MODULES/core.sh"
[[ "$COMPONENTS" == *"shell"* ]] && run_module "Shell Setup" "$MODULES/shell-setup.sh"
[[ " $COMPONENTS " == *" pkg "* ]] && run_module "Package Managers" "$MODULES/package-managers.sh"
[[ " $COMPONENTS " == *" core "* ]] && run_module "Core Packages" "$MODULES/core-packages.sh"
[[ " $COMPONENTS " == *" svc "* ]] && run_module "Core Services" "$MODULES/core.sh"
# MARCHY_DEPLOY_SHELL_RC tells shell-setup.sh whether to symlink the personal
# .bashrc/.zshrc/.vimrc, independently of installing the shell tools/plugins themselves.
[[ " $COMPONENTS " == *" shellrc "* ]] && export MARCHY_DEPLOY_SHELL_RC=1 || export MARCHY_DEPLOY_SHELL_RC=0
[[ " $COMPONENTS " == *" shell "* ]] && run_module "Shell Setup" "$MODULES/shell-setup.sh"
# Route the single selected DE value to its corresponding install script.
# "none" is the skip sentinel — no case branch matches it intentionally.