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
parent
01c58905b2
commit
2ffedd076e
|
|
@ -12,7 +12,8 @@
|
||||||
# ║ user — must NOT run as root. ║
|
# ║ user — must NOT run as root. ║
|
||||||
# ║ ║
|
# ║ ║
|
||||||
# ║ WHAT GETS DEPLOYED: ║
|
# ║ 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/starship.toml — symlink to repo ║
|
||||||
# ║ ~/.config/nvim/ — symlink to repo/nvim/ ║
|
# ║ ~/.config/nvim/ — symlink to repo/nvim/ ║
|
||||||
# ║ ~/.config/micro/ — copy from repo/micro/ (plugin state needs cp)║
|
# ║ ~/.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
|
sudo chown -R "$(id -un):$(id -gn)" "$HOME/.config" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Shell init files — symlink so edits in the repo apply immediately
|
# Shell init files — symlink so edits in the repo apply immediately.
|
||||||
ln -sf ~/Dotfiles/.bashrc ~/.bashrc
|
# MARCHY_DEPLOY_SHELL_RC lets the caller install the shell tools/plugins below
|
||||||
ln -sf ~/Dotfiles/.zshrc ~/.zshrc
|
# (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/
|
# Starship prompt config — symlink into ~/.config/
|
||||||
ln -sf ~/Dotfiles/starship.toml ~/.config/starship.toml
|
ln -sf ~/Dotfiles/starship.toml ~/.config/starship.toml
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,6 @@ dialog --backtitle "$BACKTITLE" \
|
||||||
AF_COMPONENTS=""
|
AF_COMPONENTS=""
|
||||||
AF_DE="none"
|
AF_DE="none"
|
||||||
AF_APPS=""
|
AF_APPS=""
|
||||||
AF_SHELL_RC="dotfiles"
|
|
||||||
AF_COLOR_TEXT=""
|
AF_COLOR_TEXT=""
|
||||||
AF_COLOR_BG=""
|
AF_COLOR_BG=""
|
||||||
AF_COLOR_HIGHLIGHT=""
|
AF_COLOR_HIGHLIGHT=""
|
||||||
|
|
@ -324,14 +323,6 @@ if [[ "$AF_RUN_TUI" == "true" ]]; then
|
||||||
3>&1 1>&2 2>&3) || AF_APPS=""
|
3>&1 1>&2 2>&3) || AF_APPS=""
|
||||||
# END GENERATED MODULES: module-checklist
|
# 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 ──────────────────────────────────────────────────────────────
|
# ── Colorway ──────────────────────────────────────────────────────────────
|
||||||
# Read defaults from repo colors.conf
|
# Read defaults from repo colors.conf
|
||||||
declare -A _cdef
|
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+=" FIDO2 root: $AF_FIDO2_ROOT / FIDO2 user: $AF_FIDO2_USER\n"
|
||||||
SUMMARY+=" Run TUI: $AF_RUN_TUI\n"
|
SUMMARY+=" Run TUI: $AF_RUN_TUI\n"
|
||||||
[[ -n "$AF_DE" && "$AF_DE" != "none" ]] && SUMMARY+=" DE: $AF_DE\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"
|
[[ -n "$AF_COLOR_TEXT" ]] && SUMMARY+=" Colors: custom\n"
|
||||||
|
|
||||||
dialog --backtitle "$BACKTITLE" \
|
dialog --backtitle "$BACKTITLE" \
|
||||||
|
|
@ -435,8 +425,7 @@ mkdir -p "$(dirname "$OUTPUT")"
|
||||||
printf ' "run_tui": %s,\n' "$AF_RUN_TUI"
|
printf ' "run_tui": %s,\n' "$AF_RUN_TUI"
|
||||||
printf ' "components": %s,\n' "$(_words_to_json_array "$AF_COMPONENTS")"
|
printf ' "components": %s,\n' "$(_words_to_json_array "$AF_COMPONENTS")"
|
||||||
printf ' "desktop_environment": %s,\n' "$(json_str "$AF_DE")"
|
printf ' "desktop_environment": %s,\n' "$(json_str "$AF_DE")"
|
||||||
printf ' "apps": %s,\n' "$(_words_to_json_array "$AF_APPS")"
|
printf ' "apps": %s' "$(_words_to_json_array "$AF_APPS")"
|
||||||
printf ' "shell_rc": %s' "$(json_str "$AF_SHELL_RC")"
|
|
||||||
|
|
||||||
if [[ -n "$AF_COLOR_TEXT" ]]; then
|
if [[ -n "$AF_COLOR_TEXT" ]]; then
|
||||||
printf ',\n "colors": {\n'
|
printf ',\n "colors": {\n'
|
||||||
|
|
|
||||||
|
|
@ -257,6 +257,18 @@ log_sep() {
|
||||||
printf "\n══════════════════════════════════\n %s\n %s\n" "$1" "$(date)" >> "$LOG"
|
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() {
|
run_module() {
|
||||||
local label="$1" script="$2"
|
local label="$1" script="$2"
|
||||||
STEP=$(( STEP + 1 ))
|
STEP=$(( STEP + 1 ))
|
||||||
|
|
@ -307,13 +319,14 @@ count_steps() {
|
||||||
# run_module() can display accurate [N/TOTAL] progress from the first step.
|
# run_module() can display accurate [N/TOTAL] progress from the first step.
|
||||||
# Each glob test mirrors the corresponding run_module() call below; they
|
# Each glob test mirrors the corresponding run_module() call below; they
|
||||||
# must stay in sync whenever a new module is added or removed.
|
# 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
|
TOTAL=0
|
||||||
# Base components: each keyword maps to exactly one module script.
|
# Base components: each keyword maps to exactly one module script.
|
||||||
[[ "$c" == *"pkg"* ]] && TOTAL=$(( TOTAL + 1 ))
|
# Padded with spaces so "shell" doesn't false-match the "shellrc" tag.
|
||||||
[[ "$c" == *"core"* ]] && TOTAL=$(( TOTAL + 1 ))
|
[[ "$c" == *" pkg "* ]] && TOTAL=$(( TOTAL + 1 ))
|
||||||
[[ "$c" == *"svc"* ]] && TOTAL=$(( TOTAL + 1 ))
|
[[ "$c" == *" core "* ]] && TOTAL=$(( TOTAL + 1 ))
|
||||||
[[ "$c" == *"shell"* ]] && TOTAL=$(( TOTAL + 1 ))
|
[[ "$c" == *" svc "* ]] && TOTAL=$(( TOTAL + 1 ))
|
||||||
|
[[ "$c" == *" shell "* ]] && TOTAL=$(( TOTAL + 1 ))
|
||||||
# A non-"none" DE selection always installs exactly one DE module.
|
# A non-"none" DE selection always installs exactly one DE module.
|
||||||
[[ "$de" != "none" ]] && TOTAL=$(( TOTAL + 1 ))
|
[[ "$de" != "none" ]] && TOTAL=$(( TOTAL + 1 ))
|
||||||
# Optional app modules: one word-boundary check per app, one increment per match.
|
# Optional app modules: one word-boundary check per app, one increment per match.
|
||||||
|
|
@ -410,7 +423,6 @@ count_steps() {
|
||||||
AF_COMPONENTS=""
|
AF_COMPONENTS=""
|
||||||
AF_DE="none"
|
AF_DE="none"
|
||||||
AF_APPS=""
|
AF_APPS=""
|
||||||
AF_SHELL_RC="dotfiles"
|
|
||||||
AF_COLOR_TEXT=""
|
AF_COLOR_TEXT=""
|
||||||
AF_COLOR_BG=""
|
AF_COLOR_BG=""
|
||||||
AF_COLOR_HIGHLIGHT=""
|
AF_COLOR_HIGHLIGHT=""
|
||||||
|
|
@ -427,7 +439,6 @@ load_answerfile() {
|
||||||
AF_COMPONENTS=$(jq -r '(.components // []) | join(" ")' "$ANSWERFILE")
|
AF_COMPONENTS=$(jq -r '(.components // []) | join(" ")' "$ANSWERFILE")
|
||||||
AF_DE=$(jq -r '.desktop_environment // "none"' "$ANSWERFILE")
|
AF_DE=$(jq -r '.desktop_environment // "none"' "$ANSWERFILE")
|
||||||
AF_APPS=$(jq -r '(.apps // []) | join(" ")' "$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".
|
# Color values are optional; an empty string means "keep the repo default".
|
||||||
AF_COLOR_TEXT=$(jq -r '.colors.COLOR_TEXT // ""' "$ANSWERFILE")
|
AF_COLOR_TEXT=$(jq -r '.colors.COLOR_TEXT // ""' "$ANSWERFILE")
|
||||||
AF_COLOR_BG=$(jq -r '.colors.COLOR_BG // ""' "$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 ───────────────────────────────────────────────────────────────────
|
# ── Welcome ───────────────────────────────────────────────────────────────────
|
||||||
if ! $ANSWERFILE_MODE; then
|
if ! $ANSWERFILE_MODE; then
|
||||||
|
flush_tty_input
|
||||||
ui_msgbox " Welcome " "\
|
ui_msgbox " Welcome " "\
|
||||||
the_miro's Arch dotfiles installer\n\
|
the_miro's Arch dotfiles installer\n\
|
||||||
Cyberqueer · Wayland · Hyprland\n\
|
Cyberqueer · Wayland · Hyprland\n\
|
||||||
|
|
@ -503,13 +515,17 @@ if $ANSWERFILE_MODE; then
|
||||||
COMPONENTS="$AF_COMPONENTS"
|
COMPONENTS="$AF_COMPONENTS"
|
||||||
else
|
else
|
||||||
# ui_checklist takes tag/desc/state triplets and echoes the selected tags.
|
# 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.
|
# 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:" \
|
COMPONENTS=$(ui_checklist " Select Components " "Optional base components — all enabled by default:" \
|
||||||
"pkg" "pkg Package managers yay · nvm · rust" on \
|
"pkg" "pkg Package managers yay · nvm · rust" on \
|
||||||
"core" "core Core packages 100+ base system packages" on \
|
"core" "core Core packages 100+ base system packages" on \
|
||||||
"svc" "svc Core services NetworkManager · cronie · fail2ban" 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; }
|
|| { clear; echo "Aborted."; exit 0; }
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -624,13 +640,13 @@ else
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Shell RC preference ───────────────────────────────────────────────────────
|
# ── Shell RC preference ───────────────────────────────────────────────────────
|
||||||
if $ANSWERFILE_MODE; then
|
# Derived from the "shellrc" component rather than asked separately: it only takes
|
||||||
SHELL_RC="$AF_SHELL_RC"
|
# 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
|
else
|
||||||
SHELL_RC=$(ui_menu " Shell Config for New Users " \
|
SHELL_RC="defaults"
|
||||||
"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"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Confirmation (interactive mode only) ──────────────────────────────────────
|
# ── 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
|
# 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.
|
# user can review the full list before any changes are made to the system.
|
||||||
SUMMARY=""
|
SUMMARY=""
|
||||||
[[ "$COMPONENTS" == *"pkg"* ]] && SUMMARY+=" ✦ Package managers (yay, nvm, rust)\n"
|
[[ " $COMPONENTS " == *" pkg "* ]] && SUMMARY+=" ✦ Package managers (yay, nvm, rust)\n"
|
||||||
[[ "$COMPONENTS" == *"core"* ]] && SUMMARY+=" ✦ Core packages\n"
|
[[ " $COMPONENTS " == *" core "* ]] && SUMMARY+=" ✦ Core packages\n"
|
||||||
[[ "$COMPONENTS" == *"svc"* ]] && SUMMARY+=" ✦ Core services\n"
|
[[ " $COMPONENTS " == *" svc "* ]] && SUMMARY+=" ✦ Core services\n"
|
||||||
[[ "$COMPONENTS" == *"shell"* ]] && SUMMARY+=" ✦ Shell setup\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"
|
[[ "$DE" != "none" && "$DE" != "" ]] && SUMMARY+=" ✦ Desktop environment: $DE\n"
|
||||||
[[ "$SHELL_RC" == "dotfiles" ]] && SUMMARY+=" ✦ Shell rc files → /etc/skel (dotfiles)\n" \
|
[[ "$SHELL_RC" == "dotfiles" ]] && SUMMARY+=" ✦ Shell rc's → /etc/skel for new users (dotfiles)\n" \
|
||||||
|| SUMMARY+=" ✦ Shell rc files → /etc/skel (system defaults)\n"
|
|| SUMMARY+=" ✦ Shell rc's → /etc/skel for new users (system defaults)\n"
|
||||||
|
|
||||||
if [[ -n "$SELECTED_APPS" ]]; then
|
if [[ -n "$SELECTED_APPS" ]]; then
|
||||||
SUMMARY+="\n Applications:\n"
|
SUMMARY+="\n Applications:\n"
|
||||||
|
|
@ -741,12 +758,16 @@ fi
|
||||||
count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"
|
count_steps "$COMPONENTS" "$DE" "$SELECTED_APPS"
|
||||||
|
|
||||||
# ── Installation: base components ─────────────────────────────────────────────
|
# ── 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.
|
# Order matters: package managers must be installed before packages that need yay/rust.
|
||||||
[[ "$COMPONENTS" == *"pkg"* ]] && run_module "Package Managers" "$MODULES/package-managers.sh"
|
[[ " $COMPONENTS " == *" pkg "* ]] && run_module "Package Managers" "$MODULES/package-managers.sh"
|
||||||
[[ "$COMPONENTS" == *"core"* ]] && run_module "Core Packages" "$MODULES/core-packages.sh"
|
[[ " $COMPONENTS " == *" core "* ]] && run_module "Core Packages" "$MODULES/core-packages.sh"
|
||||||
[[ "$COMPONENTS" == *"svc"* ]] && run_module "Core Services" "$MODULES/core.sh"
|
[[ " $COMPONENTS " == *" svc "* ]] && run_module "Core Services" "$MODULES/core.sh"
|
||||||
[[ "$COMPONENTS" == *"shell"* ]] && run_module "Shell Setup" "$MODULES/shell-setup.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.
|
# Route the single selected DE value to its corresponding install script.
|
||||||
# "none" is the skip sentinel — no case branch matches it intentionally.
|
# "none" is the skip sentinel — no case branch matches it intentionally.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue