#!/bin/sh # reportbin installer for macOS and Linux. Needs only sh and curl. # # curl -fsSL https://reportbin.dev/install.sh | sh # # Installs the `reportbin` command to ~/.local/bin and saves the server URL and # upload token to ~/.config/reportbin/env. To uninstall, delete both. # # Optional environment: # REPORTBIN_TOKEN upload token, skips the prompt # REPORTBIN_URL server to use instead of https://reportbin.dev # REPORTBIN_BIN_DIR where to install the command, default ~/.local/bin set -eu main () { url=${REPORTBIN_URL:-https://reportbin.dev} url=${url%/} bin_dir=${REPORTBIN_BIN_DIR:-$HOME/.local/bin} config=$HOME/.config/reportbin/env command -v curl >/dev/null 2>&1 || die 'curl is required (Ubuntu: sudo apt install -y curl)' token=${REPORTBIN_TOKEN:-} if [ -n "$token" ]; then token_ok "$token" || die 'the server rejected this token' elif token=$(saved_token) && [ -n "$token" ] && token_ok "$token"; then say "Using the saved token from $config" else [ -z "$token" ] || say 'The saved token was rejected by the server.' token=$(ask_token) [ -n "$token" ] || die 'no token given' token_ok "$token" || die 'the server rejected this token' fi mkdir -p "$bin_dir" "${config%/*}" # Written fresh and moved into place, so the token file is always private # and an interrupted install never leaves a half-written file behind. (umask 077 && printf 'REPORTBIN_URL=%s\nREPORTBIN_TOKEN=%s\n' "$url" "$token" >"$config.tmp") mv -f "$config.tmp" "$config" write_cli >"$bin_dir/.reportbin.tmp" chmod 755 "$bin_dir/.reportbin.tmp" mv -f "$bin_dir/.reportbin.tmp" "$bin_dir/reportbin" say "Installed $bin_dir/reportbin" case ":$PATH:" in *":$bin_dir:"*) ;; *) add_to_path ;; esac say '' say 'Try it:' say " echo '

Hello

' | reportbin -" } say () { printf '%s\n' "$*"; } die () { printf 'reportbin install: %s\n' "$*" >&2; exit 1; } # A saved token is only reused for the server it was saved for. saved_token () { [ -f "$config" ] && [ "$(sed -n 's/^REPORTBIN_URL=//p' "$config")" = "$url" ] || return 0 sed -n 's/^REPORTBIN_TOKEN=//p' "$config" } ask_token () { (: /dev/null || die 'no terminal to ask for the token; run with REPORTBIN_TOKEN=' printf 'Upload token for %s: ' "$url" >/dev/tty # Input is hidden while typing; make sure Ctrl+C does not leave the terminal that way. trap 'stty echo /dev/null; printf "\n" >/dev/tty; exit 130' INT TERM HUP stty -echo /dev/null || true IFS= read -r answer /dev/null || true trap - INT TERM HUP printf '\n' >/dev/tty printf '%s' "$answer" | tr -d '[:space:]' } # Checks a token without storing anything: an empty upload is rejected with 400 # after auth succeeds, and with 401 when the token is wrong. 429 means the token # is valid but its uploads are used up for the hour. token_ok () { escaped=$(printf '%s' "$1" | sed 's/[\\"]/\\&/g') status=$(curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 10 --config /dev/fd/3 \ -H 'Content-Type: text/html' --data-binary '' "$url/api/reports" 3<>"$rc" say "Added $bin_dir to PATH in $rc. Open a new terminal to use it." } write_cli () { cat <<'CLI' #!/bin/sh # reportbin: upload an HTML report and print its link. set -eu usage () { cat < | reportbin - reportbin --hi "" Uploads an HTML report and prints its link, or sends feedback to the server. Reads REPORTBIN_URL and REPORTBIN_TOKEN from the environment or from ~/.config/reportbin/env (override with REPORTBIN_CONFIG). EOF } die () { printf 'reportbin: %s\n' "$*" >&2; exit 1; } case ${1:-} in -h|--help) usage; exit 0 ;; # --feedback is an unlisted alias, for people who guess it. --hi|--feedback) shift; action=feedback; [ -n "$*" ] || { usage >&2; exit 2; } ;; *) action=upload; [ $# -eq 1 ] || { usage >&2; exit 2; } ;; esac # Environment variables win over the config file. url=${REPORTBIN_URL:-} token=${REPORTBIN_TOKEN:-} config=${REPORTBIN_CONFIG:-$HOME/.config/reportbin/env} if [ -f "$config" ]; then url=${url:-$(sed -n 's/^REPORTBIN_URL=//p' "$config")} token=${token:-$(sed -n 's/^REPORTBIN_TOKEN=//p' "$config")} fi [ -n "$url" ] && [ -n "$token" ] || die "REPORTBIN_URL and REPORTBIN_TOKEN must be set (in the environment or $config)" url=${url%/} # The token goes through a config descriptor so it never shows up in `ps`. escaped=$(printf '%s' "$token" | sed 's/[\\"]/\\&/g') # Usage: send send () { curl -sS -w '%{http_code}' --connect-timeout 10 --config /dev/fd/3 \ -H "Content-Type: $2" --data-binary "$3" "$url/api/$1" 3<