fix(installer/windows): a silent install can't hang on a prompt, and an upgrade keeps your choices
Three faults that only bite when nobody is watching the wizard — which is how a package manager always runs us, and how a re-run by hand half-behaves today. The conflicting-host warning used a plain MsgBox. That ignores /SUPPRESSMSGBOXES and displays even under /VERYSILENT, so an unattended install onto a box already running Sunshine/Apollo would sit on a modal dialog nobody can see or click. SuppressibleMsgBox with an IDNO default aborts instead, which is the honest answer for a combination the message itself calls unsupported. The GameStream and Public-firewall choices were re-applied on every run. A silent run has no wizard, so every task falls back to its script default and the installer confidently re-asserted it: a user who enabled GameStream had it turned off again, and one who opened the firewall on Public networks had it quietly revoked (that task is default-unchecked, so the reset ran the other way). Both params are now fresh-install-only, keyed off whether host.env already exists. `service install` already read an absent --gamestream as "keep host.env as-is"; --allow-public-network becomes tri-state the same way, resolving an absent flag from the marker the previous install recorded. It is strict on the value: a typo'd =of must not fall through to a marker that may say true, because that turns a mistyped opt-OUT into leaving Public open. Also brands the installer "Punktfunk Host" in the strings a user actually sees — Add/Remove Programs, the Start Menu group, the wizard task text. Paths are untouched; renaming those would relocate installs and orphan existing config. Verified on the windows-amd64 runner: ISCC compiles the script (with WithWeb defined, so the password page parses too), and cargo check + clippy -D warnings pass for punktfunk-host with nvenc,amf-qsv,qsv. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -403,7 +403,7 @@ fn web_setup(args: &[String]) -> Result<()> {
|
||||
// only with `--allow-public-network`. Delete any prior rule first so an upgrade re-scopes it
|
||||
// instead of stacking a second (possibly all-profiles) rule behind the new one.
|
||||
let fw_profile =
|
||||
crate::service::firewall_profile_arg(crate::service::allow_public_network(args));
|
||||
crate::service::firewall_profile_arg(crate::service::allow_public_network(args)?);
|
||||
run_quiet(
|
||||
"netsh",
|
||||
&[
|
||||
|
||||
@@ -718,8 +718,9 @@ fn install(args: &[String]) -> Result<()> {
|
||||
// Firewall scope: Domain + Private by default; `--allow-public-network` opts into Public too.
|
||||
// Persist the choice (so the startup warning respects an opt-in) and re-scope idempotently —
|
||||
// remove any prior rules first so an upgrade tightens the scope instead of leaving a stale
|
||||
// all-profiles rule behind the new one.
|
||||
let allow_public = allow_public_network(args);
|
||||
// all-profiles rule behind the new one. With the flag absent (every upgrade) this resolves to
|
||||
// the previously recorded choice, so the rewrite below is a no-op rather than a silent reset.
|
||||
let allow_public = allow_public_network(args)?;
|
||||
set_fw_public_marker(allow_public);
|
||||
remove_firewall_rules();
|
||||
add_firewall_rules(allow_public);
|
||||
@@ -889,10 +890,37 @@ pub(crate) fn firewall_profile_arg(allow_public: bool) -> &'static str {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `--allow-public-network` install opt-in (the installer's "Allow connections on Public
|
||||
/// networks" task forwards it). Absent = the secure default (Domain + Private only).
|
||||
pub(crate) fn allow_public_network(args: &[String]) -> bool {
|
||||
args.iter().any(|a| a == "--allow-public-network")
|
||||
/// Resolve the Public-network firewall scope for this install/upgrade (the installer's "Allow
|
||||
/// connections on Public networks" task forwards the flag).
|
||||
///
|
||||
/// Tri-state, mirroring `--gamestream=on|off`:
|
||||
/// * `--allow-public-network` or `=on` -> `true` — explicit opt-in. The bare form is kept for
|
||||
/// back-compat with existing scripts and docs that pass it that way.
|
||||
/// * `--allow-public-network=off` -> `false` — explicit opt-out.
|
||||
/// * absent -> whatever the previous install recorded.
|
||||
///
|
||||
/// The absent case is what makes an UPGRADE non-destructive. A silent upgrade (`winget upgrade`)
|
||||
/// has no wizard to carry the old checkbox forward, so the installer omits the flag entirely on an
|
||||
/// upgrade and the recorded choice stands. Re-applying the task default instead would quietly
|
||||
/// re-scope the firewall on every upgrade, undoing an opt-in the user made once. On a first-ever
|
||||
/// install there is no marker, so absence resolves to the secure default (Domain + Private only).
|
||||
///
|
||||
/// Strict on the value: a typo'd `=of` must NOT fall through to the marker, because the marker may
|
||||
/// say `true` — i.e. a mistyped opt-OUT would silently leave Public open.
|
||||
pub(crate) fn allow_public_network(args: &[String]) -> Result<bool> {
|
||||
for a in args {
|
||||
if let Some(v) = a.strip_prefix("--allow-public-network") {
|
||||
return match v {
|
||||
"" | "=on" => Ok(true),
|
||||
"=off" => Ok(false),
|
||||
_ => bail!(
|
||||
"--allow-public-network must be 'on' or 'off' (got '{}')",
|
||||
v.trim_start_matches('=')
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
Ok(fw_public_marker().exists())
|
||||
}
|
||||
|
||||
/// Inbound firewall rules for the streaming + mgmt ports (best-effort; logs but never fails the
|
||||
|
||||
Reference in New Issue
Block a user