Files
I38/scripts/i38-update-check.sh

131 lines
3.5 KiB
Bash
Executable File

#!/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