From f54f40b6b68c1cfc3f79c43d824588e4d97e1ab9 Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sun, 23 Aug 2026 10:38:43 +0000 Subject: [PATCH] Install the Stripe CLI alongside the commands The payment work needs the Stripe CLI on every box, and "install the Stripe CLI first" is the setup step that quietly never happens. The installer now fetches the official binary from stripe/stripe-cli. It follows the codeburn precedent: vendored under vendor/stripe so the name exists once, then linked into the prefix. A stripe already on PATH from a package manager wins and nothing is linked over it. The step runs after the commands are linked and warns instead of dying. An unknown architecture, a GitHub outage or a rate-limited API lookup should not fail an install that has otherwise already succeeded, so each of those paths says why and returns. The version falls back to the last one verified against this installer when the API cannot be reached. The published sha256 is checked. It comes from the same host as the tarball so it is not a supply-chain guarantee, but it catches the truncated download that this will actually see. Verified on linux/x86_64: downloads 1.50.4, links it, `stripe --version` runs, and a second run is a no-op. Co-Authored-By: Claude Opus 5 (1M context) --- README.md | 10 ++++ install.sh | 136 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/README.md b/README.md index 919da9a..47ca7cf 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,16 @@ every command into `~/.local/bin`. `CLI_TOOLS_HOME` and `CLI_TOOLS_PREFIX` override both. If a checkout already owns these command names, the installer updates *that* one rather than cloning a second copy beside it. +The installer also puts the official [Stripe CLI](https://docs.stripe.com/cli) +on PATH, because the payment work needs it on every box and installing it by +hand is the setup step that never happens. It is vendored under +`~/.local/share/cli-tools/vendor/stripe` and linked into `~/.local/bin`, so the +name exists once; a `stripe` already on PATH from somewhere else is left alone. +`STRIPE_CLI_VERSION` pins a version, `CLI_TOOLS_SKIP_STRIPE=1` skips it. It +downloads the release tarball and checks the published sha256, and if any of +that fails it warns and moves on rather than failing an install that otherwise +worked. Authenticate it once with `stripe login`. + With moshcode on the box, the same thing: ```sh diff --git a/install.sh b/install.sh index abd1dd3..7aa65a5 100755 --- a/install.sh +++ b/install.sh @@ -13,6 +13,8 @@ # CLI_TOOLS_REPO clone URL # CLI_TOOLS_BRANCH branch to track (default master) # CLI_TOOLS_FORCE set to 1 to take over links owned by another checkout +# CLI_TOOLS_SKIP_STRIPE set to 1 to skip the Stripe CLI +# STRIPE_CLI_VERSION pin the Stripe CLI (default: latest release) set -eu @@ -101,9 +103,143 @@ LINK_ARGS="" # shellcheck disable=SC2086 CLI_TOOLS_PREFIX="$PREFIX" node "$HOME_DIR/scripts/install-links.mjs" $LINK_ARGS +# ── Stripe CLI ─────────────────────────────────────────────────────────────── +# +# Not one of this repo's commands: it is the official binary from +# stripe/stripe-cli. It lives here because the payment work needs it on every +# box, and "install the Stripe CLI first" is exactly the setup step that +# quietly never happens. +# +# Vendored under $HOME_DIR/vendor/stripe for the same reason codeburn is: the +# name should exist once. If some other stripe is already on PATH, that one is +# left alone and nothing is linked over it. +# +# This runs AFTER the commands are linked, and warns rather than dying. A +# GitHub outage or an unknown architecture should not fail an install that has +# otherwise already succeeded. + +# Last release verified against this installer. Used when the version cannot be +# resolved from the API, which is mostly rate limiting on a shared IP. +STRIPE_FALLBACK_VERSION="1.50.4" + +stripe_platform() { + # Asset names look like stripe_1.50.4_linux_x86_64.tar.gz — note that the + # macOS ones say mac-os, and that arm64 is arm64 on both. + os="$(uname -s)" + arch="$(uname -m)" + case "$os" in + Linux) os="linux" ;; + Darwin) os="mac-os" ;; + *) return 1 ;; + esac + case "$arch" in + x86_64 | amd64) arch="x86_64" ;; + aarch64 | arm64) arch="arm64" ;; + *) return 1 ;; + esac + printf '%s_%s\n' "$os" "$arch" +} + +stripe_sha256() { + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$1" | cut -d' ' -f1 + elif command -v shasum >/dev/null 2>&1; then + shasum -a 256 "$1" | cut -d' ' -f1 + else + return 1 + fi +} + +install_stripe() { + [ "${CLI_TOOLS_SKIP_STRIPE:-0}" = "1" ] && return 0 + + vendor="$HOME_DIR/vendor/stripe" + + # Someone else's stripe on PATH wins. Ours would only shadow it depending on + # the order of two directories, which is not a thing to leave to chance. + existing="$(command -v stripe 2>/dev/null || true)" + if [ -n "$existing" ] && [ "$existing" != "$PREFIX/stripe" ]; then + say " stripe already on PATH at $existing — left alone." + return 0 + fi + + command -v curl >/dev/null 2>&1 || { say " skipped: curl is required."; return 0; } + command -v tar >/dev/null 2>&1 || { say " skipped: tar is required."; return 0; } + + platform="$(stripe_platform)" || { + say " skipped: no Stripe CLI build for $(uname -s)/$(uname -m)." + return 0 + } + + version="${STRIPE_CLI_VERSION:-}" + if [ -z "$version" ]; then + # Plain grep/sed rather than jq, which is not a dependency anywhere else + # in this installer. + version="$(curl -fsSL https://api.github.com/repos/stripe/stripe-cli/releases/latest 2>/dev/null \ + | sed -n 's/.*"tag_name": *"v\{0,1\}\([^"]*\)".*/\1/p' | head -1)" + [ -n "$version" ] || version="$STRIPE_FALLBACK_VERSION" + fi + version="${version#v}" + + # Already at the wanted version? Then there is nothing to download. + if [ -x "$vendor/stripe" ] && [ "$($vendor/stripe --version 2>/dev/null | sed -n 's/.*version \([0-9.]*\).*/\1/p')" = "$version" ]; then + say " stripe $version already installed." + return 0 + fi + + case "$platform" in + mac-os_*) sums="stripe-mac-checksums.txt" ;; + *) sums="stripe-linux-checksums.txt" ;; + esac + tarball="stripe_${version}_${platform}.tar.gz" + base="https://github.com/stripe/stripe-cli/releases/download/v${version}" + + tmp="$(mktemp -d)" || { say " skipped: could not create a temp dir."; return 0; } + + if ! curl -fsSL "$base/$tarball" -o "$tmp/$tarball"; then + say " skipped: could not download $tarball." + rm -rf "$tmp" + return 0 + fi + + # The checksum comes from the same host as the tarball, so this is not a + # supply-chain guarantee — it catches a truncated or corrupted download, + # which is the failure this actually sees. + if curl -fsSL "$base/$sums" -o "$tmp/sums.txt" 2>/dev/null; then + want="$(grep " $tarball\$" "$tmp/sums.txt" 2>/dev/null | cut -d' ' -f1)" + got="$(stripe_sha256 "$tmp/$tarball" 2>/dev/null || true)" + if [ -n "$want" ] && [ -n "$got" ] && [ "$want" != "$got" ]; then + say " skipped: checksum mismatch on $tarball." + rm -rf "$tmp" + return 0 + fi + fi + + if ! tar -xzf "$tmp/$tarball" -C "$tmp" stripe 2>/dev/null; then + say " skipped: could not extract stripe from $tarball." + rm -rf "$tmp" + return 0 + fi + + mkdir -p "$vendor" + # mv onto the old binary rather than writing in place: a running stripe + # keeps its inode, and the replacement is atomic. + mv "$tmp/stripe" "$vendor/stripe" + chmod +x "$vendor/stripe" + rm -rf "$tmp" + + mkdir -p "$PREFIX" + ln -sf "$vendor/stripe" "$PREFIX/stripe" + say " stripe $version -> $PREFIX/stripe" +} + +say "Installing the Stripe CLI" +install_stripe + # install-links.mjs already warns when $PREFIX is not on PATH, so there is # deliberately no second warning here. say "" say "Installed. Try:" say " cli-tools list # what landed, and what is on PATH" say " cli-tools aliases --install # the moshcode pit aliases" +say " stripe login # authenticate the Stripe CLI"