From d377c31aa9cbfc84343a3499ba6d755792a585ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Y=C3=BCce=20Tekol?= Date: Wed, 31 Jan 2024 16:54:24 +0300 Subject: [PATCH] added the install script (#441) --- extras/unix/install.sh | 427 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 427 insertions(+) create mode 100644 extras/unix/install.sh diff --git a/extras/unix/install.sh b/extras/unix/install.sh new file mode 100644 index 00000000..79cc87e9 --- /dev/null +++ b/extras/unix/install.sh @@ -0,0 +1,427 @@ +#! /bin/bash + +# Hazelcast CLC Install script +# (c) 2023 Hazelcast, Inc. + +set -eu -o pipefail + +check_ok () { + local what="$1" + local e=no + which "$what" > /dev/null && e=yes + case "$what" in + awk*) state_awk_ok=$e;; + bash*) state_bash_ok=$e;; + curl*) state_curl_ok=$e;; + tar*) state_tar_ok=$e;; + unzip*) state_unzip_ok=$e;; + wget*) state_wget_ok=$e;; + xattr*) state_xattr_ok=$e;; + zsh*) state_zsh_ok=$e;; + *) log_debug "invalid check: $what" + esac +} + +log_warn () { + echo "WARN $1" 1>&2 +} + +log_info () { + echo "INFO $1" 1>&2 +} + +log_debug () { + if [[ "${state_debug}" == "yes" ]]; then + echo "DEBUG $1" 1>&2 + fi +} + +echo_indent () { + printf " %s\n" "$1" 1>&2 +} + +echo_note () { + echo "NOTE $1" 1>&2 +} + +echo_ok () { + echo " OK $1" 1>&2 +} + +bye () { + if [[ "${1:-}" != "" ]]; then + echo "ERROR $*" 1>&2 + fi + exit 1 +} + +print_usage () { + echo "This script installs Hazelcast CLC to a user directory." + echo + echo "Usage: $0 [--beta | --debug | --help | --version VERSION ]" + echo + echo " --beta Enable downloading BETA and PREVIEW releases" + echo " --debug Enable DEBUG logging" + echo " --help Show help" + echo " --version VERSION Install a specific version" + echo + exit 0 +} + +setup () { + detect_tmpdir + for cmd in $DEPENDENCIES; do + check_ok "$cmd" + done + detect_httpget +} + +detect_tmpdir () { + state_tmp_dir="${TMPDIR:-/tmp}" +} + +do_curl () { + curl -LSs "$1" +} + +do_wget () { + wget -O- "$1" +} + +detect_uncompress () { + local ext=${state_archive_ext} + if [[ "$ext" == "tar.gz" ]]; then + state_uncompress=do_untar + elif [[ "$ext" == "zip" ]]; then + state_uncompress=do_unzip + else + bye "$ext archive is not supported" + fi +} + +do_untar () { + if [[ "$state_tar_ok" != "yes" ]]; then + bye "tar is required for install" + fi + local path="$1" + local base="$2" + tar xf "$path" -C "$base" +} + +do_unzip () { + if [[ "$state_unzip_ok" != "yes" ]]; then + bye "unzip is required for install" + fi + local path="$1" + local base="$2" + unzip -o -q "$path" -d "$base" +} + +install_release () { + # create base + local tmp="${state_tmp_dir}" + local base="$tmp/clc" + mkdir -p "$base" + # uncompress release package + local path="${state_archive_path}" + log_debug "UNCOMPRESS $path => $base" + ${state_uncompress} "$path" "$base" + # move files to their place + base="$base/${state_clc_name}" + local bin="$state_bin_dir/clc" + mv_path "$base/clc" "$bin" + local files="README.txt LICENSE.txt" + for item in $files; do + mv_path "$base/$item" "$CLC_HOME/$item" + done + local scripts + set +e + scripts="$(ls $base/scripts 2> /dev/null)" + set -e + if [[ "$scripts" != "" ]]; then + # move builtin scripts + local scripts_target="$CLC_HOME/scripts/builtin" + mkdir -p "$scripts_target" + for item in $scripts; do + mv_path "$base/scripts/$item" "$scripts_target" + done + printf "Do not edit the scripts in this directory.\nThey may be overwritten by the installer.\n" > "$scripts_target/README.txt" + log_info "Copied the scripts to $scripts_target" + fi + # on MacOS remove the clc binary from quarantine + if [[ "$state_xattr_ok" == "yes" && "$state_os" == "darwin" ]]; then + set +e + remove_from_quarantine "$bin" + set -e + fi +} + +remove_from_quarantine () { + local qa + local path + qa="com.apple.quarantine" + path="$1" + for a in $(xattr "$path"); do + if [[ "$a" == "$qa" ]]; then + log_debug "REMOVE FROM QUARANTINE: $path" + xattr -d $qa "$path" + break + fi + done +} + +update_config_files () { + if [[ "$state_bash_ok" == "yes" ]]; then + update_rc "$HOME/.bashrc" + update_rc "$HOME/.profile" + fi + if [[ "$state_zsh_ok" == "yes" ]]; then + update_rc "$HOME/.zshenv" + fi +} + +update_rc () { + local path="$1" + local set_path="PATH=\$PATH:${state_bin_dir}" + local code=" +echo \"\$PATH\" | grep \"${state_bin_dir}\" > /dev/null +if [[ \$? == 1 ]]; then + export $set_path +fi +" + if [[ -e "$path" ]]; then + # check if this file is a symbolic link + if [[ -L "$path" ]]; then + log_warn "$path is a symbolic link. Writing to symbolic links is not supported." + echo_indent "You can manually add the following in $path" + echo_indent "$code" + return + fi + local text + set +e + text=$(cat "$path" | grep "$set_path") + set -e + if [[ "$text" != "" ]]; then + # CLC PATH is already exported in this file + log_debug "CLC PATH is already installed in $path" + return + fi + fi + # Add the CLC PATH to this file + printf '\n# Added by Hazelcast CLC installer' >> "$path" + printf "$code" >> "$path" + log_info "Added CLC path to $path" +} + +mv_path () { + log_debug "MOVE $1 to $2" + mv "$1" "$2" +} + +detect_httpget () { + if [[ "${state_curl_ok}" == "yes" ]]; then + state_httpget=do_curl + elif [[ "${state_wget_ok}" == "yes" ]]; then + state_httpget=do_wget + else + bye "either curl or wget is required" + fi + log_debug "state_httpget=$state_httpget" +} + +httpget () { + log_debug "GET ${state_httpget} $1" + ${state_httpget} "$@" +} + +print_banner () { + echo + echo "Hazelcast CLC Installer (c) 2023 Hazelcast, Inc." + echo +} + +print_success () { + echo + echo_ok "Hazelcast CLC ${state_download_version} is installed at $CLC_HOME" + echo + echo_indent 'Next steps:' + echo_indent '1. Open a new terminal,' + echo_indent '2. Run `clc version` to confirm that CLC is installed,' + echo_indent '3. Enjoy!' + maybe_print_old_clc_warning + echo + echo_note 'If the steps above do not work, try copying `clc` binary to your $PATH:' + echo_indent "$ sudo cp $state_bin_dir/clc /usr/local/bin" + echo +} + +maybe_print_old_clc_warning () { + # create and assign the variable separately + # so the exit status is not lost + local clc_path + set +e + clc_path=$(which clc) + set -e + local bin_path="$state_bin_dir/clc" + if [[ "$clc_path" != "" && "$clc_path" != "$bin_path" ]]; then + echo + echo_note "A binary named 'clc' already exists at ${clc_path}." + echo_indent 'You may want to delete it before running the installed CLC.' + echo_indent "$ sudo rm -f ${clc_path}" + fi +} + +detect_last_release () { + if [[ "$state_download_version" != "" ]]; then + return + fi + if [[ "$state_awk_ok" != "yes" ]]; then + bye "Awk is required for install" + fi + local re + local text + local v + re='$1 ~ /tag_name/ { gsub(/[",]/, "", $2); print($2) }' + text="$(httpget https://api.github.com/repos/hazelcast/hazelcast-commandline-client/releases)" + if [[ "$state_beta" == "yes" ]]; then + set +e + v=$(echo "$text" | awk "$re" | head -1) + set -e + else + set +e + v=$(echo "$text" | awk "$re" | grep -vi preview | grep -vi beta | head -1) + set -e + fi + if [[ "$v" == "" ]]; then + bye "could not determine the latest version" + fi + state_download_version="$v" +} + +detect_platform () { + local os + os="$(uname -s)" + case "$os" in + Linux*) os=linux; ext="tar.gz";; + Darwin*) os=darwin; ext="zip";; + *) bye "This script supports only Linux and MacOS, not $os";; + esac + state_os=$os + log_debug "state_os=$state_os" + state_archive_ext=$ext + arch="$(uname -m)" + case "$arch" in + x86_64*) arch=amd64;; + amd64*) arch=amd64;; + armv6l*) arch=arm;; + armv7l*) arch=arm;; + arm64*) arch=arm64;; + aarch64*) arch=arm64;; + *) bye "This script supports only 64bit Intel and 32/64bit ARM architecture, not $arch" + esac + state_arch="$arch" + log_debug "state_arch=$state_arch" +} + +make_download_url () { + local v=${state_download_version} + local clc_name=${state_clc_name} + local ext=${state_archive_ext} + state_download_url="https://github.com/hazelcast/hazelcast-commandline-client/releases/download/$v/${clc_name}.${ext}" +} + +make_clc_name () { + local v="${state_download_version}" + local os="${state_os}" + local arch="${state_arch}" + state_clc_name="hazelcast-clc_${v}_${os}_${arch}" +} + +create_home () { + log_info "Creating the Home directory: $CLC_HOME" + mkdir -p "$state_bin_dir" "$CLC_HOME/etc" + echo "install-script" > "$CLC_HOME/etc/.source" +} + +download_release () { + detect_tmpdir + detect_platform + detect_uncompress + detect_last_release + log_debug "state_download_version=$state_download_version" + make_clc_name + make_download_url + log_info "Downloading: ${state_download_url}" + local tmp + local ext + tmp="${state_tmp_dir}" + ext="${state_archive_ext}" + state_archive_path="$tmp/clc.${ext}" + httpget "${state_download_url}" > "${state_archive_path}" +} + +process_flags () { + local flag="${1:-}" + while [[ "$flag" != "" ]]; do + case "$flag" in + --beta*) state_beta=yes;; + --debug*) state_debug=yes;; + --help*) print_banner; print_usage;; + --version*) + shift + state_download_version=$1 + if [[ "$state_download_version" == "" ]]; then + # the version is missing + bye "--version requires a version" + fi + ;; + *) bye "Unknown option: $flag";; + esac + shift + flag="${1:-}" + done + if [[ "$state_download_version" != "" ]]; then + if [[ "$state_beta" == "yes" ]]; then + bye "--beta and --version flags are mutually exclusive" + fi + local first_char=${state_download_version:0:1} + if [[ "$first_char" != "v" ]]; then + state_download_version="v$state_download_version" + fi + fi +} + +DEPENDENCIES="awk bash curl tar unzip wget xattr zsh" +CLC_HOME="${CLC_HOME:-$HOME/.hazelcast}" + +state_arch= +state_archive_ext= +state_archive_path= +state_beta=no +state_bin_dir="$CLC_HOME/bin" +state_clc_name= +state_debug=no +state_download_url= +state_download_version= +state_httpget= +state_os= +state_tmp_dir= +state_uncompress= + +state_awk_ok=no +state_curl_ok=no +state_tar_ok=no +state_unzip_ok=no +state_wget_ok=no +state_xattr_ok=no +state_bash_ok=no +state_zsh_ok=no + +process_flags "$@" +print_banner +setup +create_home +download_release +install_release +update_config_files +print_success