YuKu's expanded always-section died at section 6: p7zip was RETIRED
from Fedora 44 (replaced by the official '7zip' package) -> the
compression line's transaction failed -> set -e killed the whole
install after everything before it had already succeeded.
Two changes:
- p7zip -> 7zip in the Compression line (unace/unrar verified fine:
they live in RPM Fusion, which base enables).
- The always-loop now warns in red per failing category line and
continues — same resilience contract as the advanced menu. One
missing package can no longer abort a 30-minute install at the
finish line.
Harness: healthy run installs everything incl. 7zip/mpv/wget; a
deliberately broken compression line produces the red Failed block,
'finished with failures' summary, exit 0, and later categories
(mpv, wget...) still install.
Two bugs read straight out of YuKu's VM screenshot:
1. 'Unknown argument "add" for command "config-manager"' — dnf5 has
no 'add' subcommand, so the Brave repo never landed and
brave-browser had no match. Now uses 'config-manager addrepo
--from-repofile=' per Brave's own Fedora 41+ docs; skips when
/etc/yum.repos.d/brave-browser.repo exists. The .repo carries the
gpgkey, so the rpm --import dance is gone.
2. 'Installation failed: chromium brave-browser' — multi-select put
all picks in ONE dnf transaction, so brave's no-match killed
chromium too. Picks now install PER ITEM: one failure red-warns
with the item's own name and the remaining picks still install.
ui_quiet_run now paints a TTY-safe status BLOCK: a background-coloured
SPACE (never a glyph, no font/locale dependency — the braille lesson).
busy : blue block breathing through a 256-colour ramp (48;5;17..33)
with the live dnf action after it
ok : solid green block + package count
fail : solid RED block (spec: YuKu changed the red X to a red block)
followed by the log tail
Colour-depth aware: 256-colour ramp -> 16-colour blue/bright-blue ->
plain ASCII spinner when the terminal reports no colour. LANG=C pty
run shows zero non-ASCII bytes.
Also vendors lib/ui-block.sh (the standalone helper from the Telegram
session prototype; fail changed to red block) and installs the
ui_block_close EXIT trap — with a return 0, because an EXIT trap's
status becomes the script's exit code (harness caught every-successful-
run exiting 1 through that path).
The braille spinner frames and the em-dash separator are multibyte
UTF-8; on the VM session they rendered as changing random letters, not
a spinner. Frames are now the classic | / - \ cycle, the separator is
':', and the two user-facing info lines that carried em-dashes are
ASCII too. Verified under LANG=C LC_ALL=C in a pty: zero non-ASCII
bytes in the entire output.
First sudo call of the session printed its password prompt straight
into the live progress line: 'Installing (optional): gum[sudo]
password for yuri:' (VM screenshot). sudo -v asks once, on a clean
screen, right after the banner; the timestamp refreshes on every later
sudo use, so one prompt covers the whole run.
ui_quiet_run wraps every install_packages/install_optional/group
install/remove and the system update: while gum is available and the
run is interactive, dnf output is hidden behind a spinner, then a
single 'Installing: <pkgs> (N packages)' line. Full output is appended
to $SETUP_DNF_LOG (/tmp/fedora-setup-dnf-<uid>.log); on failure the
last 20 lines print in red so nothing is lost. Unattended and no-gum
runs keep plain full output (logs/cron contract).
Also: fallback ui_choose accepts 'x' to exit (muscle memory from the
old menu), fixed a stray stdout newline leaking into captured answers
(same class as the earlier '\nno' bug), fixed pluralization.
Harness: piped fallback, pty gum run incl. a failing VirtualBox pick
(red failure + log tail, menu survives, subsequent qemu install ok),
unattended regression.
Interactive install gets styled menus, confirms, multi-select and a
spinner around the system update when gum is available (dnf install
gum, installed optionally in the base section); every prompt falls
back to the classic numbered/yn prompts otherwise, and --yes runs
stay 100% non-interactive.
Call sites moved to ui_choose/ui_confirm/ui_choose_multi/ui_run:
DE + TWM menus (defaults preserved: none/none), SELinux + dotfiles
questions, advanced-menu category + multi-select app picker, reboot
prompt. Dead ask_choice/ask_yes_no removed.
Two bugs caught by pty-based harness testing before shipping:
- stdout is a pipe inside $(ui_...), so have_gum must test stdin +
/dev/tty, never -t 1 (gum would never have fired on a real term).
- fallback display output must go to >&2 — a stray newline on stdout
made choice='\nno' and broke every case match.
Fresh-VM timeline explained: on run 1 virt-manager installs AFTER the
niri module's prereq check ran (menu dispatch order), so the group add
was evaluated against a package state that did not include it yet. A
second run 'fixed' it — matching the observed behavior.
- New ensure_libvirt_group runs in the Summary section, seeing the FINAL
package state; menu/module-time check kept as an early pass.
- pkg_installed is AND-semantics (all must be installed); the gate needs
OR. New pkg_installed_any helper; both gates switched to it.
- Also fixed a set -e trap: '$done_any && ok' returned 1 (killing the
script) when no optional tools were picked — replaced with an if.
All four gate cases harness-proven: virt-only, menu-pick, no-virt,
already-in-group.
usermod used $USER directly; under sudo $USER can be root. All other
user-scoped calls already use ${SUDO_USER:-$USER} — aligned. No hardcoded
usernames anywhere in the repo (verified by grep; only the dotfiles repo
URL, which is overridable via DOTFILES_REPO).
A clean-snapshot run showed qemu/virt-manager installed without choosing
it: PR #4 had embedded the install in the niri module. Removed from there
(zero virt packages in a plain niri run, harness-verified) and moved into
install_category_pkgs Virtualisation: picking qemu/virt-manager there now
also enables libvirtd and adds the user to the libvirt group.
Root cause of the VM failures (both qemu-kvm AND VirtualBox showed 'No
match for argument: <entire list>'): picks were resolved into ONE quoted
string and passed to dnf as a single argument, so dnf searched for a
package literally named 'qemu-kvm libvirt virt-install ...'. Picks are
now word-split into separate package arguments.
Also, a failing install no longer kills the script (set -e abort): the
menu prints a red 'Installation failed' warning, notes the system is
unchanged, and drops back to the menu so another choice can be made.
Proven in harness: VBox failure (no match) followed by successful qemu
install in the same session, exit 0.
Section 6 'Always-installed packages': ALWAYS_PACKAGES array, one line per
category (first: Terminals with ghostty + ghostty-bash-completion). Every
entry installs on every run, no questions. Add a category by adding a line.
Section 7 'Advanced optional tools': menu-driven picker, data-driven —
ADV_CATEGORIES + ADV_<Category> arrays ('id:Label:pkgs...'). Ships
Virtualisation (qemu/kvm stack, VirtualBox) and Browsers (firefox,
chromium, google-chrome, brave; the latter two wire up their repos
automatically). Multi-select with comma-separated picks, invalid picks
warned and skipped, 'x' backs out, menu is skipped under --yes so
unattended runs never hang.
Bugs found by harness testing and fixed before shipping:
- ${!var} on an array name yields only element[0]: Browsers listed one
entry. Now uses a nameref (declare -n).
- var assignment was lost in an edit: unbound-variable crash on the menu.
- --snapper=no was overwritten by the interactive default; explicit flags
now always win.
- On btrfs, snapshots + zstd compression now just happen (no prompt);
--snapper=no remains the explicit opt-out.
- Vendor scripts/snapper-*.sh + config/snapper.actions from
SysGuides/sysguides-snapper-fedora (MIT, upstream 0a28eb4) into
vendor/snapper/ and install from there — no runtime clone of an
external repo, and no dependency on exec bits (fixes the
'./install.sh: Permission denied' failure seen on the test VM).
- grub-btrfs still cloned at runtime from Antynea/grub-btrfs with the
Fedora config applied; everything else is local now.
New modules/user/dotfiles.sh behind a menu question ('Clone personal
dotfiles?', default no) and --dotfiles=yes|no flag.
Does a --bare clone of yuri/fedora-dots into ~/.cfg.git, checks the repo
out into $HOME with status.showUntrackedFiles=no, and handles an empty
repo gracefully (configures the alias, skips checkout). Existing
~/.cfg.git is left untouched (idempotent).
Fix proven in the harness: the module originally used a plain git clone,
which creates a working copy and breaks the git-dir alias — the same
failure YuKu hit manually; --bare is required.
Dispatch runs the dotfiles module before DE/TWM so configs are in place
when sessions are installed.
noctalia 5.3.0 is in the Fedora 44 updates repo (confirmed on
packages.fedoraproject.org and by a fresh-install dnf install noctalia).
The niri module no longer calls add_terra_repo; Terra stays in the base
section and in the mango module, which is the only consumer left
(mangowc). README + comments updated.
New section between base and DE menus:
- 'Disable SELinux permanently?' — default no; writes SELINUX=disabled with a
backup, warns about the security tradeoff.
- When / is btrfs: 'snapper + grub-btrfs + compression?' — default yes. Shows
subvolume list + fstab, appends ,compress=zstd:1 to btrfs fstab entries that
lack a compress= option (idempotent, fstab backed up), then runs the
SysGuides sysguides-snapper-fedora installer (snapper root+home, grub-btrfs,
DNF5 pre/post snapshot actions).
- Flags for unattended runs: --selinux=keep|disabled, --snapper=yes|no.
- Fix found by the harness: the flag initializers sat after the arg loop and
wiped parsed values; --snapper=no was silently ignored. Initializers moved
before the loop.
README + section renumbering updated.
- Terra repo is added right after RPM Fusion in the base setup (it carries
noctalia + mangowc). Modules keep calling add_terra_repo so they still
work standalone; it is a no-op when terra-release is present.
- Hyprland was retired from the Fedora repos after F42 (dependency churn);
the module could never resolve 'hyprland' on F44. It now enables the
solopasha/hyprland COPR first — the de facto standard for F43/44 — and
treats the accessory packages (hyprpaper/hyprlock/waybar/mako/wofi/
xwayland-satellite) as optional so a name drift warns instead of aborting.
- README updated.
- XFCE module installs/enables sddm instead of lightdm; one greeter across
every session choice, no lightdm left anywhere.
- Interactive menus now default to 'none' for both DE and TWM (press Enter
for no DE / no TWM). none+none is still refused by the existing guard.
- README and session.sh comments updated to match.
The corrections commit replaced the lib/+modules/ entrypoint with the
older single-file version, reintroducing the DEBUG_MODE unbound-variable
crash, the unguarded clear, and TWM dispatch pointing at nonexistent
root-level scripts. lib/ and modules/ were orphaned.
Restores the tested restructured setup.sh + the SC2034 fix in lib/common.sh.
Splits setup.sh into lib/common.sh, lib/session.sh and seven modules
(3 DE, 4 TWM). Targets a bare Fedora 44+ install.
Fixes carried over from the first build:
- DEBUG_MODE was unbound under set -u, aborting the script at the version banner
- mangled 'echohttps://...' line left over from the starburst split
- set -e was missing entirely; --debug flag parsing restored
- unguarded 'clear' aborted the run without TERM
- $PRETTY_NAME unguarded under set -u
- modules were sourced, leaking shell options into the parent
- Flathub added twice (system-wide + per-user)
- no dnf5-plugins check before using 'dnf config-manager'
- no Fedora release gate
- /usr/share/xsessions assumed to exist when writing session entries