curl | bash fails: BASH_SOURCE[0] unset when piped from stdin #5

Closed
opened 2026-08-23 12:31:02 +00:00 by MillasAgent · 0 comments
Owner

Summary

When invoked as curl -fsSL <url>/workspace/opencode/install.sh | bash, install.sh fails immediately with bash: línea 24: BASH_SOURCE[0]: variable sin asignar. This breaks the documented one-line install UX entirely (the entire openmillas install value proposition).

Reproduction

$ curl -fsSL https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode/install.sh | bash
bash: línea 24: BASH_SOURCE[0]: variable sin asignar
bash: línea 26: /home/borja/lib/jsonc.sh: No Existe el fichero o el directorio
curl: (23) Failure writing output to destination, passed 4096 returned 448

(Confirmed on a public-read clone of the repo today; the URL resolves, the script downloads, but bash exits with the errors above before any install logic runs.)

Expected

curl ... | bash should run the installer interactively (or non-interactively if --yes --no-interaction are appended), with a graceful fallback when the bundle files are not co-located with the install script (because stdin-installed scripts have no on-disk SCRIPT_DIR).

Actual

Bash exits at line 24 with BASH_SOURCE[0]: variable sin asignar, the source "$SCRIPT_DIR/lib/jsonc.sh" on line 26 fails because SCRIPT_DIR resolved to the wrong path (the script's stderr ended up at the user's $HOME), and curl's Failure writing output to destination shows the pipe was closed prematurely. Three symptoms of one bug.

Root cause

install.sh line 24 (and line 28 in openmillas) compute SCRIPT_DIR from BASH_SOURCE[0]:

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

