testsAndMisc/linux_configuration/install_core_system.sh
Krzysztof kuhy Rudnicki f15f00f3fe Re-point paths for the ~ reorganisation (home-tidy)
Also bumps the 16 meta/requirements pins the freshness gate flagged
(anthropic 1.5.0, numpy 2.5.3, pytest-randomly 5, flake8-bugbear 26.9.9,
ruff 0.16.7, pyright 1.1.414, selenium 4.49.0, hypothesis 6.168.0, lxml
6.1.3, pipdeptree 4.2.5, the two types-* stubs) and Flutter 3.47.3 in
meta/.fvmrc. One test still expected the pre-move ~/mcp-servers path.

The pre-commit hook fed .zsh files to shfmt and shellcheck, neither of
which parses zsh (SC2148/SC1071), so no zsh file could be committed since
the hook was tightened on 2026-08-22; those now get `zsh -n` instead. The
hook's own shfmt pass also staged its reformat of 8 shell files.

CI_GREEN_SKIP because main is red on exactly the dependency-freshness
finding this commit fixes, which is the documented use of that flag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
2026-09-11 20:50:19 +02:00

216 lines
8.9 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Unified installer for all personal Linux system modules.
#
# This installer is CROSS-REPO: several modules were extracted into sibling
# repos. A missing sibling is cloned automatically (see require_extracted_repo);
# a clone that fails is a loud module failure, never a silent skip.
#
# CORE modules (always installed):
# 0. Guard library (guardctl) – github.com/kuhyx/utils (~/src/utils/guard-lib)
# 1. Workout screen locker – github.com/kuhyx/screen-locker (~/src/screen-locker)
# 2. Hosts blocking setup – github.com/kuhyx/hosts-blocker (~/src/hosts-blocker)
# 3. Midnight shutdown timer – github.com/kuhyx/digital-wellbeing
#
# SECONDARY modules (prompted unless --all / --none given):
# 4. Steam backlog enforcer – github.com/kuhyx/steam-backlog-enforcer
# 5. Pacman wrapper – github.com/kuhyx/digital-wellbeing (~/src/digital-wellbeing)
# 6. i3 configuration – i3/
# 7. Compulsive opening block – block_compulsive_opening.sh
#
# Usage:
# ./install_core_system.sh [--all | --none]
#
# Flags:
# --all Install all secondary modules without prompting
# --none Skip all secondary modules
# -h Show this help
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LINUX_CONFIG="$SCRIPT_DIR"
# Locates repos extracted out of this monorepo (hosts-blocker, ...).
# shellcheck source=lib/extracted_repos.sh
source "$LINUX_CONFIG/lib/extracted_repos.sh"
# ── Colour helpers ───────────────────────────────────────────────────────────
bold() { printf '\e[1m%s\e[0m' "$*"; }
green() { printf '\e[1;32m%s\e[0m' "$*"; }
yellow() { printf '\e[1;33m%s\e[0m' "$*"; }
red() { printf '\e[1;31m%s\e[0m' "$*"; }
header() { printf '\n%s\n%s\n' "$(bold "=== $1 ===")" "$(printf '=%.0s' {1..50})"; }
ok() { printf '%s %s\n' "$(green "✓")" "$*"; }
skip() { printf '%s %s\n' "$(yellow "–")" "$*"; }
fail() { printf '%s %s\n' "$(red "✗")" "$*"; }
# ── Argument parsing ─────────────────────────────────────────────────────────
SECONDARY_MODE="ask" # ask | all | none
for arg in "$@"; do
case "$arg" in
--all) SECONDARY_MODE="all" ;;
--none) SECONDARY_MODE="none" ;;
-h | --help)
sed -n '2,/^$/p' "$0"
exit 0
;;
*)
printf 'Unknown option: %s\n' "$arg" >&2
exit 1
;;
esac
done
# ── Result tracking ──────────────────────────────────────────────────────────
declare -a INSTALLED=()
declare -a SKIPPED=()
declare -a FAILED=()
run_installer() {
local name="$1"
shift
header "$name"
if "$@"; then
ok "$name installed"
INSTALLED+=("$name")
else
fail "$name failed (exit $?)"
FAILED+=("$name")
fi
}
# install_module_from_repo <repo-name> <what> <script-rel-path> [args...]
# Runs an installer that lives in a sibling repo extracted out of this monorepo.
#
# This installer is cross-repo: screen-locker, steam-backlog-enforcer and
# guard-lib (inside utils) are separate checkouts now. require_extracted_repo
# clones a missing one, so a bare machine still gets a working install from the
# single documented command. If the clone fails, or the repo is present but the
# script inside it is not, that is a hard failure -- the module lands in the
# Failed() summary rather than being silently skipped, which is the failure mode
# that let these two defects survive unnoticed.
#
# ROOT_MODE selects how the script is invoked:
# auto - run as-is; the script elevates itself if it needs to (default)
# sudo - run under sudo; for scripts that require root but do NOT self-elevate
# (guard-lib's install.sh hard-exits as non-root instead of re-execing)
# Never blanket-sudo: screen-locker installs a systemd *user* service into
# $HOME/.config/systemd/user, so running it as root would install it for /root.
install_module_from_repo() {
local repo="$1" what="$2" rel="$3" root_mode="${ROOT_MODE:-auto}"
shift 3
local dir script
dir="$(require_extracted_repo "$repo" "$what")" || return 1
script="$dir/$rel"
if [[ ! -f $script ]]; then
printf 'ERROR: %s: expected installer not found at %s\n' "$what" "$script" >&2
return 1
fi
if [[ $root_mode == "sudo" && $EUID -ne 0 ]]; then
sudo bash "$script" "$@"
else
bash "$script" "$@"
fi
}
# install_root_module <repo> <what> <script-rel-path> [args...]
# As install_module_from_repo, for installers that require root but do not
# elevate themselves. A separate function rather than an env prefix because
# `env VAR=x fn` does not work -- these are shell functions, not binaries.
install_root_module() {
ROOT_MODE=sudo install_module_from_repo "$@"
}
ask_install() {
# ask_install <name> <command...>
# Prompts user; respects SECONDARY_MODE override.
local name="$1"
shift
if [[ $SECONDARY_MODE == "none" ]]; then
skip "$name (--none)"
SKIPPED+=("$name")
return
fi
if [[ $SECONDARY_MODE == "all" ]]; then
run_installer "$name" "$@"
return
fi
# interactive
local answer
printf '\nInstall %s? [y/N] ' "$(bold "$name")"
read -r answer
if [[ "${answer,,}" == "y" ]]; then
run_installer "$name" "$@"
else
skip "$name"
SKIPPED+=("$name")
fi
}
# ── Summary ──────────────────────────────────────────────────────────────────
print_summary() {
printf '\n%s\n' "$(bold "========== INSTALL SUMMARY ==========")"
if [[ ${#INSTALLED[@]} -gt 0 ]]; then
printf '%s\n' "$(green "Installed (${#INSTALLED[@]}):")"
for m in "${INSTALLED[@]}"; do printf ' %s %s\n' "$(green "✓")" "$m"; done
fi
if [[ ${#SKIPPED[@]} -gt 0 ]]; then
printf '%s\n' "$(yellow "Skipped (${#SKIPPED[@]}):")"
for m in "${SKIPPED[@]}"; do printf ' %s %s\n' "$(yellow "–")" "$m"; done
fi
if [[ ${#FAILED[@]} -gt 0 ]]; then
printf '%s\n' "$(red "Failed (${#FAILED[@]}):")"
for m in "${FAILED[@]}"; do printf ' %s %s\n' "$(red "✗")" "$m"; done
return 1
fi
}
# ═══════════════════════════════════════════════════════════════════════════════
# CORE MODULES (always installed)
# ═══════════════════════════════════════════════════════════════════════════════
printf '\n%s\n' "$(bold "Installing CORE modules…")"
# guard-lib provides guardctl, which the hosts guards and the shutdown timer
# both hard-depend on. It must land BEFORE them: setup_midnight_shutdown.sh
# dies with "guardctl not found on PATH" otherwise, which used to make the
# midnight-shutdown module fail on every fresh machine.
#
# guard-lib is not its own repo -- it is a subdirectory of github.com/kuhyx/utils,
# so the handle is "utils". Asking for "guard-lib" would resolve to ~/guard-lib
# and derive a clone URL that does not exist.
run_installer "Guard library (guardctl)" \
install_root_module utils "Guard library" guard-lib/install.sh
run_installer "Workout screen locker" \
install_module_from_repo screen-locker "Workout screen locker" install_systemd.sh
run_installer "Hosts blocking" \
install_module_from_repo hosts-blocker "Hosts blocking" install.sh
run_installer "Midnight shutdown timer" \
install_module_from_repo digital-wellbeing "Midnight shutdown timer" setup_midnight_shutdown.sh
# ═══════════════════════════════════════════════════════════════════════════════
# SECONDARY MODULES (prompted unless --all / --none)
# ═══════════════════════════════════════════════════════════════════════════════
printf '\n%s\n' "$(bold "Secondary modules (${SECONDARY_MODE})…")"
ask_install "Steam backlog enforcer" \
install_module_from_repo steam-backlog-enforcer "Steam backlog enforcer" install.sh
ask_install "Pacman wrapper" \
install_module_from_repo digital-wellbeing "Pacman wrapper" pacman/install_pacman_wrapper.sh
ask_install "i3 configuration" \
bash "$LINUX_CONFIG/i3/install.sh"
ask_install "Compulsive opening blockade" \
install_root_module digital-wellbeing "Compulsive opening blockade" block_compulsive_opening.sh install
# ═══════════════════════════════════════════════════════════════════════════════
print_summary