Files
TallyNote/scripts/tallynote-update.sh
T
Qiufeng 9719429f4a
TallyNote release / linux-x64 (push) Failing after 2m41s
feat: add TallyNote local reimbursement ledger
2026-08-29 01:02:29 +08:00

96 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
PATH=/usr/sbin:/usr/bin:/sbin:/bin
export PATH
umask 077
# Manual updater for operators without using the web control. The same
# verified TypeScript updater used by the systemd queue performs download,
# extraction and atomic release switching.
PREFIX=${TALLYNOTE_INSTALL_PREFIX:-${TALLYNOTE_PREFIX:-/opt/tallynote}}
DATA_DIR=${TALLYNOTE_DATA_DIR:-/var/lib/tallynote}
REQUEST_FILE=${TALLYNOTE_UPDATE_REQUEST_FILE:-$DATA_DIR/update-request.json}
NODE=${TALLYNOTE_NODE:-}
die() { printf 'tallynote update: %s\n' "$*" >&2; exit 1; }
version_sort_desc() {
if sort -V </dev/null >/dev/null 2>&1; then
sort -V -r
return
fi
awk -F'[.-]' '{ printf "%020d.%020d.%020d.%s\t%s\n", $1, $2, $3, ($4 == "" ? "~" : $4), $0 }' \
| sort -r | cut -f2-
}
[[ ${EUID:-$(id -u)} -eq 0 ]] || die 'must run as root'
if [[ "${1:-}" == "--rollback" ]]; then
current="$PREFIX/current"
[[ -L "$current" ]] || die 'current release is not a symlink'
current_target=$(readlink -f -- "$current")
current_name=$(basename -- "$current_target")
[[ "$current_name" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$ ]] || die 'current release version is invalid'
mapfile -t releases < <(
find "$PREFIX/releases" -mindepth 1 -maxdepth 1 -type d -printf '%p\n' \
| awk -F/ '$NF ~ /^[0-9]+\.[0-9]+\.[0-9]+([.-][0-9A-Za-z.-]+)?$/' \
| version_sort_desc
)
previous=''
found_current=0
for release in "${releases[@]}"; do
release_target=$(readlink -f -- "$release")
if [[ "$release_target" == "$current_target" ]]; then
found_current=1
continue
fi
if (( found_current )); then
previous=$release
break
fi
done
[[ -n "$previous" && -d "$previous" ]] || die 'no previous release available'
was_active=0
if systemctl is-active --quiet tallynote.service; then was_active=1; fi
if (( was_active )); then systemctl stop tallynote.service; fi
tmp="$PREFIX/.current-rollback-$$-${RANDOM}"
[[ ! -e "$tmp" && ! -L "$tmp" ]] || die 'rollback temporary path already exists'
ln -s -- "$previous" "$tmp"
mv -Tf -- "$tmp" "$current"
if (( was_active )); then systemctl start tallynote.service; fi
printf 'rolled back to %s\n' "$(basename -- "$previous")"
exit 0
fi
[[ -f "$REQUEST_FILE" ]] || die "no queued update request at $REQUEST_FILE"
[[ -L "$PREFIX/current" ]] || die 'current release is not a symlink'
# Prefer the systemd runner, which performs the post-switch health check and
# rollback. The fallback remains useful in development installations where the
# privileged helper has not been installed yet.
if [[ -x /usr/local/libexec/tallynote-update-runner ]]; then
exec /usr/local/libexec/tallynote-update-runner
fi
if [[ -z "$NODE" ]]; then
NODE="$PREFIX/current/runtime/bin/node"
[[ -x "$NODE" ]] || NODE=$(command -v node || true)
fi
[[ -n "$NODE" ]] || die 'node runtime not found'
CLI="$PREFIX/current/dist/server/cli/update.js"
[[ -f "$CLI" ]] || die 'update CLI not found'
was_active=0
if systemctl is-active --quiet tallynote.service; then was_active=1; fi
if (( was_active )); then systemctl stop tallynote.service; fi
set +e
"$NODE" "$CLI" --request-file "$REQUEST_FILE"
result=$?
set -e
if (( result != 0 )); then
rm -f -- "$REQUEST_FILE"
if (( was_active )); then systemctl start tallynote.service || true; fi
die 'update failed; the previous release remains active'
fi
if (( was_active )); then systemctl start tallynote.service || { rm -f -- "$REQUEST_FILE"; die 'updated service failed to start'; }; fi
rm -f -- "$REQUEST_FILE"
printf 'update completed; inspect the update page for details\n'