Skip to content

Add a one-line curl install - #59

Open
leogdion wants to merge 1 commit into
release-1.0.3from
54-curl-install
Open

Add a one-line curl install#59
leogdion wants to merge 1 commit into
release-1.0.3from
54-curl-install

Conversation

@leogdion

@leogdion leogdion commented Aug 21, 2026

Copy link
Copy Markdown
Member

The README's curl path was a 12-line copy-paste blob: mktemp, curl, mv, chmod, then a second guarded block repeating the whole dance for the agents template. Issue #54 asks for the conventional shape — one line:

curl -fsSL https://raw.githubusercontent.com/brightdigit/git-trees/main/install.sh | bash

No-repo detection

That one-liner requires install.sh to work with nothing around it. It previously did:

SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
[ -f "$SRC/git-trees" ] || { echo "install.sh: git-trees not found in $SRC" >&2; exit 1; }

Piped into bash, $SRC is meaningless and it hard-fails. Now, when the expected files are not next to the script, it downloads git-trees and AGENTS.md.template into a mktemp -d and proceeds exactly as before, with a trap removing the temp directory on exit.

Two set -u details the piped path exposes: piped bash has no BASH_SOURCE and no $1, and a bare reference to either is fatal under set -u. The first showed up only when actually piping — a copied-file test still has BASH_SOURCE — as bash: line 20: BASH_SOURCE[0]: unbound variable. Both are defaulted now, and a test pipes the script on stdin to keep it that way.

TREES_DEST override

A piped script receives no positional arguments, so ./install.sh /usr/local/bin has no equivalent when piping — there is nowhere for the argument to go. Hence an env var:

TREES_DEST=/usr/local/bin curl -fsSL https://raw.githubusercontent.com/brightdigit/git-trees/main/install.sh | bash

The positional argument still takes precedence (DEST="${1:-${TREES_DEST:-$HOME/.local/bin}}"), so the clone path is unaffected and both are tested.

Download verification

Downloads go through curl or wget, whichever is present, with a clear error if neither is. Each download is verified non-empty before anything is installed. curl -fsSL fails on HTTP errors, and its status is checked rather than assumed — but it still leaves a zero-byte file behind on some failures, and that case is the one an exit-status check alone misses, so it has its own fixture. An empty or truncated git-trees installed onto PATH is the worst outcome here. A failed git-trees download aborts; a failed template download only warns, since the template is optional and git-trees already warns without it.

Clone path unchanged

Same ~/.local/bin default, same install -m 0755, same PATH warning, same trailing hints, no download attempted. Verified against a redirected HOME, with the existing tests still passing untouched.

One deliberate change: the template guard moves from [ ! -f ] to [ ! -e ] && [ ! -L ]. That matches the no-clobber-a-broken-symlink behavior the README already documented and that root --agents implements; previously a broken symlink there would have been written through.

Tests

The install.sh section of tests/smoke.sh is extended and a no-repo bootstrap section added — 31 new assertions, and the suite stays network-free: TREES_BASE_URL points at a file:// fixture, so the real download branch runs without touching the network and cannot silently no-op offline. The clean section remains last. Coverage: piped-on-stdin invocation, the wget fallback on a PATH built without curl (macOS ships /usr/bin/curl, so proving the fallback runs means excluding the real one), the neither-downloader error, a zero-byte body, a missing script failing loudly and installing nothing, a missing template warning while the binary still installs, no-clobber on rerun, and temp-dir cleanup.

Each new assertion was mutation-tested — the guard, the trap, and the BASH_SOURCE default were each reverted in turn to confirm the corresponding test actually fails. Two early drafts that passed against a broken implementation were replaced (macOS mktemp -d ignores TMPDIR unless the template spells it out, which is why install.sh now does).

