#!/usr/bin/env bash # install.sh — install dmigrate to /usr/local/bin/dmigrate # # Usage: # curl -fsSL | bash # # Environment: # DMIGRATE_URL Override download URL for dmigrate.sh set -Eeuo pipefail INSTALL_TARGET="/usr/local/bin/dmigrate" INSTALL_SCRIPT_URL="https://gist.githubusercontent.com/mewisme/dd46a582b6131d6543f4336ac7087454/raw/install.sh" DMIGRATE_URL="${DMIGRATE_URL:-https://gist.githubusercontent.com/mewisme/dd46a582b6131d6543f4336ac7087454/raw/dmigrate.sh}" DRY_RUN=false supports_color() { [ -t 1 ] && command -v tput >/dev/null 2>&1 && [ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ] } if supports_color; then C_RESET="$(tput sgr0)" C_RED="$(tput setaf 1)" C_GREEN="$(tput setaf 2)" C_YELLOW="$(tput setaf 3)" C_BLUE="$(tput setaf 4)" C_BOLD="$(tput bold)" else C_RESET="" C_RED="" C_GREEN="" C_YELLOW="" C_BLUE="" C_BOLD="" fi info() { printf '%s\n' "${C_BLUE}INFO${C_RESET} $*"; } ok() { printf '%s\n' "${C_GREEN}OK${C_RESET} $*"; } warn() { printf '%s\n' "${C_YELLOW}WARN${C_RESET} $*"; } fail() { printf '%s\n' "${C_RED}ERROR${C_RESET} $*" >&2; } on_error() { local exit_code=$? fail "command failed at line ${1}: ${BASH_COMMAND}" exit "$exit_code" } trap 'on_error $LINENO' ERR run() { if [ "$DRY_RUN" = true ]; then printf '[DRY] ' printf '%q ' "$@" printf '\n' else "$@" fi } usage() { cat </dev/null 2>&1; then fail "missing command: $1" exit 1 fi } need_download_cmd() { if command -v curl >/dev/null 2>&1; then echo "curl" return 0 fi if command -v wget >/dev/null 2>&1; then echo "wget" return 0 fi fail "missing command: curl or wget" exit 1 } fetch_url() { local url="$1" local dest="$2" local tool tool="$(need_download_cmd)" case "$tool" in curl) curl -fsSL "$url" -o "$dest" ;; wget) wget -qO "$dest" "$url" ;; esac } read_script_version() { local file="$1" grep -E '^VERSION=' "$file" | head -n 1 | cut -d= -f2- | tr -d '"' | tr -d "'" } validate_downloaded_script() { local file="$1" if [ ! -s "$file" ]; then fail "downloaded script is empty" return 1 fi if ! head -n 1 "$file" | grep -q '^#!/usr/bin/env bash'; then fail "downloaded file is not a bash script" return 1 fi if ! grep -q 'dmigrate' "$file"; then fail "downloaded file does not look like dmigrate" return 1 fi if [ -z "$(read_script_version "$file")" ]; then fail "downloaded file is missing VERSION" return 1 fi if ! tail -n 1 "$file" | tr -d '\r' | grep -Fxq 'main "$@"'; then fail "downloaded file has invalid ending (expected: main \"\$@\")" return 1 fi if [ "$(tr -d '\r' < "$file" | grep -cFx 'main "$@"' || true)" -ne 1 ]; then fail "downloaded file must contain exactly one main entrypoint" return 1 fi return 0 } parse_args() { while [ $# -gt 0 ]; do case "$1" in --dry-run) DRY_RUN=true ;; -h|--help) usage exit 0 ;; *) fail "unknown option: $1" usage >&2 exit 1 ;; esac shift done } main() { local tmp version parse_args "$@" need_cmd sudo need_cmd mktemp if [ -e "$INSTALL_TARGET" ]; then warn "overwriting existing install at $INSTALL_TARGET" fi tmp="$(mktemp)" trap "rm -f -- '${tmp}'" EXIT info "downloading dmigrate from $DMIGRATE_URL" if ! fetch_url "$DMIGRATE_URL" "$tmp"; then fail "failed to download dmigrate from $DMIGRATE_URL" exit 1 fi if ! validate_downloaded_script "$tmp"; then exit 1 fi version="$(read_script_version "$tmp")" info "installing dmigrate v${version} -> $INSTALL_TARGET" run sudo bash -c 'cat "$1" > "$2"' _ "$tmp" "$INSTALL_TARGET" run sudo chmod 755 "$INSTALL_TARGET" if [ "$DRY_RUN" = true ]; then ok "dry run complete" else if ! validate_downloaded_script "$INSTALL_TARGET"; then fail "install wrote an invalid script to $INSTALL_TARGET" exit 1 fi ok "installed: $INSTALL_TARGET" echo "Run: dmigrate help" fi } main "$@"