Skip to content

feat: Add a curl | bash bootstrap installer - #40

Merged
bougyman merged 1 commit into
mainfrom
feat/bootstrap-installer
Oct 5, 2026
Merged

bougyman merged 1 commit into
mainfrom
feat/bootstrap-installer

Conversation

@ruby-automation

Copy link
Copy Markdown
Collaborator

Closes #39.

curl -fsSL https://raw.githubusercontent.com/rubyists/sv-helper/main/bootstrap/install.sh | bash
curl -fsSL https://raw.githubusercontent.com/rubyists/sv-helper/main/bootstrap/install.sh | VERSION=v4.2.0 bash
curl -fsSL https://raw.githubusercontent.com/rubyists/sv-helper/main/bootstrap/install.sh | bash -s -- --prefix /opt/sv

bootstrap/install.sh downloads a published release, verifies it, and runs the install.sh inside it. Nothing about how releases are built or published changes. It lives under bootstrap/ because the root install.sh is the installer inside the archive.

What it does

  1. Resolves the tag once: VERSION (v4.2.0 or 4.2.0), or the tag GitHub's /releases/latest redirects to.
  2. Downloads SHA256SUMS and sv-helper-linux.tar.gz or sv-helper-darwin.tar.gz, over HTTPS only, redirects included.
  3. Checks the archive against SHA256SUMS.
  4. If packslip is on PATH, verifies the signature against .github/workflows/main.yaml@. Otherwise it says the signature wasn't checked.
  5. Unpacks the archive and runs its install.sh "$@", with the environment passed through, so PREFIX and DESTDIR work.

Exit statuses: 2 missing tool, 3 unsupported OS, 4 no such or invalid release, 5 download failed, 6 verification failed, 7 unpack failed, 8 no temp dir. Otherwise it exits with install.sh's own status. Nothing runs until the last line calls main, so a truncated download does nothing.

Written in bash in your style: no semicolons, no set -euo pipefail, and every command checked with if, if !, or a || { block.

Checked against the real releases

Run through a pipe, as users will run it, against GitHub on this host:

  • latest (v4.2.0) installed all 11 commands, and svls --version reports 4.2.0
  • VERSION=4.0.0 passed real packslip signature verification and installed
  • VERSION=v3.5.0, which has no assets, gets a 404 that's named, exit 4
  • VERSION=v.0.1 is refused before any download, exit 4
  • the temporary directory was gone afterwards

Tests

test/bootstrap.bats (15 tests) installs from a payload built with ci/build_release_payload.sh and served over file://, with no network. Covered:

  • a pinned version, a v-less version, and PREFIX from the environment
  • a missing release, and an invalid version
  • a tampered archive, and an archive missing from SHA256SUMS
  • a signature that passes, one that fails, and a missing bundle (through a fake packslip)
  • an unsupported OS (through a fake uname)
  • install.sh's own exit status
  • truncation before main, and truncation inside it
  • cleanup, and no privilege escalation

Resolving "latest" needs GitHub's redirect, so the suite doesn't cover it. I checked it by hand above. make test passes locally (135 tests).

🤖 Generated with Claude Code

    curl -fsSL https://raw.githubusercontent.com/rubyists/sv-helper/main/bootstrap/install.sh | bash

bootstrap/install.sh installs a published release. It resolves VERSION
(v4.2.0 or 4.2.0), or the latest tag, once. It downloads that release's
archive for Linux or macOS and checks it against the release's own
SHA256SUMS. When packslip is installed, it also verifies the signature
against the release workflow's identity. Then it runs the install.sh
inside the archive with any arguments after `bash -s --`, and with the
environment, so PREFIX and DESTDIR work as they do there.

Nothing about how releases are built changes. A mismatch, a missing
checksum entry or a failed signature installs nothing. A release that
doesn't exist, an unsupported OS and a missing tool are each named, with
their own exit status. A failing install.sh exits with its own status.
It never escalates privilege. Downloads are HTTPS only, redirects
included. The temporary directory goes on success, failure or
interrupt. Nothing runs until the last line calls main, so a truncated
download does nothing.

test/bootstrap.bats installs from a payload built with
ci/build_release_payload.sh and served over file://, with no network.
A fake packslip and a fake uname cover the signature and platform
paths.

Closes #39

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@bougyman
bougyman merged commit f45a625 into main Oct 5, 2026
2 checks passed
@bougyman
bougyman deleted the feat/bootstrap-installer branch October 5, 2026 23:26
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.

Add a curl | bash bootstrap installer

2 participants