diff --git a/install_dsync.sh b/install_dsync.sh index f220d21..0a3d9e4 100644 --- a/install_dsync.sh +++ b/install_dsync.sh @@ -1,11 +1,18 @@ #!/usr/bin/env bash # Install dsync from https://git.ai.infran.ru/ilyukhin/dsync.git # +# Behavior: +# - Fetch latest dsync from repo (git shallow clone or raw download main/master). +# - Compare VERSION in remote vs locally installed (DEST_PATH). +# * If equal and no --force: say "already up to date" and exit 0. +# * If remote newer: update. +# * If local newer and no --force: warn and exit 1 (avoid accidental downgrade). +# # Usage: # ./install_dsync.sh # install to ~/.local/bin (default) # ./install_dsync.sh --system # install to /usr/local/bin (requires sudo) # ./install_dsync.sh -d DIR # install to custom DIR -# ./install_dsync.sh -f # force overwrite if target exists +# ./install_dsync.sh -f # force overwrite (also allows downgrade/reinstall) # ./install_dsync.sh -h # show help set -euo pipefail @@ -23,12 +30,58 @@ Install dsync from ${REPO_URL} Options: -s, --system install into /usr/local/bin (system-wide; may use sudo) -d DIR, --dest DIR install into DIR - -f, --force overwrite existing file without asking + -f, --force overwrite existing file; also bypasses version equality/downgrade checks -h, --help show this help EOF } -# parse options +# --- version helpers ---------------------------------------------------------- + +extract_version() { + # Parse VERSION from a dsync file (Python). Prints version or nothing. + # Accepts forms like: VERSION = "0.2.1" + local f="$1" + [[ -f "$f" ]] || { echo ""; return 1; } + # get line with VERSION= + local line + line="$(grep -E '^[[:space:]]*VERSION[[:space:]]*=' "$f" | head -1 || true)" + [[ -n "$line" ]] || { echo ""; return 1; } + # strip up to '=' + line="${line#*=}" + # trim spaces + line="$(echo "$line" | sed -E 's/^[[:space:]]+|[[:space:]]+$//g')" + # drop trailing comments + line="${line%%#*}" + # strip quotes + line="${line%\"}"; line="${line#\"}" + line="${line%\'}"; line="${line#\'}" + # keep only semver-ish chars + line="$(echo "$line" | sed -E 's/[^0-9A-Za-z\.\-\+].*$//')" + echo "$line" +} + +compare_versions() { + # Compare two dotted numeric versions: echoes 1 if $1 > $2, -1 if $1 < $2, 0 if equal. + # Non-numeric segments beyond dots are ignored (basic semver compare). + local a="$1" b="$2" + # fallback: if either empty, consider different + [[ -n "$a" && -n "$b" ]] || { [[ "$a" == "$b" ]] && echo 0 || echo 1; return; } + IFS=. read -r -a av <<< "$a" + IFS=. read -r -a bv <<< "$b" + local len="${#av[@]}" + (( ${#bv[@]} > len )) && len="${#bv[@]}" + local i ai bi + for ((i=0;i 10#$bi )); then echo 1; return; fi + if (( 10#$ai < 10#$bi )); then echo -1; return; fi + done + echo 0 +} + +# --- parse options ------------------------------------------------------------ + while [[ $# -gt 0 ]]; do case "$1" in -s|--system) @@ -68,13 +121,13 @@ done DEST_PATH="$DEST_DIR/dsync" -# create temporary dir and ensure cleanup +# --- prepare dirs ------------------------------------------------------------- + TMPDIR="$(mktemp -d)" trap 'rm -rf "$TMPDIR"' EXIT echo "Installing dsync to: $DEST_DIR" -# ensure target directory exists (create with sudo if system dir) if [[ ! -d "$DEST_DIR" ]]; then echo "Target directory $DEST_DIR does not exist — attempting to create it..." if ! mkdir -p "$DEST_DIR" 2>/dev/null; then @@ -89,29 +142,30 @@ fi FOUND="" -# 1) Try to clone repo with git (if git available) +# --- fetch from git (preferred) ---------------------------------------------- + if command -v git >/dev/null 2>&1; then echo "git found — attempting shallow clone..." - if git clone --depth 1 "$REPO_URL" "$TMPDIR/repo" >/dev/null 2>&1; then + if git clone --quiet --depth 1 "$REPO_URL" "$TMPDIR/repo" >/dev/null 2>&1; then echo "Repository cloned." else echo "Shallow clone failed, attempting normal clone (may show output)..." git clone --depth 1 "$REPO_URL" "$TMPDIR/repo" || true fi - if [[ -d "$TMPDIR/repo" ]]; then + # try to locate the dsync file FOUND="$(find "$TMPDIR/repo" -maxdepth 6 -type f -name dsync -print -quit || true)" fi fi -# 2) If git did not find the file, try to download raw file via curl (main/master) +# --- fallback: raw download from main/master --------------------------------- + if [[ -z "$FOUND" ]]; then if command -v curl >/dev/null 2>&1; then for BR in main master; do RAW_URL="$RAW_BASE/$BR/dsync" echo "Trying to download raw file: $RAW_URL" if curl --fail -L --max-redirs 5 -o "$TMPDIR/dsync" "$RAW_URL"; then - # check that downloaded file looks like text/script, not HTML error if file "$TMPDIR/dsync" | grep -qE 'text|python|ASCII'; then FOUND="$TMPDIR/dsync" break @@ -136,13 +190,47 @@ fi echo "Found source file at: $FOUND" -# Warn if target exists -if [[ -e "$DEST_PATH" && "$FORCE" -ne 1 ]]; then - echo "Error: $DEST_PATH already exists. Use --force to overwrite." >&2 - exit 1 +# --- version comparison logic ------------------------------------------------- + +REMOTE_VER="$(extract_version "$FOUND" || true)" +LOCAL_VER="" +if [[ -e "$DEST_PATH" ]]; then + LOCAL_VER="$(extract_version "$DEST_PATH" || true)" fi -# copy into destination (use sudo if target dir is not writable) +if [[ "$FORCE" -eq 0 && -e "$DEST_PATH" ]]; then + if [[ -n "$REMOTE_VER" && -n "$LOCAL_VER" ]]; then + CMP="$(compare_versions "$REMOTE_VER" "$LOCAL_VER")" + if [[ "$CMP" -eq 0 ]]; then + echo "dsync is already up to date (version $LOCAL_VER). Use --force to reinstall." + exit 0 + elif [[ "$CMP" -lt 0 ]]; then + echo "Installed dsync ($LOCAL_VER) is newer than remote ($REMOTE_VER)." + echo "Use --force to downgrade or reinstall." + exit 1 + else + echo "Updating dsync: $LOCAL_VER -> $REMOTE_VER" + fi + else + echo "Version check inconclusive (remote='$REMOTE_VER', local='$LOCAL_VER')." + echo "Proceeding with install (use --force to bypass version checks explicitly)." + fi +elif [[ -e "$DEST_PATH" && "$FORCE" -eq 1 ]]; then + if [[ -n "$REMOTE_VER" && -n "$LOCAL_VER" ]]; then + echo "Reinstalling (forced): $LOCAL_VER -> $REMOTE_VER" + else + echo "Reinstalling (forced)." + fi +else + if [[ -n "$REMOTE_VER" ]]; then + echo "Installing dsync version $REMOTE_VER" + else + echo "Installing dsync (version unknown)" + fi +fi + +# --- install/copy ------------------------------------------------------------- + if touch "$DEST_DIR/.write_test" >/dev/null 2>&1; then rm -f "$DEST_DIR/.write_test" cp -f "$FOUND" "$DEST_PATH" @@ -159,7 +247,8 @@ else echo "Installed (via sudo): $DEST_PATH" fi -# quick PATH check +# --- final PATH hint ---------------------------------------------------------- + echo echo "PATH check:" if command -v dsync >/dev/null 2>&1; then