Ctrl K
ComunidadGuías…

memos-server-updater.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env bash
set -euo pipefail

platform=$(uname -ms)

if [[ ${OS:-} = Windows_NT ]]; then
    if [[ $platform != MINGW64* ]]; then
        powershell -c "Set-ExecutionPolicy Bypass -Scope Process -Force; irm https://raw.githubusercontent.com/memospot/memospot/main/memos-server-updater.ps1|iex"
        exit $?
    else
        echo "Cygwin is not supported by this script."
        exit 1
    fi
fi

# Reset
Color_Off=''

# Regular Colors
Dim='' # White
Green=''
Red=''
Yellow=''

# Bold
Bold_Cyan=''
Bold_Green=''
Bold_Red=''
Bold_White=''
Bold_Yellow=''

if [[ -t 1 ]]; then
    # Reset
    Color_Off='\033[0m' # Text Reset

    # Regular Colors
    Dim='\033[0;2m'
    Green='\033[0;32m'  # Green
    Red='\033[0;31m'    # Red
    Yellow='\033[0;33m' # Yellow

    # Bold
    Bold_Cyan='\033[1;36m'   # Bold Cyan
    Bold_Green='\033[1;32m'  # Bold Green
    Bold_Red='\033[1;31m'    # Bold Red
    Bold_White='\033[1m'     # Bold White
    Bold_Yellow='\033[1;33m' # Bold Yellow
fi

error() {
    echo -e "${Red}error${Color_Off}:" "$@" >&2
    exit 1
}

info() {
    echo -e "${Dim}$* ${Color_Off}"
}

msg() {
    echo -e "$* ${Color_Off}"
}

warn() {
    echo -e "${Yellow}$* ${Color_Off}"
}

info_bold() {
    echo -e "${Bold_White}$* ${Color_Off}"
}

success() {
    echo -e "${Green}$* ${Color_Off}"
}

terminate_user_processes() {
    local user="$1"
    local process

    if ! command -v pgrep >/dev/null || ! command -v pkill >/dev/null; then
        warn "pgrep/pkill not available; skipping process termination checks."
        return
    fi

    for process in memos memospot; do
        if pgrep -u "$user" -x "$process" >/dev/null 2>&1; then
            info "Stopping running process for user $Bold_White$user$Dim: $Bold_White$process"
            pkill -u "$user" -x "$process" >/dev/null 2>&1 || true
            sleep 1
            if pgrep -u "$user" -x "$process" >/dev/null 2>&1; then
                error "Failed to terminate process '$process' for user '$user'."
            fi
        fi
    done
}

cpu_supports_avx2() {
    case $platform in
    'Darwin x86_64')
        if sysctl -a | grep machdep.cpu | grep -q AVX2; then
            return 0
        fi
        ;;
    'Linux x86_64' | *)
        if grep -q avx2 </proc/cpuinfo; then
            return 0
        fi
        ;;
    esac
    return 1
}

cpu_supports_popcnt() {
    case $platform in
    'Darwin x86_64')
        if sysctl -a | grep machdep.cpu | grep -q POPCNT; then
            return 0
        fi
        ;;
    'Linux x86_64' | *)
        if grep -q popcnt </proc/cpuinfo; then
            return 0
        fi
        ;;
    esac
    return 1
}

################################################################################
main() {

    banner=$(
        cat <<"EOF"
         __  __ _____ __  __  ___  ____  ____   ___ _____
        |  \/  | ____|  \/  |/ _ \/ ___||  _ \ / _ \_   _|
        | |\/| |  _| | |\/| | | | \___ \| |_) | | | || |
        | |  | | |___| |  | | |_| |___) |  __/| |_| || |
        |_|  |_|_____|_|  |_|\___/|____/|_|    \___/ |_|
                                                 _       _
 ___  ___ _ ____   _____ _ __    _   _ _ __   __| | __ _| |_ ___ _ __
