The guided installer asked about internals (usbip, linger, GameStream) and defaulted every optional to no. Holding Enter — or curl | sh with no TTY — on Bazzite/Nobara produced a degraded pad and a host that died at logout, which those distro pages already tell people not to do.
This PR:
asks about intent (full controller, third-party clients, clipboard, start at boot)
derives defaults from the already-detected family (sysext / Bazzite / Nobara / ujust → group + linger; active Sunshine/Apollo → GameStream; no graphical seat → linger)
prints a one-screen summary before the first sudo so --yes is auditable
keeps every flag and PUNKTFUNK_INSTALL_* twin; --yes uses sudo -n so a missing ticket fails instead of hanging
asserts the matrix in scripts/ci/check-install-defaults.sh (docs-drift + installer-smoke)
Checked --dry-run --yes --no-start on lea-oma (Omarchy 4.0.1): SSH/seatless → linger yes, group no; fake desktop seat → both no.
**User-facing fact changed?** Yes — `docs-site/content/docs/install.mdx`, `bazzite.mdx`, `gamescope.md`.
The guided installer asked about internals (usbip, linger, GameStream) and defaulted every optional to no. Holding Enter — or `curl | sh` with no TTY — on Bazzite/Nobara produced a degraded pad and a host that died at logout, which those distro pages already tell people not to do.
This PR:
- asks about intent (full controller, third-party clients, clipboard, start at boot)
- derives defaults from the already-detected family (`sysext` / Bazzite / Nobara / `ujust` → group + linger; active Sunshine/Apollo → GameStream; no graphical seat → linger)
- prints a one-screen summary before the first sudo so `--yes` is auditable
- keeps every flag and `PUNKTFUNK_INSTALL_*` twin; `--yes` uses `sudo -n` so a missing ticket fails instead of hanging
- asserts the matrix in `scripts/ci/check-install-defaults.sh` (docs-drift + installer-smoke)
Checked `--dry-run --yes --no-start` on lea-oma (Omarchy 4.0.1): SSH/seatless → linger yes, group no; fake desktop seat → both no.
Fixes #431
The guided installer defaulted every option to no, so Bazzite and
Nobara users who held Enter got a degraded pad and no linger —
the opposite of those distro pages. Prompts now name the intent,
defaults follow the box, and a summary prints before the first sudo.
Fixes#431
luxus
requested review from enricobuehler 2026-08-28 22:39:41 +00:00
Review of the intent-based defaults. Five fixes, three of them defects
the new matrix could not see, so each one gains a case that fails on the
code as it stood.
run(): the `sudo -n` rewrite keyed off --yes, but no terminal already
implies --yes and never the reverse, so `sh install.sh --yes` typed at a
prompt sent `sudo -n` and died on the first step without a cached ticket.
sudo reads /dev/tty, not stdin, so a terminal under --yes prompts fine.
Gated on `[ -z "$TTY" ]` instead, which is the case that actually has
nobody to type a password.
linger: the summary promised "start at boot" unconditionally, but the
`loginctl enable-linger` sat inside `if [ "$START" = 1 ]`, so
`--no-start` printed the promise and dropped it. Every case in the matrix
passes --no-start, so the gate was certifying exactly that. Linger is
configuration, not starting — moved out of the START branch, still ahead
of the `systemctl --user` probe it has to precede.
couch_box(): keying off FAMILY=sysext and a `ujust` on PATH covered
Silverblue, Kinoite, Bluefin and Aurora — desktop workstations that ship
rpm-ostree and ujust and got the group plus linger under --yes without
being asked. Narrowed to `like bazzite || like nobara`, which is what
install.mdx already claimed; gamescope.md corrected to match.
The group prompts name the grant again (usbip attach, only on a machine
you trust, with the docs link). Asking about intent is right, but under
--yes the summary is the only place it is stated, so that line names the
group too.
sunshine_active(): any non-zero exit from `detect-conflicts` read as
"Sunshine is running", so a crashed or too-old host binary would flip
GameStream on and open the plain-HTTP pairing port unasked. Only exit 1
is an answer now; anything else falls through to the unit probe.
check-install-defaults.sh also lifts run() out of the script and drives
it against a stub sudo — --dry-run returns before run() executes
anything, so no dry-run case can reach it.
installer-smoke went red on the root-container leg:
/tmp/tmp.9gBiJE8x4x/sudo: 2: exec: -n: not found
That shim is install.sh's own — the one it writes when running as root
with no sudo, so the verbatim `sudo …` lines from platforms.json still
work — and it is `exec "$@"`. Rewriting `sudo install -d …` to
`sudo -n install -d …` makes it `exec -n install -d …`, which is not a
command. The smoke job covers that path on purpose.
Not introduced by the previous commit: driving the run() from edf4effb
with no TTY and --yes against the same shim fails identically. Narrowing
the condition moved which sessions hit it, not whether they do.
So the rewrite goes. It buys nothing it claimed: with no terminal, sudo
already exits at once with "no tty present and no askpass program
specified" rather than hanging, and where an askpass helper IS set up,
-n breaks the one unattended path that works. Deleting it also drops a
global sed over an arbitrary command string.
The gate grows the case that would have caught this. Its stub sudo is
now that shim byte for byte, and it asserts the command actually runs.
First attempt at that check was vacuous — run() echoes each command
before running it, so a marker word matched the echo and passed on known
-bad code. The probe is `sudo expr 40 + 2` and the assertion is on 42,
which appears nowhere in the command text.
smoke (arch) got past the sudo fix and died one step later, on the
linger step this branch moved out of --no-start:
==> Starting at boot with nobody logged in
+ sudo loginctl enable-linger "$USER"
System has not been booted with systemd as init system (PID 1).
The smoke containers have no logind, so the call cannot work and linger
would mean nothing there. Reverting the move would put back the summary
that promises start-at-boot and silently drops it under --no-start, so
guard the call instead: run it when systemd is PID 1, warn and carry on
when it is not. An install that otherwise succeeded should not fail on
its last step for a setting the box cannot hold.
--dry-run still prints the command — it reports what a real box would do,
and the defaults matrix asserts on it.
The container path itself is checked by installer-smoke, which is where
it surfaced; no dry-run case can reach it.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
User-facing fact changed? Yes —
docs-site/content/docs/install.mdx,bazzite.mdx,gamescope.md.The guided installer asked about internals (usbip, linger, GameStream) and defaulted every optional to no. Holding Enter — or
curl | shwith no TTY — on Bazzite/Nobara produced a degraded pad and a host that died at logout, which those distro pages already tell people not to do.This PR:
sysext/ Bazzite / Nobara /ujust→ group + linger; active Sunshine/Apollo → GameStream; no graphical seat → linger)--yesis auditablePUNKTFUNK_INSTALL_*twin;--yesusessudo -nso a missing ticket fails instead of hangingscripts/ci/check-install-defaults.sh(docs-drift + installer-smoke)Checked
--dry-run --yes --no-starton lea-oma (Omarchy 4.0.1): SSH/seatless → linger yes, group no; fake desktop seat → both no.Fixes #431
smoke (arch) got past the sudo fix and died one step later, on the linger step this branch moved out of --no-start: ==> Starting at boot with nobody logged in + sudo loginctl enable-linger "$USER" System has not been booted with systemd as init system (PID 1). The smoke containers have no logind, so the call cannot work and linger would mean nothing there. Reverting the move would put back the summary that promises start-at-boot and silently drops it under --no-start, so guard the call instead: run it when systemd is PID 1, warn and carry on when it is not. An install that otherwise succeeded should not fail on its last step for a setting the box cannot hold. --dry-run still prints the command — it reports what a real box would do, and the defaults matrix asserts on it. The container path itself is checked by installer-smoke, which is where it surfaced; no dry-run case can reach it.