From cca717cabc9cc4036982b22907d1367bc0d0a25f Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 21 May 2026 13:49:43 +0200 Subject: [PATCH 1/4] feat(installer): replace init with version-aware router, preserve legacy Rename the old init script to init-legacy (unchanged) and introduce a thin version-aware router as the new init entry point. - Versions >= 0.31.0 (first cargo-dist release) and 'latest' are routed to the cargo-dist installer on GitHub Releases. - Older versions are delegated to init-legacy, preserving full backward compatibility including the sh -s -- VERSION interface. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/docs.yml | 4 +- website/init | 483 +++---------------------------------- website/init-legacy | 465 +++++++++++++++++++++++++++++++++++ 3 files changed, 497 insertions(+), 455 deletions(-) create mode 100755 website/init-legacy diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 0d0886d91..4c6a24cb9 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -28,8 +28,8 @@ jobs: - name: Build book run: mdbook build book/ - - name: Copy init script - run: cp website/init book/book/init + - name: Copy init scripts + run: cp website/init website/init-legacy book/book/ - name: Setup Pages uses: actions/configure-pages@v5 diff --git a/website/init b/website/init index 37c7ce048..cf46c332c 100755 --- a/website/init +++ b/website/init @@ -1,465 +1,42 @@ -#!/usr/bin/env bash +#!/usr/bin/env sh # Copyright (c) 2019 ETH Zurich # SPDX-License-Identifier: Apache-2.0 OR MIT -# Author: Andreas Kurth -# This is just a little script that can be downloaded from the internet to -# install Bender. It does platform detection, downloads the specified or the -# newest release package, and extracts it. -# Code and concept derived from the rustup installer. +# Installer entry point for Bender. +# Routes to the cargo-dist installer (v0.32.0+) or the legacy installer for +# older versions. The interface is unchanged: pass a version as the first +# argument to pin to a specific release, or omit it for the latest release. +# +# Usage: +# curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh +# curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- 0.31.0 -set -u +set -eu -readonly GITHUB_API_BENDER_ROOT='https://api.github.com/repos/pulp-platform/bender' -readonly GITHUB_API_HEADER='Accept: application/vnd.github.v3+json' +REPO="pulp-platform/bender" +# v0.32.0 is the first release with cargo-dist installers (all versions are 0.x.y) +# TODO: revisit this comparison if bender ever reaches v1.x +CARGO_DIST_MIN_MINOR=32 -usage() { - cat 1>&2 </dev/null - mv "$_tmpdir/bender" . || err "Failed to move executable to target directory!" - # Remove temporary directory. - rm -rf "$_tmpdir" || echo "Warning: Failed to clean up temporary directory." - - echo "Successfully installed $(./bender -V) in '$(pwd)'." -} - -get_bitness() { - need_cmd head - # Architecture detection without dependencies beyond coreutils. - # ELF files start out "\x7fELF", and the following byte is - # 0x01 for 32-bit and - # 0x02 for 64-bit. - # The printf builtin on some shells like dash only supports octal - # escape sequences, so we use those. - local _current_exe_head - _current_exe_head=$(head -c 5 /proc/self/exe ) - if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then - echo 32 - elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then - echo 64 - else - err "unknown platform bitness" - fi +# Returns 0 (true) if VERSION should use the cargo-dist installer +uses_cargo_dist() { + [ "$VERSION" = "latest" ] && return 0 + minor=$(printf '%s' "$VERSION" | cut -d. -f2) + [ "$minor" -ge "$CARGO_DIST_MIN_MINOR" ] } -get_endianness() { - local cputype=$1 - local suffix_eb=$2 - local suffix_el=$3 - - # detect endianness without od/hexdump, like get_bitness() does. - need_cmd head - need_cmd tail - - local _current_exe_endianness - _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" - if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then - echo "${cputype}${suffix_el}" - elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then - echo "${cputype}${suffix_eb}" +if uses_cargo_dist; then + if [ "$VERSION" = "latest" ]; then + URL="https://github.com/$REPO/releases/latest/download/bender-installer.sh" else - err "unknown platform endianness" - fi -} - -get_architecture() { - local _ostype _cputype _bitness _arch - _ostype="$(uname -s)" - _cputype="$(uname -m)" - - if [ "$_ostype" = Linux ]; then - if [ "$(uname -o)" = Android ]; then - _ostype=Android - fi - fi - - if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then - # Darwin `uname -m` lies - if sysctl hw.optional.x86_64 | grep -q ': 1'; then - _cputype=x86_64 - fi + URL="https://github.com/$REPO/releases/download/v$VERSION/bender-installer.sh" fi - - case "$_ostype" in - - Android) - _ostype=linux-android - ;; - - Linux) - _ostype=linux-gnu - _bitness=$(get_bitness) - ;; - - FreeBSD) - _ostype=freebsd - ;; - - NetBSD) - _ostype=netbsd - ;; - - DragonFly) - _ostype=dragonfly - ;; - - Darwin) - _ostype=apple-darwin - ;; - - MINGW* | MSYS* | CYGWIN*) - _ostype=pc-windows-gnu - ;; - - *) - err "unrecognized OS type: $_ostype" - ;; - - esac - - case "$_cputype" in - - i386 | i486 | i686 | i786 | x86) - _cputype=i686 - ;; - - xscale | arm) - _cputype=arm - if [ "$_ostype" = "linux-android" ]; then - _ostype=linux-androideabi - fi - ;; - - armv6l) - _cputype=arm - if [ "$_ostype" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - - armv7l | armv8l) - _cputype=armv7 - if [ "$_ostype" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - - aarch64) - _cputype=aarch64 - ;; - - arm64) - _cputype=aarch64 - ;; - - x86_64 | x86-64 | x64 | amd64) - _cputype=x86_64 - ;; - - mips) - _cputype=$(get_endianness mips '' el) - ;; - - mips64) - if [ "$_bitness" -eq 64 ]; then - # only n64 ABI is supported for now - _ostype="${_ostype}abi64" - _cputype=$(get_endianness mips64 '' el) - fi - ;; - - ppc) - _cputype=powerpc - ;; - - ppc64) - _cputype=powerpc64 - ;; - - ppc64le) - _cputype=powerpc64le - ;; - - s390x) - _cputype=s390x - ;; - - *) - err "unknown CPU type: $_cputype" - - esac - - # Detect 64-bit linux with 32-bit userland - if [ "${_ostype}" = linux-gnu ] && [ "${_bitness}" -eq 32 ]; then - case $_cputype in - x86_64) - _cputype=i686 - ;; - mips64) - _cputype=$(get_endianness mips '' el) - ;; - powerpc64) - _cputype=powerpc - ;; - esac - fi - - # Detect armv7 but without the CPU features Rust needs in that build, - # and fall back to arm. - # See https://github.com/rust-lang/rustup.rs/issues/587. - if [ "$_ostype" = "linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then - if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then - # At least one processor does not have NEON. - _cputype=arm - fi - fi - - _arch="${_cputype}-${_ostype}" - - RETVAL="$_arch" -} - -get_distribution() { - local _vendor="" - local _release="" - if check_cmd lsb_release; then - _vendor=$(lsb_release -i | cut -f 2 | tr '[:upper:]' '[:lower:]') - _release=$(lsb_release -r | cut -f 2) - else - _vendor=$(cat /etc/os-release | sed -n -e "s/^ID=\(.*\)$/\1/p" | sed "s/\"//g") - _release=$(cat /etc/os-release | sed -n -e "s/^VERSION_ID=\(.*\)$/\1/p" | sed "s/\"//g") - fi - if [ "$_vendor" = "centos" ] && [ "$(echo "$_release" | cut -d. -f3)" -lt 1708 ]; then - say "Warning: CentOS older than 7.4.1708 detected, falling back to the latter." - _release="7.4.1708" - fi - if [ "$_vendor" = "redhatenterprise" ]; then - _vendor="rhel" - fi - RETVAL="$_vendor$_release" -} - -get_download_url() { - local _suburl="$1" - local _platform="$2" - RETVAL="$(downloader "$GITHUB_API_BENDER_ROOT/releases/$_suburl" | \ - grep -Eo " \"browser_download_url\": \".*?bender-.*-$_platform\.tar\.gz\"" | \ - grep -Eo "http.*?bender-.*-$_platform\.tar\.gz")" -} - -get_asset_url() { - local _version="$1" - local _platform="$2" - if [ $_version = "latest" ]; then - local _url="latest" - else - local _url="tags/v$_version" - fi - get_download_url "$_url" "$_platform" - if [ -z "$RETVAL" ]; then - say "Warning: No release for platform '$_platform' version '$_version' found, using latest." - get_download_url "latest" "$_platform" - fi - if [ -z "$RETVAL" ]; then - case "$_platform" in - x86_64-linux-gnu*) - say "Warning: Latest release not available for platform '$_platform', falling back to 'x86_64-linux-gnu'." - _fallback="x86_64-linux-gnu" - ;; - arm64-linux-gnu*) - say "Warning: Latest release not available for platform '$_platform', falling back to 'arm64-linux-gnu'." - _fallback="arm64-linux-gnu" - ;; - *) - err "Error: Latest release not available for platform '$_platform'!" - ;; - esac - get_download_url "latest" "$_fallback" - fi - assert_nz "$RETVAL" assets -} - -say() { - printf 'bender-init: %s\n' "$1" -} - -err() { - say "$1" >&2 - exit 1 -} - -need_cmd() { - if ! check_cmd "$1"; then - err "need '$1' (command not found)" - fi -} - -check_cmd() { - command -v "$1" > /dev/null 2>&1 -} - -assert_nz() { - if [ -z "$1" ]; then err "assert_nz $2"; fi -} - -# Run a command that should never fail. If the command fails execution -# will immediately terminate with an error showing the failing -# command. -ensure() { - if ! "$@"; then err "command failed: $*"; fi -} - -# This is just for indicating that commands' results are being -# intentionally ignored. Usually, because it's being executed -# as part of error handling. -ignore() { - "$@" -} - -# This wraps curl or wget. Try curl first, if not installed, -# use wget instead. -downloader() { - local _dld - if check_cmd curl; then - _dld=curl - elif check_cmd wget; then - _dld=wget - else - _dld='curl or wget' # to be used in error message of need_cmd - fi - - if [ "$1" = --check ]; then - need_cmd "$_dld" - elif [ "$_dld" = curl ]; then - #local _curl="curl -H '$GITHUB_API_HEADER'" - local _curl="curl" - if ! check_help_for curl --proto --tlsv1.2; then - echo "Warning: Not forcing TLS v1.2, this is potentially less secure" - else - _curl="$_curl --proto =https --tlsv1.2" - fi - _curl="$_curl --silent --show-error --fail --location $1" - if [ "$#" -ge 2 ]; then - _curl="$_curl --output $2" - fi - $_curl 2>/dev/null - elif [ "$_dld" = wget ]; then - local _wget="wget -H '$GITHUB_API_HEADER'" - if ! check_help_for wget --https-only --secure-protocol; then - echo "Warning: Not forcing TLS v1.2, this is potentially less secure" - else - _wget="$_wget --https-only --secure-protocol=TLSv1_2" - fi - _wget="$_wget $1" - if [ "$#" -ge 2 ]; then - _wget="$_wget -O $2" - fi - $_wget 2>/dev/null - else - err "Unknown downloader" # should not reach here - fi - return $? -} - -check_help_for() { - local _cmd - local _arg - local _ok - _cmd="$1" - _ok="y" - shift - - if [ "$_cmd" = "curl" ]; then - _cmd="$_cmd --help all" - else - _cmd="$_cmd --help" - fi - - # If we're running on OS-X, older than 10.13, then we always - # fail to find these options to force fallback - if check_cmd sw_vers; then - if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then - # Older than 10.13 - echo "Warning: Detected OS X platform older than 10.13" - _ok="n" - fi - fi - - for _arg in "$@"; do - if ! $_cmd | grep -q -- "$_arg"; then - _ok="n" - fi - done - - test "$_ok" = "y" -} - -main "$@" || exit 1 + curl --proto '=https' --tlsv1.2 -LsSf "$URL" | sh +else + # Delegate to the legacy installer for versions before cargo-dist was introduced + curl --proto '=https' --tlsv1.2 -LsSf \ + "https://pulp-platform.github.io/bender/init-legacy" | sh -s -- "$VERSION" +fi diff --git a/website/init-legacy b/website/init-legacy new file mode 100755 index 000000000..37c7ce048 --- /dev/null +++ b/website/init-legacy @@ -0,0 +1,465 @@ +#!/usr/bin/env bash + +# Copyright (c) 2019 ETH Zurich +# SPDX-License-Identifier: Apache-2.0 OR MIT +# Author: Andreas Kurth + +# This is just a little script that can be downloaded from the internet to +# install Bender. It does platform detection, downloads the specified or the +# newest release package, and extracts it. +# Code and concept derived from the rustup installer. + +set -u + +readonly GITHUB_API_BENDER_ROOT='https://api.github.com/repos/pulp-platform/bender' +readonly GITHUB_API_HEADER='Accept: application/vnd.github.v3+json' + +usage() { + cat 1>&2 </dev/null + mv "$_tmpdir/bender" . || err "Failed to move executable to target directory!" + # Remove temporary directory. + rm -rf "$_tmpdir" || echo "Warning: Failed to clean up temporary directory." + + echo "Successfully installed $(./bender -V) in '$(pwd)'." +} + +get_bitness() { + need_cmd head + # Architecture detection without dependencies beyond coreutils. + # ELF files start out "\x7fELF", and the following byte is + # 0x01 for 32-bit and + # 0x02 for 64-bit. + # The printf builtin on some shells like dash only supports octal + # escape sequences, so we use those. + local _current_exe_head + _current_exe_head=$(head -c 5 /proc/self/exe ) + if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then + echo 32 + elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then + echo 64 + else + err "unknown platform bitness" + fi +} + +get_endianness() { + local cputype=$1 + local suffix_eb=$2 + local suffix_el=$3 + + # detect endianness without od/hexdump, like get_bitness() does. + need_cmd head + need_cmd tail + + local _current_exe_endianness + _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" + if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then + echo "${cputype}${suffix_el}" + elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then + echo "${cputype}${suffix_eb}" + else + err "unknown platform endianness" + fi +} + +get_architecture() { + local _ostype _cputype _bitness _arch + _ostype="$(uname -s)" + _cputype="$(uname -m)" + + if [ "$_ostype" = Linux ]; then + if [ "$(uname -o)" = Android ]; then + _ostype=Android + fi + fi + + if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then + # Darwin `uname -m` lies + if sysctl hw.optional.x86_64 | grep -q ': 1'; then + _cputype=x86_64 + fi + fi + + case "$_ostype" in + + Android) + _ostype=linux-android + ;; + + Linux) + _ostype=linux-gnu + _bitness=$(get_bitness) + ;; + + FreeBSD) + _ostype=freebsd + ;; + + NetBSD) + _ostype=netbsd + ;; + + DragonFly) + _ostype=dragonfly + ;; + + Darwin) + _ostype=apple-darwin + ;; + + MINGW* | MSYS* | CYGWIN*) + _ostype=pc-windows-gnu + ;; + + *) + err "unrecognized OS type: $_ostype" + ;; + + esac + + case "$_cputype" in + + i386 | i486 | i686 | i786 | x86) + _cputype=i686 + ;; + + xscale | arm) + _cputype=arm + if [ "$_ostype" = "linux-android" ]; then + _ostype=linux-androideabi + fi + ;; + + armv6l) + _cputype=arm + if [ "$_ostype" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + + armv7l | armv8l) + _cputype=armv7 + if [ "$_ostype" = "linux-android" ]; then + _ostype=linux-androideabi + else + _ostype="${_ostype}eabihf" + fi + ;; + + aarch64) + _cputype=aarch64 + ;; + + arm64) + _cputype=aarch64 + ;; + + x86_64 | x86-64 | x64 | amd64) + _cputype=x86_64 + ;; + + mips) + _cputype=$(get_endianness mips '' el) + ;; + + mips64) + if [ "$_bitness" -eq 64 ]; then + # only n64 ABI is supported for now + _ostype="${_ostype}abi64" + _cputype=$(get_endianness mips64 '' el) + fi + ;; + + ppc) + _cputype=powerpc + ;; + + ppc64) + _cputype=powerpc64 + ;; + + ppc64le) + _cputype=powerpc64le + ;; + + s390x) + _cputype=s390x + ;; + + *) + err "unknown CPU type: $_cputype" + + esac + + # Detect 64-bit linux with 32-bit userland + if [ "${_ostype}" = linux-gnu ] && [ "${_bitness}" -eq 32 ]; then + case $_cputype in + x86_64) + _cputype=i686 + ;; + mips64) + _cputype=$(get_endianness mips '' el) + ;; + powerpc64) + _cputype=powerpc + ;; + esac + fi + + # Detect armv7 but without the CPU features Rust needs in that build, + # and fall back to arm. + # See https://github.com/rust-lang/rustup.rs/issues/587. + if [ "$_ostype" = "linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then + if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then + # At least one processor does not have NEON. + _cputype=arm + fi + fi + + _arch="${_cputype}-${_ostype}" + + RETVAL="$_arch" +} + +get_distribution() { + local _vendor="" + local _release="" + if check_cmd lsb_release; then + _vendor=$(lsb_release -i | cut -f 2 | tr '[:upper:]' '[:lower:]') + _release=$(lsb_release -r | cut -f 2) + else + _vendor=$(cat /etc/os-release | sed -n -e "s/^ID=\(.*\)$/\1/p" | sed "s/\"//g") + _release=$(cat /etc/os-release | sed -n -e "s/^VERSION_ID=\(.*\)$/\1/p" | sed "s/\"//g") + fi + if [ "$_vendor" = "centos" ] && [ "$(echo "$_release" | cut -d. -f3)" -lt 1708 ]; then + say "Warning: CentOS older than 7.4.1708 detected, falling back to the latter." + _release="7.4.1708" + fi + if [ "$_vendor" = "redhatenterprise" ]; then + _vendor="rhel" + fi + RETVAL="$_vendor$_release" +} + +get_download_url() { + local _suburl="$1" + local _platform="$2" + RETVAL="$(downloader "$GITHUB_API_BENDER_ROOT/releases/$_suburl" | \ + grep -Eo " \"browser_download_url\": \".*?bender-.*-$_platform\.tar\.gz\"" | \ + grep -Eo "http.*?bender-.*-$_platform\.tar\.gz")" +} + +get_asset_url() { + local _version="$1" + local _platform="$2" + if [ $_version = "latest" ]; then + local _url="latest" + else + local _url="tags/v$_version" + fi + get_download_url "$_url" "$_platform" + if [ -z "$RETVAL" ]; then + say "Warning: No release for platform '$_platform' version '$_version' found, using latest." + get_download_url "latest" "$_platform" + fi + if [ -z "$RETVAL" ]; then + case "$_platform" in + x86_64-linux-gnu*) + say "Warning: Latest release not available for platform '$_platform', falling back to 'x86_64-linux-gnu'." + _fallback="x86_64-linux-gnu" + ;; + arm64-linux-gnu*) + say "Warning: Latest release not available for platform '$_platform', falling back to 'arm64-linux-gnu'." + _fallback="arm64-linux-gnu" + ;; + *) + err "Error: Latest release not available for platform '$_platform'!" + ;; + esac + get_download_url "latest" "$_fallback" + fi + assert_nz "$RETVAL" assets +} + +say() { + printf 'bender-init: %s\n' "$1" +} + +err() { + say "$1" >&2 + exit 1 +} + +need_cmd() { + if ! check_cmd "$1"; then + err "need '$1' (command not found)" + fi +} + +check_cmd() { + command -v "$1" > /dev/null 2>&1 +} + +assert_nz() { + if [ -z "$1" ]; then err "assert_nz $2"; fi +} + +# Run a command that should never fail. If the command fails execution +# will immediately terminate with an error showing the failing +# command. +ensure() { + if ! "$@"; then err "command failed: $*"; fi +} + +# This is just for indicating that commands' results are being +# intentionally ignored. Usually, because it's being executed +# as part of error handling. +ignore() { + "$@" +} + +# This wraps curl or wget. Try curl first, if not installed, +# use wget instead. +downloader() { + local _dld + if check_cmd curl; then + _dld=curl + elif check_cmd wget; then + _dld=wget + else + _dld='curl or wget' # to be used in error message of need_cmd + fi + + if [ "$1" = --check ]; then + need_cmd "$_dld" + elif [ "$_dld" = curl ]; then + #local _curl="curl -H '$GITHUB_API_HEADER'" + local _curl="curl" + if ! check_help_for curl --proto --tlsv1.2; then + echo "Warning: Not forcing TLS v1.2, this is potentially less secure" + else + _curl="$_curl --proto =https --tlsv1.2" + fi + _curl="$_curl --silent --show-error --fail --location $1" + if [ "$#" -ge 2 ]; then + _curl="$_curl --output $2" + fi + $_curl 2>/dev/null + elif [ "$_dld" = wget ]; then + local _wget="wget -H '$GITHUB_API_HEADER'" + if ! check_help_for wget --https-only --secure-protocol; then + echo "Warning: Not forcing TLS v1.2, this is potentially less secure" + else + _wget="$_wget --https-only --secure-protocol=TLSv1_2" + fi + _wget="$_wget $1" + if [ "$#" -ge 2 ]; then + _wget="$_wget -O $2" + fi + $_wget 2>/dev/null + else + err "Unknown downloader" # should not reach here + fi + return $? +} + +check_help_for() { + local _cmd + local _arg + local _ok + _cmd="$1" + _ok="y" + shift + + if [ "$_cmd" = "curl" ]; then + _cmd="$_cmd --help all" + else + _cmd="$_cmd --help" + fi + + # If we're running on OS-X, older than 10.13, then we always + # fail to find these options to force fallback + if check_cmd sw_vers; then + if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then + # Older than 10.13 + echo "Warning: Detected OS X platform older than 10.13" + _ok="n" + fi + fi + + for _arg in "$@"; do + if ! $_cmd | grep -q -- "$_arg"; then + _ok="n" + fi + done + + test "$_ok" = "y" +} + +main "$@" || exit 1 From f411babd6043a8daf3b946722dad305d2b217ddc Mon Sep 17 00:00:00 2001 From: Tim Fischer Date: Thu, 21 May 2026 13:50:10 +0200 Subject: [PATCH 2/4] init: support versions v1.x.x and beyond --- website/init | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/website/init b/website/init index cf46c332c..542cff4af 100755 --- a/website/init +++ b/website/init @@ -15,15 +15,18 @@ set -eu REPO="pulp-platform/bender" -# v0.32.0 is the first release with cargo-dist installers (all versions are 0.x.y) -# TODO: revisit this comparison if bender ever reaches v1.x +# v0.32.0 is the first release with cargo-dist installers CARGO_DIST_MIN_MINOR=32 VERSION=${1:-latest} -# Returns 0 (true) if VERSION should use the cargo-dist installer +# Returns 0 (true) if VERSION should use the cargo-dist installer. +# Uses cargo-dist for latest, any v1.x.y+, or v0.32.0+. +# Only falls back to legacy for v0.x.y where x < 32. uses_cargo_dist() { [ "$VERSION" = "latest" ] && return 0 + major=$(printf '%s' "$VERSION" | cut -d. -f1) + [ "$major" -ge 1 ] && return 0 minor=$(printf '%s' "$VERSION" | cut -d. -f2) [ "$minor" -ge "$CARGO_DIST_MIN_MINOR" ] } From ad428317eb83f8e72b6c36f7a980e239396c2dcc Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 21 May 2026 14:22:18 +0200 Subject: [PATCH 3/4] feat(installer): add `global` opt-in, default to legacy CWD drop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flip the v0.32+ cargo-dist path to mirror legacy behavior by default (binary in CWD, PATH untouched) and introduce a `global` keyword that installs into $CARGO_HOME/bin — via cargo-dist's installer for v0.32+, or by relocating the legacy script's output for older versions. Update the installation guide and CI example accordingly. Co-Authored-By: Claude Opus 4.7 (1M context) --- book/src/installation.md | 19 +++++++++++++++-- book/src/workflow/ci.md | 4 +++- website/init | 44 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/book/src/installation.md b/book/src/installation.md index f5e3bd9df..ad5bfbaa3 100644 --- a/book/src/installation.md +++ b/book/src/installation.md @@ -4,18 +4,33 @@ Bender is a single standalone binary. You can either use our recommended shell i ## Recommended: Shell Installer -The fastest way to install Bender is using our shell script. It detects your operating system and architecture, downloads the latest release, and places it in your path (typically `~/.cargo/bin`). +The fastest way to install Bender is using our shell script. It detects your operating system and architecture, downloads the matching release, and places the `bender` binary in the current directory: ```sh curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh ``` +After the script finishes, you'll find a `bender` executable in your current directory. Move it onto your `PATH` (e.g. `mv bender ~/.local/bin/`) or invoke it as `./bender`. + ### Installing a Specific Version -If you need a specific version of Bender (e.g., `0.31.0`), you can pass it as an argument: +Pass the desired version (e.g. `0.31.0`) as a positional argument: ```sh curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- 0.31.0 ``` +### Global Install +Pass `global` to install into `${CARGO_HOME:-$HOME/.cargo}/bin` instead of the current directory. For v0.32.0 and newer, this also adds the install directory to your `PATH` automatically via the underlying [cargo-dist](https://opensource.axo.dev/cargo-dist/) installer; for older versions, the binary is moved into place but you may need to add the directory to `PATH` manually. + +```sh +# Latest release, global install +curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- global + +# Specific version, global install (order of arguments is interchangeable) +curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- global 0.32.0 +``` + +> **Note:** The installer always overwrites an existing `bender` at the target location without prompting. + ## Alternative: Build from Source If you prefer building your own binary, you will need to [install Rust](https://rustup.rs/). diff --git a/book/src/workflow/ci.md b/book/src/workflow/ci.md index 9c55307e2..077a77640 100644 --- a/book/src/workflow/ci.md +++ b/book/src/workflow/ci.md @@ -41,7 +41,9 @@ variables: BENDER_VERSION: "0.31.0" before_script: - # Install Bender using the init script + # Install Bender using the init script. The default install drops `bender` in + # the current directory; add it to PATH for subsequent steps. Alternatively, + # pass `global` to install into $CARGO_HOME/bin instead. - curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- $BENDER_VERSION - export PATH=$PATH:$(pwd) diff --git a/website/init b/website/init index 542cff4af..d01dcf976 100755 --- a/website/init +++ b/website/init @@ -5,12 +5,16 @@ # Installer entry point for Bender. # Routes to the cargo-dist installer (v0.32.0+) or the legacy installer for -# older versions. The interface is unchanged: pass a version as the first -# argument to pin to a specific release, or omit it for the latest release. +# older versions. Pass a version to pin to a specific release, or omit it for +# the latest. By default, drops the `bender` binary into the current directory +# (matching legacy behavior); pass `global` to install into $CARGO_HOME and +# update PATH via the cargo-dist installer (v0.32.0+ only). # # Usage: # curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh # curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- 0.31.0 +# curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- global +# curl --proto '=https' --tlsv1.2 https://pulp-platform.github.io/bender/init -sSf | sh -s -- global 0.32.0 set -eu @@ -18,7 +22,14 @@ REPO="pulp-platform/bender" # v0.32.0 is the first release with cargo-dist installers CARGO_DIST_MIN_MINOR=32 -VERSION=${1:-latest} +GLOBAL=0 +VERSION=latest +for arg in "$@"; do + case "$arg" in + global) GLOBAL=1 ;; + *) VERSION="$arg" ;; + esac +done # Returns 0 (true) if VERSION should use the cargo-dist installer. # Uses cargo-dist for latest, any v1.x.y+, or v0.32.0+. @@ -37,7 +48,32 @@ if uses_cargo_dist; then else URL="https://github.com/$REPO/releases/download/v$VERSION/bender-installer.sh" fi - curl --proto '=https' --tlsv1.2 -LsSf "$URL" | sh + if [ "$GLOBAL" -eq 1 ]; then + curl --proto '=https' --tlsv1.2 -LsSf "$URL" | sh + else + # Default: have cargo-dist install into a temp dir without touching PATH, + # then move the binary into the current directory to mirror legacy behavior. + _tmpdir=$(mktemp -d) + trap 'rm -rf "$_tmpdir"' EXIT + curl --proto '=https' --tlsv1.2 -LsSf "$URL" | \ + CARGO_DIST_FORCE_INSTALL_DIR="$_tmpdir" INSTALLER_NO_MODIFY_PATH=1 sh + mv "$_tmpdir/bin/bender" . + fi +elif [ "$GLOBAL" -eq 1 ]; then + # Legacy installer drops the binary in CWD; run it in a temp dir, then + # relocate to $CARGO_HOME/bin to match the v0.32+ global install layout. + _dest="${CARGO_HOME:-$HOME/.cargo}/bin" + mkdir -p "$_dest" + _tmpdir=$(mktemp -d) + trap 'rm -rf "$_tmpdir"' EXIT + ( cd "$_tmpdir" && curl --proto '=https' --tlsv1.2 -LsSf \ + "https://pulp-platform.github.io/bender/init-legacy" | sh -s -- "$VERSION" ) + mv "$_tmpdir/bender" "$_dest/" + echo "Installed bender to $_dest/bender" + case ":${PATH:-}:" in + *":$_dest:"*) ;; + *) echo "Note: '$_dest' is not on PATH; add it to use 'bender' from anywhere." >&2 ;; + esac else # Delegate to the legacy installer for versions before cargo-dist was introduced curl --proto '=https' --tlsv1.2 -LsSf \ From 4521aa59ff8160834c05161eb1f9a3157735c337 Mon Sep 17 00:00:00 2001 From: Michael Rogenmoser Date: Thu, 21 May 2026 14:35:00 +0200 Subject: [PATCH 4/4] refactor(installer): derive Pages URL from $REPO The legacy installer fallback was hardcoded to pulp-platform.github.io, requiring fork-test setups to edit three places instead of one. Derive the Pages base from $REPO via parameter expansion so retargeting the script to a fork only requires updating the single $REPO variable. Co-Authored-By: Claude Opus 4.7 (1M context) --- website/init | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/website/init b/website/init index d01dcf976..fe9454520 100755 --- a/website/init +++ b/website/init @@ -19,6 +19,9 @@ set -eu REPO="pulp-platform/bender" +# Derive the GitHub Pages base from $REPO so a fork only needs to edit one +# variable to retarget both release artifacts and the legacy installer. +PAGES_URL="https://${REPO%%/*}.github.io/${REPO##*/}" # v0.32.0 is the first release with cargo-dist installers CARGO_DIST_MIN_MINOR=32 @@ -67,7 +70,7 @@ elif [ "$GLOBAL" -eq 1 ]; then _tmpdir=$(mktemp -d) trap 'rm -rf "$_tmpdir"' EXIT ( cd "$_tmpdir" && curl --proto '=https' --tlsv1.2 -LsSf \ - "https://pulp-platform.github.io/bender/init-legacy" | sh -s -- "$VERSION" ) + "$PAGES_URL/init-legacy" | sh -s -- "$VERSION" ) mv "$_tmpdir/bender" "$_dest/" echo "Installed bender to $_dest/bender" case ":${PATH:-}:" in @@ -77,5 +80,5 @@ elif [ "$GLOBAL" -eq 1 ]; then else # Delegate to the legacy installer for versions before cargo-dist was introduced curl --proto '=https' --tlsv1.2 -LsSf \ - "https://pulp-platform.github.io/bender/init-legacy" | sh -s -- "$VERSION" + "$PAGES_URL/init-legacy" | sh -s -- "$VERSION" fi