#!/bin/sh # # JoyStream CLI installer. # # curl -fsSL https://get.joystream.ai | sh # # Detects your platform, downloads the matching binary, VERIFIES ITS CHECKSUM, # and installs it as `jstm`. Re-run it to upgrade. # # You are piping a script into a shell, so the checksum step is not optional: # nothing is installed unless the downloaded bytes match the published digest, # and a missing digest is treated as a failure rather than a skip. # # Prefer to read before running? That is the right instinct: # curl -fsSL https://get.joystream.ai -o install.sh && less install.sh && sh install.sh # # Environment overrides: # JSTM_VERSION install a specific version (e.g. 0.22.0) instead of latest # JSTM_INSTALL_DIR where to install (default: /usr/local/bin, else ~/.local/bin) # JSTM_BASE_URL base URL to fetch assets from (testing / mirrors) set -eu RELEASES="https://github.com/joystream-ai/cli-releases/releases" BIN_NAME="jstm" TMPDIR_="" # cleaned up on exit, see trap below cleanup() { [ -n "$TMPDIR_" ] && rm -rf "$TMPDIR_" } trap cleanup EXIT INT TERM die() { echo "error: $*" >&2 exit 1 } info() { echo "$*" } # --------------------------------------------------------------------- platform detect_target() { os="$(uname -s)" arch="$(uname -m)" case "$os" in Darwin) os_name="macos" ;; Linux) os_name="linux" ;; *) die "unsupported operating system: $os (supported: macOS, Linux)" ;; esac # uname reports x86_64; release assets say amd64. case "$arch" in arm64 | aarch64) arch_name="arm64" ;; x86_64 | amd64) arch_name="amd64" ;; *) die "unsupported architecture: $arch (supported: arm64, x86_64)" ;; esac target="${os_name}-${arch_name}" # Only three combinations are actually built. Linux/arm64 in particular is a # valid-looking pair with no asset behind it, so reject it here rather than # 404ing later with a confusing message. case "$target" in macos-arm64 | macos-amd64 | linux-amd64) ;; *) die "unsupported platform: ${os}/${arch} — no ${BIN_NAME}-${target} is published" ;; esac echo "${BIN_NAME}-${target}" } # --------------------------------------------------------------------- download fetch() { # $1 = url, $2 = destination if command -v curl >/dev/null 2>&1; then curl -fsSL "$1" -o "$2" elif command -v wget >/dev/null 2>&1; then wget -qO "$2" "$1" else die "need curl or wget to download files" fi } sha256_of() { 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 die "need sha256sum or shasum to verify the download" fi } # --------------------------------------------------------------------- install dir choose_install_dir() { if [ -n "${JSTM_INSTALL_DIR:-}" ]; then echo "$JSTM_INSTALL_DIR" return fi # /usr/local/bin is on the default PATH; ~/.local/bin often is not, so it is # the fallback rather than the default, and we warn when we land there. if [ -w /usr/local/bin ] 2>/dev/null; then echo /usr/local/bin else echo "$HOME/.local/bin" fi } # --------------------------------------------------------------------- main asset="$(detect_target)" if [ -n "${JSTM_BASE_URL:-}" ]; then base_url="$JSTM_BASE_URL" label="$JSTM_BASE_URL" elif [ -n "${JSTM_VERSION:-}" ]; then base_url="${RELEASES}/download/cli-v${JSTM_VERSION}" label="v${JSTM_VERSION}" else # `latest` resolves to the newest non-prerelease, which is why the rolling # cli-latest build is published as a prerelease and never picked up here. base_url="${RELEASES}/latest/download" label="latest" fi info "Installing ${BIN_NAME} (${asset}, ${label})" TMPDIR_="$(mktemp -d)" if ! fetch "${base_url}/${asset}" "${TMPDIR_}/${asset}"; then die "download failed: ${base_url}/${asset}" fi if ! fetch "${base_url}/checksums.txt" "${TMPDIR_}/checksums.txt"; then die "download failed: ${base_url}/checksums.txt (cannot verify without it)" fi expected="$(grep " ${asset}\$" "${TMPDIR_}/checksums.txt" | cut -d' ' -f1 || true)" [ -n "$expected" ] || die "no checksum published for ${asset} — refusing to install unverified" actual="$(sha256_of "${TMPDIR_}/${asset}")" [ "$expected" = "$actual" ] || die "checksum mismatch for ${asset} expected: ${expected} actual: ${actual} Nothing was installed." install_dir="$(choose_install_dir)" mkdir -p "$install_dir" || die "cannot create ${install_dir}" chmod +x "${TMPDIR_}/${asset}" # mv, not cp: replacing a running binary in place fails on some systems, and mv # is atomic within a filesystem so an interrupted upgrade cannot leave a partial file. if ! mv -f "${TMPDIR_}/${asset}" "${install_dir}/${BIN_NAME}" 2>/dev/null; then die "cannot write to ${install_dir} — set JSTM_INSTALL_DIR to a writable directory" fi info "Installed ${install_dir}/${BIN_NAME}" case ":${PATH}:" in *":${install_dir}:"*) ;; *) info "" info "warning: ${install_dir} is not on your PATH. Add it:" info " export PATH=\"${install_dir}:\$PATH\"" ;; esac "${install_dir}/${BIN_NAME}" --version 2>/dev/null || true