Docs

  • ## Install — one-liner replaces the blob; clone-and-run stays the recommended full install; both still install the script and seed the agents template.
  • TREES_DEST documented for the piped path and added to the ## Environment table.
  • main stays pinned, and is still described as the stable release.
  • Pre-existing fix: ## Configuration opened with "All three variables are optional" while ## Environment listed five. It now refers to the table instead of hardcoding a count that drifts.

Edits are scoped to install/configuration prose to keep merges clean with the concurrent Homebrew (#49), completions (#51), and subcommand (#50, #55) PRs.

Verification

$ bash -n git-trees && bash -n install.sh && bash -n tests/smoke.sh
(exit 0)

$ shellcheck -s bash git-trees install.sh tests/smoke.sh
(exit 0, no output)

$ tests/smoke.sh ./git-trees
smoke tests passed          # 231 assertions, 0 failures (exit 0)

End-to-end against a file:// URL with HOME and TREES_DEST in temp dirs: the script lands executable (-rwxr-xr-x, git trees help runs), the template is seeded byte-identical to AGENTS.md.template, and a rerun after editing that template leaves the edit intact.

Closes #54

🤖 Generated with Claude Code


Release coordination (v1.0.3)

One of five PRs into release-1.0.3 (#56 prune, #58 sync, #60 completions, #59 curl install, #57 Homebrew). All five are green on CI (smoke on Linux + macOS).

Suggested merge order: #56#58#60#59#57. This PR merges cleanly against every other branch.

⚠️ Please look closely: this changes existing behavior

The AGENTS.md template guard moved from [ ! -f "$CFG/AGENTS.md" ] to [ ! -e ] && [ ! -L ]. That is a clone-path behavior change, not just an addition: previously a broken symlink at that path would be written through, and now it is treated as occupied. This matches what the README already documents and what root --agents implements via _seed_agents, but it was outside the strict scope of "add a curl install" — flagging it so it is a deliberate decision rather than a silent one.

Also worth a look: BASH_SOURCE[0]:-$0 in the SRC assignment. Piped bash has no BASH_SOURCE and no $1, and under set -u a bare reference is fatal — so without this the whole feature would fail on the very path it exists to add. Found by actually piping the script; a file-copy test cannot catch it, because a copied script does have BASH_SOURCE.

Note: CodeRabbit skipped all five — "reviews are disabled for this base branch." These have not had automated review; that would come when release-1.0.3 merges to main.

The CHANGELOG is deliberately excluded from every PR — it needs the merged PR URLs, so it lands as one commit on release-1.0.3 before tagging v1.0.3.

The README's curl path was a 12-line copy-paste blob that mktemp'd, curled,
moved, chmod'd, then repeated the whole dance for the agents template. Replace
it with the conventional one-liner:

    curl -fsSL https://raw.githubusercontent.com/brightdigit/git-trees/main/install.sh | bash

That requires install.sh to work with no repo around it. It now detects the
case where the expected files are not next to the script, downloads git-trees
and AGENTS.md.template into a mktemp -d, and proceeds exactly as before, with a
trap cleaning up the temp directory on exit.

Downloads go through curl or wget, whichever is present, and each is verified
non-empty before anything is installed — curl -fsSL fails on HTTP errors but
still leaves a zero-byte file behind, and an empty or truncated git-trees
installed onto PATH is the worst outcome here.

A piped script receives no positional arguments, so TREES_DEST is the only way
to choose a destination on that path. The positional argument still wins for
the clone path, which is otherwise unchanged: same ~/.local/bin default, same
install -m 0755, same template no-overwrite guard, same PATH warning and hints.

Piped bash has neither BASH_SOURCE nor $1, and set -u makes a bare reference to
either fatal, so the source-directory probe defaults them. The template guard
also moves from -f to -e/-L, matching the no-clobber behavior the README
already documented for a broken symlink.

Also fixes the README's "All three variables are optional" against an
Environment table that listed five, and adds TREES_DEST to that table.

Closes #54

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5d3eb6db-b193-465f-aa8e-8d6f3fd2a666

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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