chipsenkbeil / chipsenkbeil/distant
install.sh: fresh install aborts on missing .b2 checksum (404) and hangs in infinite loop on re-run without a TTY
- Dominant language
- Rust
- Stars
- 635
- Forks
- 12
- PR merge metrics
- No merged PRs in 30d
Description
## Summary
The official install script (`curl -L https://sh.distant.dev | sh`) is broken in two independent ways on a standard Linux host (Ubuntu 24.04, `/bin/sh` = dash):
1. **Fresh install aborts silently** because the script tries a BLAKE2 (`.b2`) checksum that releases don't ship, and the 404 handler hard-exits before the SHA256 fallback can run.
2. **Re-running the script hangs in an infinite loop** (eventually OOM/spew) when stdin is a pipe and there's no `/dev/tty`.
Tested with the script served from `https://sh.distant.dev` (sha256 of my copy aborts at the same place), `distant` release `v0.20.0`.
---
## Bug 1: `.b2` checksum 404 aborts install (SHA256 fallback unreachable)
`verify_checksum()` tries BLAKE2 first when `b2sum` is present:
```sh
if check_cmd b2sum; then
local _checksum_url="${_url}.b2"
...
if downloader "$_checksum_url" "$_tmpsum" "$_arch" 2>/dev/null; then
```
But the `v0.20.0` release assets only contain `*.sha256sum` files — there is **no `.b2`** (and no `.sha256`):
```
distant-x86_64-unknown-linux-gnu -> 200
distant-x86_64-unknown-linux-gnu.b2 -> 404
distant-x86_64-unknown-linux-gnu.sha256 -> 404
distant-x86_64-unknown-linux-gnu.sha256sum -> 200
```
The intent is for the `.b2` download to fail gracefully and fall through to SHA256. But `downloader()` turns any 404 into a fatal `err` (which calls `exit 1`):
```sh
if [ -n "$_err" ]; then
echo "$_err" >&2
if echo "$_err" | grep -q 404$; then
err "binary for platform '$3' not found, this may be unsupported"
fi
fi
```
So when `b2sum` is installed (it is, by default, on Ubuntu 24.04 via coreutils), the `.b2` fetch 404s, `downloader` calls `err`/`exit`, and the script dies *before* the SHA256 path. The `2>/dev/null` in `verify_checksum` even hides the message, so the user just sees:
```
Initializing...
Downloading https://github.com/chipsenkbeil/distant/releases/latest/download/distant-x86_64-unknown-linux-gnu to /home/me/.local/bin/distant
```
…and then nothing — no binary installed, exit code 1.
### Reproduce
```sh
$ b2sum --version # present on stock Ubuntu 24.04
$ curl -L https://sh.distant.dev | sh
Initializing...
Downloading .../distant-x86_64-unknown-linux-gnu to ~/.local/bin/distant
$ echo $?
1
$ ls ~/.local/bin/distant
ls: cannot access ... : No such file or directory
```
`sh -x` trace ends at:
```
+ downloader https://.../distant-x86_64-unknown-linux-gnu.b2 /tmp/tmp.XXXX x86_64-unknown-linux-gnu
(script exits here)
```
### Suggested fix
Either ship `.b2` checksums in releases, or make the checksum-download path tolerant of a missing checksum file. The cleanest fix is to not treat a checksum-URL 404 as fatal — e.g. have `downloader` return non-zero on 404 instead of `exit`ing, or add a "soft" download mode for checksum probes. Also worth probing `.sha256sum` (the name actually published) in the SHA256 fallback — currently it tries `.sha256` then `.sha256sum`, but it never gets there.
---
## Bug 2: Infinite loop on re-run via pipe (no `/dev/tty`)
When `distant` is already installed and `--on-conflict` is the default (`ask`):
```sh
*)
local _answer
while [ ! "${_answer:-}" = "y" ] && [ ! "${_answer:-}" = "n" ]; do
_answer=$(prompt "Distant is already installed. Overwrite it? (y/n)" "")
done
```
`prompt()` reads from `/dev/tty` when stdin is not a TTY:
```sh
if [ ! -t 0 ]; then
read -r _value < /dev/tty
else
read -r _value
fi
```
In a piped install (`curl ... | sh`) there is no controlling terminal, so `/dev/tty` can't be opened. Under dash this produces:
```
Distant is already installed. Overwrite it? (y/n): install.sh: 561: cannot open /dev/tty: No such device or address
install.sh: 566: _value: parameter not set
```
`read` fails, `_value` is never set, the `while` condition is never satisfied, and the loop spins forever printing the prompt (I had to kill it after it produced ~55MB / 724k lines of output).
Note there's already logic at the top that forces `fail` when `QUIET=yes`, but a normal piped, non-quiet install still reaches this loop.
### Reproduce
```sh
# with distant already installed at ~/.local/bin/distant
$ curl -L https://sh.distant.dev | sh
Distant is already installed. Overwrite it? (y/n): install.sh: ... cannot open /dev/tty ...
# spins forever
```
### Suggested fix
Detect that no TTY is available and bail out with a clear message (or honor `--on-conflict fail`) instead of looping. At minimum, break the loop if `read` fails / `/dev/tty` can't be opened. The README example `curl -L https://sh.distant.dev | sh -s -- --on-conflict overwrite` works around it, but the documented default one-liner should not hang.
---
## Environment
- OS: Ubuntu 24.04.4 LTS, x86_64
- `/bin/sh` -> dash
- `b2sum` present (coreutils), `sha256sum` present
- distant release: `v0.20.0` (`latest`)
## Workaround
Manual download works fine:
```sh
curl -fL https://github.com/chipsenkbeil/distant/releases/latest/download/distant-x86_64-unknown-linux-gnu \
-o ~/.local/bin/distant && chmod +x ~/.local/bin/distant
```
`distant --version` -> `distant 0.20.0`.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with install.sh's verify_checksum, downloader, and prompt paths, and reproduce under /bin/sh (dash) with b2sum and with stdin piped and no /dev/tty. Verify that the published .sha256sum fallback is reached and that a noninteractive rerun exits clearly instead of looping; exercise the documented curl command and conflict options.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- shell
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 72/100