/ __|/ _ \ '__\ \ / / _ \ '__|  | | | | '_ \ / _` |/ _` | __/ _ \ '__|
\__ \  __/ |   \ V /  __/ |     | |_| | |_) | (_| | (_| | ||  __/ |
|___/\___|_|    \_/ \___|_|      \__,_| .__/ \__,_|\__,_|\__\___|_|
                                      |_|
EOF
    )
    echo -e "${Bold_Cyan}${banner}${Color_Off}"
    echo -e "${Bold_Green}https://memospot.github.io/ | https://github.com/memospot/memospot${Color_Off}\n"

    command -v tar >/dev/null ||
        error "tar is required to extract Memos' updates"
    command -v jq >/dev/null ||
        error "jq is required to get latest Memos' tag"

    if [[ $# -gt 1 ]]; then
        error 'Too many arguments, only one is allowed. The allowed argument can be a specific tag of Memos to install. (e.g. "v0.22.0")'
    fi

    case $platform in
    'Darwin x86_64')
        if cpu_supports_avx2; then
            target=darwin-x86_64_v3
        elif cpu_supports_popcnt; then
            target=darwin-x86_64_v2
        else
            target=darwin-x86_64
        fi
        ;;
    'Darwin arm64')
        target=darwin-arm64
        ;;
    'Linux aarch64' | 'Linux arm64')
        target=linux-arm64
        ;;
    'Linux x86_64' | *)
        if cpu_supports_avx2; then
            target=linux-x86_64_v3
        elif cpu_supports_popcnt; then
            target=linux-x86_64_v2
        else
            target=linux-x86_64
        fi
        ;;
    esac

    if [[ $target = darwin-arm64 ]]; then
        # Is this process running in Rosetta?
        # redirect stderr to devnull to avoid error message when not running in Rosetta
        if [[ $(sysctl -n sysctl.proc_translated 2>/dev/null) = 1 ]]; then
            target=darwin-arm64
            info "Your shell is running in Rosetta 2. Downloading Memos for $target instead"
        fi
    fi

    GITHUB=${GITHUB-"https://github.com"}
    REPO=${REPO-"memospot/memos-builds"}
    repo_url="$GITHUB/$REPO"
    latest_tag=$(curl -s "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name') || error "Failed to fetch latest tag from GitHub."
    requested_tag=${1:-latest}
    if [[ $requested_tag = latest ]]; then
        install_tag=$latest_tag
    else
        install_tag=$requested_tag
    fi
    download_file=memos-$install_tag-$target.tar.gz
    sha256sums_uri=$(curl -s "https://api.github.com/repos/$REPO/releases/tags/$install_tag" | jq -r '.assets[] | select(.name|endswith(".txt")) | .browser_download_url') || error "Failed to fetch tag $install_tag from GitHub."
    memos_uri=$repo_url/releases/download/$install_tag/$download_file

    INSTALL_DIR=${INSTALL_DIR-"/usr/bin"}
    SUDO_USER=${SUDO_USER-"$USER"}
    USER_HOME=$(getent passwd "$SUDO_USER" | cut -d: -f6)
    legacy_data_dir="$USER_HOME/.memospot"
    xdg_data_dir=${XDG_CONFIG_HOME:-$USER_HOME/.config}/memospot

    if [[ -f "$xdg_data_dir/memos_prod.db" ]]; then
        DATA_DIR="$xdg_data_dir"
    else
        DATA_DIR="$legacy_data_dir"
    fi

    memos_bin=''
    locations=(
        "$INSTALL_DIR"
        "/usr/local/bin"
        "$USER_HOME/.local/bin"
    )
    for location in "${locations[@]}"; do
        if [[ -f "$location/memos" ]]; then
            INSTALL_DIR="$location"
            memos_bin="$location/memos"
            break
        fi
    done
    if [[ -z $memos_bin ]]; then
        error "Memos' binary was not found in any of the following locations: ${Bold_Red}${locations[*]}"
    fi

    info "· Found Memos' binary at $Bold_White$memos_bin"

    if [[ ! -w $memos_bin ]]; then
        error "Memos' binary at $memos_bin is not writable. Please run this script with sudo."
    fi

    terminate_user_processes "$SUDO_USER"

    msg "· Latest Memos' release found: $Bold_White$latest_tag"
    msg "· Selected tag: $Bold_Cyan$install_tag$Dim (You may pass a different tag as an argument to this script)"
    msg "· Repository URL: $Bold_White$repo_url"
    info "· Release URL: $Bold_White$memos_uri"
    info "· Release SHA256SUMS: $Bold_White$sha256sums_uri"

    warn "\nAvoid doing Major and Minor version updates. Memospot may not be able to handle them yet."
    warn "Version scheme is Major.Minor.Patch. ${Bold_Yellow}Patch${Yellow} version updates should be fine."
    warn "It's essential to back-up your database before trying a major or minor version update."

    msg "\n» This script will not check whether you need to update the Memos server."
    msg "» Version $install_tag will be downloaded and installed regardless."

    info "$Bold_Green\n-> Press any key to continue <-\n"
    read -n 1 -sr </dev/tty

    info "Downloading Memos $install_tag ($target) from $Bold_White$memos_uri"
    (cd /tmp &&
        curl --fail --location --progress-bar --output "$download_file" "$memos_uri") ||
        error "Failed to download Memos from \"$memos_uri\"."

    info "Downloading SHA256SUMS from $Bold_White$sha256sums_uri"
    (cd /tmp &&
        curl --fail --location --progress-bar --output "memos_sha256sums.txt" "$sha256sums_uri") ||
        error "Failed to download sha256sums from \"$sha256sums_uri\"."

    info "Verifying SHA256SUMS"
    expected_hash=$(awk -v file="$download_file" '$2 == file { print $1 }' /tmp/memos_sha256sums.txt)
    if [[ -z $expected_hash ]]; then
        error "Failed to find checksum entry for $download_file."
    fi

    if command -v sha256sum >/dev/null; then
        actual_hash=$(sha256sum "/tmp/$download_file" | awk '{print $1}')
    elif command -v shasum >/dev/null; then
        actual_hash=$(shasum -a 256 "/tmp/$download_file" | awk '{print $1}')
    else
        error "No SHA256 tool found (sha256sum or shasum required)."
    fi

    if [[ $expected_hash != $actual_hash ]]; then
        error "SHA256 mismatch for $download_file. Expected $expected_hash but got $actual_hash."
    fi

    backup_dir="$DATA_DIR/server-updater-backups"
    date_string=$(date +%Y-%m-%d_%H-%M-%S)
    mkdir -p "$backup_dir" || error "Failed to create backup directory."

    # Backup existing Memos binary if it exists, before extracting the new one
    if [[ -f "$memos_bin" ]]; then
        info "Backing up current memos binary to $Bold_White$backup_dir"

        binary_backup_file="$backup_dir/memos_binary-${date_string}_before_${install_tag}.tar.gz"
        (cd "$(dirname "$memos_bin")" && tar -czf "$binary_backup_file" "$(basename "$memos_bin")") || error "Failed to backup memos binary."
        [[ -s "$binary_backup_file" ]] || error "Binary backup archive was not created correctly."
        success "Binary backup was created at $Bold_Green$backup_dir"
    fi

    database_dir="$DATA_DIR"
    database="$database_dir/memos_prod.db"
    if [[ -f "$database" ]]; then
        info "Backing up database to $Bold_White$backup_dir"

        db_files=("memos_prod.db")
        if [[ -f "$database-wal" ]]; then
            db_files+=("memos_prod.db-wal")
        fi
        if [[ -f "$database-shm" ]]; then
            db_files+=("memos_prod.db-shm")
        fi

        database_backup_file="$backup_dir/memos_db-${date_string}_before_${install_tag}.tar.gz"
        (cd "$database_dir" && tar -czf "$database_backup_file" "${db_files[@]}") || error "Failed to backup memos database files."
        [[ -s "$database_backup_file" ]] || error "Database backup archive was not created correctly."
        success "Database backup was created at $Bold_Green$backup_dir"
    fi

    extract_dir=$(mktemp -d "/tmp/memos-updater-extract.XXXXXX") || error "Failed to create temp extraction directory."
    tar -xzf "/tmp/$download_file" -C "$extract_dir" memos || error "Failed to extract Memos from \"/tmp/$download_file\"."
    [[ -f "$extract_dir/memos" ]] || error "Extracted archive does not contain memos binary."

    cp "$extract_dir/memos" "$memos_bin.new" || error "Failed to stage updated memos binary."
    chmod +x "$memos_bin.new" || error 'Failed to set permissions on staged Memos executable'
    mv -f "$memos_bin.new" "$memos_bin" || error 'Failed to replace Memos executable'

    rm -rf "$extract_dir"
    rm -f "/tmp/$download_file" "/tmp/memos_sha256sums.txt"
    chown -R "$SUDO_USER":"$SUDO_USER" "$backup_dir" || true

    success "Memos $install_tag was installed successfully to $INSTALL_DIR"

    success "Update complete."
}

main "$@" || exit 1