mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 21:27:02 +01:00
Also bumps the 16 meta/requirements pins the freshness gate flagged (anthropic 1.5.0, numpy 2.5.3, pytest-randomly 5, flake8-bugbear 26.9.9, ruff 0.16.7, pyright 1.1.414, selenium 4.49.0, hypothesis 6.168.0, lxml 6.1.3, pipdeptree 4.2.5, the two types-* stubs) and Flutter 3.47.3 in meta/.fvmrc. One test still expected the pre-move ~/mcp-servers path. The pre-commit hook fed .zsh files to shfmt and shellcheck, neither of which parses zsh (SC2148/SC1071), so no zsh file could be committed since the hook was tightened on 2026-08-22; those now get `zsh -n` instead. The hook's own shfmt pass also staged its reformat of 8 shell files. CI_GREEN_SKIP because main is red on exactly the dependency-freshness finding this commit fixes, which is the documented use of that flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
124 lines
3.6 KiB
Bash
Executable File
124 lines
3.6 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================================================
|
|
# Install the SyncYomi library integrity guard as a user systemd timer.
|
|
#
|
|
# TachiyomiSY 1.13.2 can finish a backup restore, report success, and have
|
|
# written almost nothing (upstream jobobby04/TachiyomiSY#1634, #1638). The app
|
|
# then syncs that near-empty library up, replacing the good server copy. On
|
|
# 2026-08-09 the real library survived only as stale pages in the SQLite WAL.
|
|
#
|
|
# The guard cannot fix the app. It makes the silent case loud, and keeps
|
|
# restorable .tachibk snapshots so recovery never again depends on WAL forensics.
|
|
# ============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
readonly SCRIPT_NAME
|
|
REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
readonly REPO_ROOT
|
|
readonly UNIT_DIR="${HOME}/.config/systemd/user"
|
|
readonly SERVICE_NAME="syncyomi-guard.service"
|
|
readonly TIMER_NAME="syncyomi-guard.timer"
|
|
readonly SNAPSHOT_DIR="${HOME}/services/syncyomi/snapshots"
|
|
readonly STATE_DIR="${HOME}/.local/share/syncyomi_guard"
|
|
readonly DB_PATH="${HOME}/services/syncyomi/config/syncyomi.db"
|
|
|
|
log_info() { echo "[*] $*"; }
|
|
log_ok() { echo "[+] $*"; }
|
|
die() {
|
|
echo "[!] $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
usage() {
|
|
echo "Usage: ${SCRIPT_NAME} [--uninstall]"
|
|
echo " Installs a user timer that verifies the SyncYomi payload every 30 min."
|
|
exit 0
|
|
}
|
|
|
|
require_python() {
|
|
command -v python3 >/dev/null 2>&1 ||
|
|
die "python3 not found — install it with: sudo pacman -S python"
|
|
}
|
|
|
|
# The guard reads the database through sudo-less paths, so it must be able to
|
|
# open it. A root-owned db (the container writes as root) is readable only if
|
|
# the mode allows it; check now rather than failing silently on the first timer.
|
|
check_readable() {
|
|
[[ -f ${DB_PATH} ]] || die "no SyncYomi database at ${DB_PATH}"
|
|
if ! head -c 1 "${DB_PATH}" >/dev/null 2>&1; then
|
|
die "cannot read ${DB_PATH} as $(whoami) — run: sudo chmod o+r '${DB_PATH}'"
|
|
fi
|
|
}
|
|
|
|
write_units() {
|
|
mkdir -p "${UNIT_DIR}" "${SNAPSHOT_DIR}" "${STATE_DIR}"
|
|
|
|
cat >"${UNIT_DIR}/${SERVICE_NAME}" <<EOF
|
|
# Generated by ${SCRIPT_NAME} — do not edit by hand.
|
|
[Unit]
|
|
Description=Verify the SyncYomi library payload has not silently collapsed
|
|
Documentation=https://github.com/jobobby04/TachiyomiSY/issues/1634
|
|
|
|
[Service]
|
|
Type=oneshot
|
|
WorkingDirectory=${REPO_ROOT}
|
|
Environment=PYTHONPATH=${REPO_ROOT}
|
|
ExecStart=/usr/bin/python3 -m python_pkg.syncyomi_guard \\
|
|
--db ${DB_PATH} \\
|
|
--state-dir ${STATE_DIR} \\
|
|
--snapshot-dir ${SNAPSHOT_DIR}
|
|
EOF
|
|
|
|
cat >"${UNIT_DIR}/${TIMER_NAME}" <<EOF
|
|
# Generated by ${SCRIPT_NAME} — do not edit by hand.
|
|
[Unit]
|
|
Description=Periodic SyncYomi library integrity check
|
|
|
|
[Timer]
|
|
OnCalendar=*:00/30
|
|
Persistent=true
|
|
|
|
[Install]
|
|
WantedBy=timers.target
|
|
EOF
|
|
|
|
log_ok "Wrote ${UNIT_DIR}/${SERVICE_NAME} and ${TIMER_NAME}"
|
|
}
|
|
|
|
install_timer() {
|
|
require_python
|
|
check_readable
|
|
write_units
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable --now "${TIMER_NAME}"
|
|
log_ok "Timer enabled (every 30 minutes)"
|
|
|
|
log_info "Running one check now"
|
|
if systemctl --user start "${SERVICE_NAME}"; then
|
|
systemctl --user status "${SERVICE_NAME}" --no-pager -n 10 || true
|
|
else
|
|
die "first run failed — inspect: journalctl --user -u ${SERVICE_NAME}"
|
|
fi
|
|
}
|
|
|
|
uninstall_timer() {
|
|
systemctl --user disable --now "${TIMER_NAME}" 2>/dev/null || true
|
|
rm -f "${UNIT_DIR}/${SERVICE_NAME}" "${UNIT_DIR}/${TIMER_NAME}"
|
|
systemctl --user daemon-reload
|
|
log_ok "Removed timer and service (snapshots in ${SNAPSHOT_DIR} kept)"
|
|
}
|
|
|
|
main() {
|
|
case "${1:-}" in
|
|
--uninstall) uninstall_timer ;;
|
|
-h | --help) usage ;;
|
|
"") install_timer ;;
|
|
*) die "Unknown option: $1" ;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|