When a script is read from stdin (the curl | bash case), bash does not populate BASH_SOURCE for the script being executed. BASH_SOURCE[0] is unset, dirname of an empty string returns ., the cd ... && pwd resolves to $HOME (wherever the user's bash happens to start), and then source "$SCRIPT_DIR/lib/jsonc.sh" tries /home/<user>/lib/jsonc.sh which does not exist.

The pattern works for bash workspace/opencode/install.sh because the script is a real file on disk and BASH_SOURCE[0] is the path to it. It fails for curl | bash because there is no file.

This affects install.sh and openmillas (same pattern), and it is the only curl | bash UX we ship.

Suggested fix

Make SCRIPT_DIR resolution tolerant of stdin-sourced scripts. When BASH_SOURCE[0] is unset or empty, fall back to:

  1. $0 (may be bash itself if the user's shell invoked it that way — not useful).
  2. A remote-fetch path: download the entire bundle (lib/jsonc.sh, install.sh, agents/, skills/, command/, tui.json, AGENTS.md, codegraph/, openmillas) to a temp dir and set BUNDLE_DIR=<tmpdir>. This is what the spec's §2.3 already calls for — but it requires install.sh to reach that point, which it currently cannot because of the SCRIPT_DIR crash at line 24.

Concretely, replace the line 24 pattern with something like:

if [[ -n "${BASH_SOURCE[0]:-}" && "${BASH_SOURCE[0]}" != "bash" && "${BASH_SOURCE[0]}" != "/dev/stdin" && -f "${BASH_SOURCE[0]}" ]]; then
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
else
  # stdin invocation: defer SCRIPT_DIR resolution until after the bundle fetch,
  # or default to /tmp if the user has set BUNDLE_DIR explicitly.
  SCRIPT_DIR="${BUNDLE_DIR:-}"
fi

And then early in cmd_install, if BUNDLE_DIR is empty, fetch the bundle from $REMOTE_RAW_BASE into a temp dir and set BUNDLE_DIR to that. This makes the curl | bash UX work.

Apply the same pattern to workspace/opencode/openmillas (line 28-ish) so the post-install CLI also doesn't crash if someone runs it from a piped context.

Verification

After fix, the following must succeed on a Linux box:

$ curl -fsSL https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode/install.sh | bash -s -- --yes --no-interaction
Welcome to openmillas 0.3.0 — installing to /root/.config/opencode
[openmillas] downloaded bundle from https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode
[openmillas] copied bundle to /root/.config/opencode
... (full install flow) ...
✓ Done.

The existing bash workspace/opencode/install.sh (local checkout) UX must continue to work — the test harness at workspace/opencode/tests/test_install.sh covers that.

Severity

High. This is the only documented install UX for end users; the existing deploy.sh --install flow is targeted at maintainers. Shipping without fixing this means every curl | bash install fails on the first line.

  • workspace/opencode/install.sh line 24: SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)".
  • workspace/opencode/openmillas line ~28: same pattern.
  • PLAN_installer.md §10 step 5 mentions "Backwards compatibility note: install.sh works on a curl | bash invocation" — this was incorrect; the file does not currently handle the stdin case.
  • Forgejo issue #3 (analyzer+architect parallel dispatch bug) is unrelated but also affects how we deliver this fix.
## Summary When invoked as `curl -fsSL <url>/workspace/opencode/install.sh | bash`, `install.sh` fails immediately with `bash: línea 24: BASH_SOURCE[0]: variable sin asignar`. This breaks the documented one-line install UX entirely (the entire `openmillas install` value proposition). ## Reproduction ```bash $ curl -fsSL https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode/install.sh | bash bash: línea 24: BASH_SOURCE[0]: variable sin asignar bash: línea 26: /home/borja/lib/jsonc.sh: No Existe el fichero o el directorio curl: (23) Failure writing output to destination, passed 4096 returned 448 ``` (Confirmed on a public-read clone of the repo today; the URL resolves, the script downloads, but bash exits with the errors above before any install logic runs.) ## Expected `curl ... | bash` should run the installer interactively (or non-interactively if `--yes --no-interaction` are appended), with a graceful fallback when the bundle files are not co-located with the install script (because stdin-installed scripts have no on-disk SCRIPT_DIR). ## Actual Bash exits at line 24 with `BASH_SOURCE[0]: variable sin asignar`, the `source "$SCRIPT_DIR/lib/jsonc.sh"` on line 26 fails because `SCRIPT_DIR` resolved to the wrong path (the script's stderr ended up at the user's $HOME), and curl's `Failure writing output to destination` shows the pipe was closed prematurely. Three symptoms of one bug. ## Root cause `install.sh` line 24 (and line 28 in `openmillas`) compute `SCRIPT_DIR` from `BASH_SOURCE[0]`: ```bash SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ``` When a script is read from stdin (the `curl | bash` case), bash does not populate `BASH_SOURCE` for the script being executed. `BASH_SOURCE[0]` is unset, `dirname` of an empty string returns `.`, the `cd ... && pwd` resolves to `$HOME` (wherever the user's bash happens to start), and then `source "$SCRIPT_DIR/lib/jsonc.sh"` tries `/home/<user>/lib/jsonc.sh` which does not exist. The pattern works for `bash workspace/opencode/install.sh` because the script is a real file on disk and `BASH_SOURCE[0]` is the path to it. It fails for `curl | bash` because there is no file. This affects `install.sh` and `openmillas` (same pattern), and it is the only `curl | bash` UX we ship. ## Suggested fix Make `SCRIPT_DIR` resolution tolerant of stdin-sourced scripts. When `BASH_SOURCE[0]` is unset or empty, fall back to: 1. `$0` (may be `bash` itself if the user's shell invoked it that way — not useful). 2. A remote-fetch path: download the entire bundle (lib/jsonc.sh, install.sh, agents/, skills/, command/, tui.json, AGENTS.md, codegraph/, openmillas) to a temp dir and set `BUNDLE_DIR=<tmpdir>`. This is what the spec's §2.3 already calls for — but it requires `install.sh` to reach that point, which it currently cannot because of the `SCRIPT_DIR` crash at line 24. Concretely, replace the line 24 pattern with something like: ```bash if [[ -n "${BASH_SOURCE[0]:-}" && "${BASH_SOURCE[0]}" != "bash" && "${BASH_SOURCE[0]}" != "/dev/stdin" && -f "${BASH_SOURCE[0]}" ]]; then SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" else # stdin invocation: defer SCRIPT_DIR resolution until after the bundle fetch, # or default to /tmp if the user has set BUNDLE_DIR explicitly. SCRIPT_DIR="${BUNDLE_DIR:-}" fi ``` And then early in `cmd_install`, if `BUNDLE_DIR` is empty, fetch the bundle from `$REMOTE_RAW_BASE` into a temp dir and set `BUNDLE_DIR` to that. This makes the `curl | bash` UX work. Apply the same pattern to `workspace/opencode/openmillas` (line 28-ish) so the post-install CLI also doesn't crash if someone runs it from a piped context. ## Verification After fix, the following must succeed on a Linux box: ```bash $ curl -fsSL https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode/install.sh | bash -s -- --yes --no-interaction Welcome to openmillas 0.3.0 — installing to /root/.config/opencode [openmillas] downloaded bundle from https://git.millaredos.com/MillasDev/memory/raw/branch/main/workspace/opencode [openmillas] copied bundle to /root/.config/opencode ... (full install flow) ... ✓ Done. ``` The existing `bash workspace/opencode/install.sh` (local checkout) UX must continue to work — the test harness at `workspace/opencode/tests/test_install.sh` covers that. ## Severity High. This is the **only** documented install UX for end users; the existing `deploy.sh --install` flow is targeted at maintainers. Shipping without fixing this means every `curl | bash` install fails on the first line. ## Related - `workspace/opencode/install.sh` line 24: `SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"`. - `workspace/opencode/openmillas` line ~28: same pattern. - `PLAN_installer.md` §10 step 5 mentions "Backwards compatibility note: `install.sh` works on a `curl | bash` invocation" — this was incorrect; the file does not currently handle the stdin case. - Forgejo issue #3 (analyzer+architect parallel dispatch bug) is unrelated but also affects how we deliver this fix.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MillasDev/memory#5
No description provided.