One clean set of scripts to replace arch-mod, arch-mini-mod, artix-mod, debian-mod, debian-mini-mod, fedora-mod, and void-mod.
No per-distro script piles: every module is a single file that branches on the detected distro internally — one place to fix a package name, one place to add a distro.
linux-mod.sh entry point: detect distro -> menu -> modules
lib/
├── common.sh logging, sudo, network, resumable state
├── ui.sh designed TUI: gum → fzf → whiptail → select
└── pkg.sh the abstraction that makes unification work:
detect_distro, pi (install), aur, fp (flatpak),
svc_enable/disable (systemd/runit/openrc/dinit)
modules/
├── base.sh unified step-1: repos, CLI core, audio, network,
│ services, fonts, theme, GNOME, debloat, mini tweaks
├── gnome-extensions.sh pop-shell, blur-my-shell, appindicator, gsconnect,
│ just-perfection, workspaces, super-key
├── apps.sh unified apps: flatpak core, yazi+plugins, neovim,
│ vscode/opencode, tailscale/synology, docker...
├── dots.sh piercing-dots rice + .bashrc + first-login polish
├── wm.sh Hyprland, Sway, i3, bspwm, Awesome, Qtile, DWM,
│ herbstluftwm, GNOME, BusyBox — per-distro branches
├── drivers.sh Nvidia+CUDA, Surface kernel, KooTigers touchscreen,
│ NuVision tablet, TTY rotation
└── extras.sh printers (Canon D530 / Omezizy), gaming, Cosmic
resources/
├── kootigers/ Goodix touchscreen boot service
└── nuvision/ Broadcom wifi/BT + SOF audio firmware, hwdb
| Full | Mini | Notes | |
|---|---|---|---|
| Arch (+ CachyOS, EndeavourOS, Manjaro) | ✅ | ✅ | paru auto-bootstrapped |
| Artix | ✅ | ✅ | runit/OpenRC/dinit aware, ly login |
| Debian / Ubuntu / Pop!_OS / Mint | ✅ | ✅ | Hyprland built from source |
| Fedora | ✅ | ✅ | COPR + RPM Fusion |
| Void | ✅ | ✅ | runit, GDM off by default |
| openSUSE Tumbleweed | ✅ | ✅ | zypper, NVIDIA repo for drivers |
| Alpine | ✅ | musl: mini-focused; flatpaks fill the gaps |
Full = complete workstation: desktop, media/production apps, docker, gaming offer. Mini = lean tablet/mini-PC build: core system + rice, extra wifi/BT firmware, boots to TTY on Arch (no display manager), skips the heavy apps. Both profiles work on every distro.
git clone https://github.com/Piercingxx/linux-mod.git
cd linux-mod
./linux-mod.sh # interactive menu
./linux-mod.sh --full # unattended full install (no DE unless --wm)
./linux-mod.sh --mini # unattended mini install
./linux-mod.sh --full --wm hyprland,gnome
./linux-mod.sh --dry-run # print what would happen, change nothing
./linux-mod.sh --reset-state # forget completed steps, redo everything
./linux-mod.sh --no-color # plain output (NO_COLOR env works too)
./linux-mod.sh update # pull the latest linux-modRun as your normal user (not root); sudo is requested once and kept alive.
Everything is logged to /tmp/linux-mod.log, and every run ends with a
summary table of any warnings.
Resumable installs: each stage (base / dots / apps) writes a marker to
~/.local/state/linux-mod/ on success. If an install dies halfway, rerun it —
completed stages are skipped. --reset-state starts fresh.
Dry-run: --dry-run short-circuits every package install, service change,
flatpak, AUR build, and sudo command into a [dry-run] echo. It's what the CI
matrix runs in each distro container. (It's an approximation: a few read-only
probes still execute.)
TUI: PiercingXX ink/signal chrome (not Charm pink). Prefers gum, then
fzf, then whiptail, then POSIX select. If gum is missing it is installed
from the distro repo, or (non-Alpine) fetched as one pinned static binary into
~/.local/share/linux-mod/bin. Full and Mini do not assume a desktop —
the menu asks you to pick one or more WMs after apps. Unattended runs skip
that unless you pass --wm. No automatic reboot.
Menu: Full Install · Mini Install · Window Managers (multi-select) · Drivers & Hardware · Extras — options that don't apply to your distro simply don't appear.
piinstalls packages and skips individually-unavailable ones with a warning instead of aborting a 40-minute install.svc_enable bluetooth bluetoothd— first service name that exists wins, regardless of init system.- Distro detection uses
/etc/os-releaseIDwithID_LIKEfallback, so derivatives just work. - Modules never call
pacman/apt/... directly except inside an explicitcase "$DISTRO"branch that needs distro-specific behavior.
- openSUSE Tumbleweed support (
zypperbranch inlib/pkg.sh) - Alpine support for the mini profile (apk, OpenRC)
-
--dry-runflag that prints every install/enable action without executing - Per-module state markers (
~/.local/state/linux-mod/) for resumable installs - CI: shellcheck + per-distro container dry-runs (
.github/workflows/ci.yml) - Designed TUI (PiercingXX ink/signal): gum → fzf → whiptail → select, pinned gum bootstrap, no auto-reboot,
--wm - Self-update:
linux-mod.sh update - Move package lists out to
packages/*data files (skipped for now — thecase "$DISTRO"blocks are the data, and one more indirection layer isn't worth it at this size)
MIT — see LICENSE.