Skip to content

chore: resync install.sh + uninstall.sh with freenet-core post-review#37

Merged
sanity merged 1 commit into
mainfrom
uninstall-sh-resync
Apr 18, 2026
Merged

chore: resync install.sh + uninstall.sh with freenet-core post-review#37
sanity merged 1 commit into
mainfrom
uninstall-sh-resync

Conversation

@sanity
Copy link
Copy Markdown
Contributor

@sanity sanity commented Apr 18, 2026

Problem

#36 merged an early snapshot of static/install.sh and static/uninstall.sh. After #36 opened, freenet-core#3906 picked up several review fixes (from code-first, skeptical, and codex) that never made it into the freenet/web copies — so curl https://freenet.org/uninstall.sh | sh is currently serving the pre-review version with known bugs.

What's missed in the current live copy

uninstall.sh:

  • Linux purge paths were UPPERCASE (~/.local/share/Freenet). directories crate on Linux lowercases the application name, so the actual paths are lowercase. Purge silently removed nothing.
  • rm -rf "${HOME}/..." with empty HOME turned into an absolute-from-root path. HOME=/root also deleted the wrong user's data.
  • $(id -u) via PATH could be spoofed by a user-writable ~/.local/bin/id.
  • FREENET_INSTALL_DIR honored by install.sh was ignored by uninstall.sh.
  • Docstring claimed to scan "the current exe's dir" — never implemented.

install.sh:

  • One remaining em-dash in the post-install sudo warning.

Approach

Straight copy from freenet-core/scripts/uninstall.sh and install.sh at commit 1b7c8855 (freenet-core#3906 head after all review fixes). No freenet/web-specific changes on top.

Testing

  • sh -n clean on both files.
  • Hugo builds cleanly.
  • The script itself is covered by scripts/test-uninstall-sh.sh in freenet-core#3906 (13 smoke-test cases, all passing).

Related

[AI-assisted - Claude]

PR #36 merged an early snapshot of these two scripts. After that PR
opened, freenet-core#3906 picked up several review fixes that never
made it into the freenet/web copy:

- uninstall.sh: Linux paths lowercased to match what `directories`
  actually creates (`~/.local/share/freenet` etc.), HOME="" and
  HOME="/root" safety guards added, absolute /usr/bin/id used so a
  PATH-shimmed id can't bypass the root check, FREENET_INSTALL_DIR
  now honored, em-dashes replaced with hyphens, docstring corrected
  to drop the "current exe's dir" claim that was never implemented.

- install.sh: a trailing em-dash in the post-install sudo warning
  replaced with an ASCII hyphen for house-style consistency.

This PR is a straight copy from `freenet-core/scripts/` at commit
1b7c8855 so the two sources stay in sync.

[AI-assisted - Claude]
@sanity sanity merged commit dac00eb into main Apr 18, 2026
@sanity sanity deleted the uninstall-sh-resync branch April 18, 2026 16:20
sanity added a commit that referenced this pull request Apr 30, 2026
* fix(install): read prompt from /dev/tty when stdin is piped

When the install script is run via the documented `curl ... | sh`
pattern, stdin is the pipe carrying script bytes, so `read -r response`
cannot capture keystrokes and the service-install prompt silently
ignores keyboard input (reported by Ivvor on Matrix).

A previous fix (eff317ae, Dec 2025) addressed this with `< /dev/tty`,
but PRs #36 / #37 resynced this file from freenet-core and dropped the
override. Reapply the fix and harden it for environments without a
controlling terminal.

The new logic:
- If stdin is a TTY, read normally (covers `sh install.sh`).
- Else, probe /dev/tty by actually opening it (`{ true </dev/tty; }`)
  rather than relying on `[ -r /dev/tty ]`, which can return true even
  when the open later fails. If /dev/tty is openable, read from it
  (covers `curl | sh`).
- Otherwise, skip the prompt rather than aborting under `set -eu`
  (covers truly non-interactive shells, e.g. CI or `setsid`).

Verified via a pty harness: `curl | sh` simulation under `script(1)`
reads "y" from /dev/tty and proceeds; `setsid ... </dev/null` skips
the prompt and prints follow-up instructions.

Note: freenet-core/scripts/install.sh has the identical bug; a matching
fix will follow there to keep the resync in sync.

[AI-assisted - Claude]

* fix(install): address review feedback - $0 detection, EOF guard

Five-perspective review on PR #42 surfaced three issues with the
initial fix:

- Codex P2: discriminating only on `[ -t 0 ]` regresses
  `printf 'y' | sh install.sh` automation patterns. When stdin is
  piped but the script ran from a file, the user's answer is on
  stdin, not /dev/tty.

- Skeptical #1: `read -r response` returning EOF (Ctrl-D, closed
  stdin) under `set -eu` aborts the script.

- Skeptical #10: comment used an em-dash, which Ian's writing-style
  rule rejects.

Redesigned the dispatch around `$0`: when sh runs a script file
(file-execution), $0 is the script path; when sh reads its own
source from stdin (`curl | sh`), $0 is the shell name. The case
arm tests for known shell names and only redirects to /dev/tty in
that branch. The file-execution arm reads from stdin as before, so
piped automation still works.

Both `read` calls now have `|| response=""` so EOF leaves the
default-N case path intact rather than aborting.

Also (lower priority feedback applied):

- Skipped path now mentions FREENET_NO_SERVICE=1 as the documented
  way to bypass the prompt non-interactively (Skeptical #2/#3).

- Removed the redundant "Non-interactive shell detected" info line
  (Skeptical #7); the existing `*)` case arm already prints
  "Skipping service installation" plus remediation guidance.

- Added a NOTE block at the top of the file pointing future
  resync agents at the freenet-core sibling and explaining why
  the prompt block must not be flattened back to a plain `read`
  (Big-picture #5).

Re-tested via the same pty harness across five scenarios:
  1. `curl | sh` under pty: reads 'y' from /dev/tty, installs.
  2. `sh install.sh` interactive: reads from stdin.
  3. `printf 'y' | sh install.sh`: reads from piped stdin (regression
     test for Codex's concern).
  4. `setsid ... </dev/null`: skips, prints FREENET_NO_SERVICE hint.
  5. `sh install.sh </dev/null`: handles EOF without aborting.

All five pass.

[AI-assisted - Claude]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant