#!/usr/bin/env bash # This file is part of I38. # # I38 is free software: you can redistribute it and/or modify it under the terms # of the GNU General Public License as published by the Free Software # Foundation, either version 3 of the License, or (at your option) any later # version. set -u remoteUrl="${I38_UPDATE_REMOTE_URL:-https://git.stormux.org/storm/I38}" remoteRef="${I38_UPDATE_REMOTE_REF:-refs/heads/master}" initialDelay="${I38_UPDATE_INITIAL_DELAY:-90}" checkInterval="${I38_UPDATE_CHECK_INTERVAL:-21600}" notifyInterval="${I38_UPDATE_NOTIFY_INTERVAL:-604800}" networkTimeout="${I38_UPDATE_NETWORK_TIMEOUT:-20}" scriptDir="$(unset CDPATH; cd -- "$(dirname -- "$0")" && pwd)" i3Dir="$(dirname -- "$scriptDir")" versionFile="${I38_VERSION_FILE:-${i3Dir}/i38-version}" stateDir="${XDG_STATE_HOME:-${HOME}/.local/state}/I38" notifyState="${stateDir}/update-check-notified" runtimeDir="${XDG_RUNTIME_DIR:-/tmp}" lockDir="${runtimeDir}/i38-update-check" valid_commit() { [[ "${1:-}" =~ ^[0-9a-fA-F]{40}$ ]] } read_installed_commit() { local commit [[ -r "$versionFile" ]] || return 1 IFS= read -r commit < "$versionFile" || return 1 valid_commit "$commit" || return 1 printf '%s\n' "$commit" } read_remote_commit() { local output commit if command -v timeout > /dev/null; then output="$(timeout "$networkTimeout" git ls-remote "$remoteUrl" "$remoteRef" 2> /dev/null)" || return 1 else output="$(git ls-remote "$remoteUrl" "$remoteRef" 2> /dev/null)" || return 1 fi commit="${output%%[[:space:]]*}" valid_commit "$commit" || return 1 printf '%s\n' "$commit" } already_notified() { local remoteCommit="$1" local lastCommit lastTime now [[ -r "$notifyState" ]] || return 1 { IFS= read -r lastCommit IFS= read -r lastTime } < "$notifyState" || return 1 [[ "$lastCommit" == "$remoteCommit" ]] || return 1 [[ "$lastTime" =~ ^[0-9]+$ ]] || return 1 now="$(date +%s)" (( now - lastTime < notifyInterval )) } remember_notification() { local remoteCommit="$1" mkdir -p "$stateDir" || return 0 { printf '%s\n' "$remoteCommit" date +%s } > "$notifyState" 2> /dev/null || true } send_update_notification() { local remoteCommit="$1" notify-send \ "I38 update available" \ "A newer I38 version is available. Update your checkout, then run i38.sh -u or rerun i38.sh to refresh your setup." \ 2> /dev/null || return 1 remember_notification "$remoteCommit" } stop_update_check() { rm -rf "$lockDir" exit 0 } if ! command -v git > /dev/null || ! command -v notify-send > /dev/null; then exit 0 fi if [[ -d "$lockDir" ]]; then if [[ -f "${lockDir}/pid" ]]; then IFS= read -r existingPid < "${lockDir}/pid" || existingPid="" if [[ "$existingPid" =~ ^[0-9]+$ ]] && kill -0 "$existingPid" 2> /dev/null; then exit 0 fi fi rm -rf "$lockDir" fi if ! mkdir "$lockDir" 2> /dev/null; then exit 0 fi printf '%s\n' "$$" > "${lockDir}/pid" trap 'rm -rf "$lockDir"' EXIT trap stop_update_check INT TERM sleep "$initialDelay" while :; do installedCommit="$(read_installed_commit)" || exit 0 remoteCommit="$(read_remote_commit)" remoteStatus=$? if [[ $remoteStatus -eq 0 && "$remoteCommit" != "$installedCommit" ]]; then if ! already_notified "$remoteCommit"; then send_update_notification "$remoteCommit" exit 0 fi fi sleep "$checkInterval" done