From eb3c509523c4c19eef27b49f620c07fbb35b46f2 Mon Sep 17 00:00:00 2001 From: Rick Peters Date: Sat, 3 Oct 2026 11:52:06 +0200 Subject: [PATCH] refactor: entry point picks its mode once; NO_COLOR Co-Authored-By: Claude Sonnet 5.5 --- CHANGELOG.md | 1 + lib/common.sh | 7 +++- steamify.sh | 106 ++++++++++++++++++++++++++------------------------ 3 files changed, 62 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9491575..5dee485 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ one per merged pull request. - **refactor: Valve's package mirror in one module (`lib/valve-mirror.sh`: newest repo, package database fields, checksummed download); the SteamOS extras, CEC driver, BIOS lookup and os-release version used four copies** - **refactor: the rules between menu items are three tables (`REQUIRES`, `FOLLOWS`, `EXCLUDES` in `lib/menu.sh`) used by the menu's toggles and, as `normalize_wanted`, by the app's selection (it had its own copy of the rules). The app now also ticks the Steamify shortcut for "Add as non-Steam game" and unticks the game entry with it, like the menu** +- **refactor: the entry point picks its mode once (`MODE`) and `--defaults` and `--boot` are functions with one failure report, instead of four booleans and two copies of the apply-and-report code; `NO_COLOR` gives plain output** - **test: the menu rules, the plan and the app's and installer's JSON are checked against a golden file (`tests/menu-test.sh` in steamify-cachyos-dev); `make check`, `.editorconfig`, `.shellcheckrc`** - **docs: README lists the sources and projects Steamify builds on, with licences and thanks** diff --git a/lib/common.sh b/lib/common.sh index 648d18d..ac12b53 100644 --- a/lib/common.sh +++ b/lib/common.sh @@ -2,7 +2,12 @@ # Output helpers, prompts and small shared utilities. # Sourced by steamify.sh; not meant to be run on its own. -c_reset="\033[0m"; c_bold="\033[1m"; c_green="\033[32m"; c_yellow="\033[33m"; c_red="\033[31m"; c_cyan="\033[36m"; c_dim="\033[2m" +# NO_COLOR (https://no-color.org): plain output, e.g. for a log file. +if [[ -n "${NO_COLOR:-}" ]]; then + c_reset=""; c_bold=""; c_green=""; c_yellow=""; c_red=""; c_cyan=""; c_dim="" +else + c_reset="\033[0m"; c_bold="\033[1m"; c_green="\033[32m"; c_yellow="\033[33m"; c_red="\033[31m"; c_cyan="\033[36m"; c_dim="\033[2m" +fi info() { echo -e "${c_cyan}[INFO]${c_reset} $*"; } ok() { echo -e "${c_green}[OK]${c_reset} $*"; } diff --git a/steamify.sh b/steamify.sh index bcebe23..1ba7d3a 100755 --- a/steamify.sh +++ b/steamify.sh @@ -26,23 +26,28 @@ done require_root_helper -BACKEND=false -[[ "${1:-}" == --backend ]] && BACKEND=true -# --defaults [--options ,...] [--boot gamescope|desktop]: apply what the -# menu would preselect (or exactly the listed items), without the menu or any -# prompt (the Steam Machine ISO's first login runs this; sudo must not ask). -DEFAULTS=false -[[ "${1:-}" == --defaults ]] && DEFAULTS=true -# --defaults --list: what --defaults can set up here, as JSON (an installer -# page is built from it); nothing else on stdout. -LIST=false -[[ "$DEFAULTS" == true && "${2:-}" == --list ]] && LIST=true -# --boot gamescope|desktop: only change where an installed conversion -# starts, everything else stays as it is (for scripts; no menu). -BOOT_ONLY=false -[[ "${1:-}" == --boot ]] && BOOT_ONLY=true +# What this run does, from the first argument: +# --backend ... the graphical app's machine-readable mode +# --defaults [--options ,...] [--boot gamescope|desktop]: apply what the +# menu would preselect (or exactly the listed items), without the menu or +# any prompt (the Steam Machine ISO's first login runs this; sudo must not +# ask) +# --defaults --list what --defaults can set up here, as JSON +# (an installer page is built from it); nothing else on stdout +# --boot gamescope|desktop only change where an installed conversion +# starts, everything else stays as it is (for scripts; no menu) +# --first-login the installer's one-time follow-up +# (none) the menu +MODE=menu +case "${1:-}" in + --backend) MODE=backend ;; + --defaults) MODE=defaults; [[ "${2:-}" == --list ]] && MODE=list ;; + --boot) MODE=boot ;; + --first-login) MODE=first-login ;; +esac +BACKEND=false; [[ "$MODE" == backend ]] && BACKEND=true -if [[ "$BACKEND" == false && "$LIST" == false ]]; then +if [[ "$MODE" != backend && "$MODE" != list ]]; then echo -e "${c_bold}Steamify CachyOS${c_reset} v$VERSION" echo "Turn the SteamOS-style parts on or off. The menu shows what is on now;" echo "anything you turn off is put back the way it was." @@ -118,63 +123,62 @@ quit_prompt() { return 0 } -# The graphical app (steamify-ui) drives the same components through -# lib/backend.sh instead of the menu. -if [[ "$BACKEND" == true ]]; then - RESTART_FOR_LOGIN=false - shift - backend_main "$@" - exit $? -fi +report_failed() { + # Exit status for the scripted modes: 1 when a component had problems. + [[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; return 1; } +} -if [[ "${1:-}" == --first-login ]]; then - first_login_run - exit 0 -fi - -if [[ "$LIST" == true ]]; then - defaults_list - exit 0 -fi - -if [[ "$DEFAULTS" == true ]]; then +run_defaults() { RESTART_FOR_LOGIN=false REAPPLY=false - sudo -n true 2>/dev/null || { err "--defaults needs sudo without a password."; exit 1; } + sudo -n true 2>/dev/null || { err "--defaults needs sudo without a password."; return 1; } detect_components - defaults_options "${@:2}" || exit 1 + defaults_options "${@:2}" || return 1 plan_changes apply_changes # Run by the installer: the rest waits for the first desktop login. user_session || first_login_schedule - [[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; exit 1; } + report_failed || return 1 ok "Done; the changes take effect after a restart." - exit 0 -fi +} -if [[ "$BOOT_ONLY" == true ]]; then +run_boot_only() { + local mode="${2:-}" c RESTART_FOR_LOGIN=false REAPPLY=false - case "${2:-}" in + case "$mode" in gamescope|desktop) ;; - *) err "--boot takes gamescope or desktop"; exit 1 ;; + *) err "--boot takes gamescope or desktop"; return 1 ;; esac detect_components - [[ "${CURRENT[gaming]}" == 1 ]] || { err "--boot needs the SteamOS conversion; turn it on first."; exit 1; } + [[ "${CURRENT[gaming]}" == 1 ]] || { err "--boot needs the SteamOS conversion; turn it on first."; return 1; } # What is on stays on, and nothing else changes: not the updates or # removals a normal run would pick. for c in "${COMPONENTS[@]}"; do WANTED[$c]=${CURRENT[$c]:-0}; done - WANTED[boot]=0; [[ "$2" == desktop ]] && WANTED[boot]=1 + WANTED[boot]=0; [[ "$mode" == desktop ]] && WANTED[boot]=1 plan_changes if [[ ${#TO_DISABLE[@]} -eq 0 && ${#TO_ENABLE[@]} -eq 0 ]]; then - ok "Already starting in $2." - exit 0 + ok "Already starting in $mode." + return 0 fi apply_changes - [[ ${#FAILED[@]} -eq 0 ]] || { warn "These had problems (see above): ${FAILED[*]}"; exit 1; } - ok "Starts in $2 from the next boot on." - exit 0 -fi + report_failed || return 1 + ok "Starts in $mode from the next boot on." +} + +case "$MODE" in + backend) + # The graphical app (steamify-ui) drives the same components through + # lib/backend.sh instead of the menu. + RESTART_FOR_LOGIN=false + shift + backend_main "$@" + exit $? ;; + first-login) first_login_run; exit 0 ;; + list) defaults_list; exit 0 ;; + defaults) run_defaults "$@"; exit $? ;; + boot) run_boot_only "$@"; exit $? ;; +esac # Menu loop: after each run the menu comes back with the new state, until # the user quits; the restart question comes then, once, for everything.