mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 21:07:15 +01:00
`sickdays gui` opens a Tk window modelled on the phone's Sick days screen: a Monday-first month calendar (cat-2 fill, a dot for kept lockers, past days faded and inert), a five-switch panel for the clicked day, and the "Marked sick" list with "Kept: ..." lines. A free day is refused with the phone's wording. Each write starts sync_quietly() on a non-daemon thread. The window class is "sickdays" and Escape closes it. sickdays.gui.KeepPanel is the panel on its own, a plain tk.Frame with no root, mainloop, grab or wm_* call, so screen-locker can embed it under its lock-screen grab. `import sickdays` and the rest of the CLI never import tkinter: the CLI loads the gui module by name only for `gui`, and a subprocess test checks this. The tests drive real Tk on a withdrawn root, and CI now runs them under xvfb-run, as gatelock's CI does. Adds a sickdays.desktop launcher, which install.sh installs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAhbNszs5JKdtnD4uBi3xp
134 lines
4.7 KiB
Bash
Executable File
134 lines
4.7 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# ============================================================================
|
|
# Install the sickdays CLI and its sync timer for the current user.
|
|
#
|
|
# sickdays gets its OWN venv, like freedays: the pool is shared by every gate,
|
|
# so the command that manages it must not depend on whichever app venv
|
|
# happened to be active. freedays goes into the same venv because adding a
|
|
# sick day checks the free-day pool first (a date is never in both).
|
|
# Idempotent: rerun it after any change.
|
|
# ============================================================================
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
UTILS_DIR="$(dirname "$REPO_DIR")"
|
|
readonly SCRIPT_NAME REPO_DIR UTILS_DIR
|
|
readonly STATE_DIR="${HOME}/.local/share/sickdays"
|
|
readonly VENV_DIR="${STATE_DIR}/venv"
|
|
readonly BIN_DIR="${HOME}/.local/bin"
|
|
readonly UNIT_DIR="${HOME}/.config/systemd/user"
|
|
readonly APPS_DIR="${HOME}/.local/share/applications"
|
|
readonly SYSTEM_PYTHON="/usr/bin/python3"
|
|
|
|
log() { printf 'install: %s\n' "$1" >&2; }
|
|
fail() { printf 'install: FAILED -- %s\n' "$1" >&2; exit 1; }
|
|
|
|
usage() {
|
|
echo "Usage: $SCRIPT_NAME [--no-timer]"
|
|
echo " --no-timer install the CLI only; do not enable the sync timer"
|
|
exit 0
|
|
}
|
|
|
|
INSTALL_TIMER=1
|
|
|
|
build_venv() {
|
|
log "building the sickdays venv at $VENV_DIR"
|
|
mkdir -p "$STATE_DIR"
|
|
if [[ ! -x "$VENV_DIR/bin/python" ]]; then
|
|
"$SYSTEM_PYTHON" -m venv "$VENV_DIR" || fail "could not create the venv"
|
|
fi
|
|
# Siblings install from the working tree, not from a tag: this script runs
|
|
# from a checkout, and installing the released tag here would silently
|
|
# test something other than what is on disk.
|
|
"$VENV_DIR/bin/pip" install --quiet --upgrade pip || fail "pip self-upgrade"
|
|
"$VENV_DIR/bin/pip" install --quiet "$UTILS_DIR/crdt-sync" \
|
|
|| fail "the crdt-sync sibling package"
|
|
"$VENV_DIR/bin/pip" install --quiet --no-deps "$UTILS_DIR/freedays" \
|
|
|| fail "the freedays sibling package"
|
|
"$VENV_DIR/bin/pip" install --quiet --no-deps -e "$REPO_DIR" \
|
|
|| fail "sickdays itself"
|
|
}
|
|
|
|
link_cli() {
|
|
log "putting the sickdays command on PATH at $BIN_DIR/sickdays"
|
|
mkdir -p "$BIN_DIR"
|
|
ln -sf "$VENV_DIR/bin/sickdays" "$BIN_DIR/sickdays"
|
|
}
|
|
|
|
install_launcher() {
|
|
# Exec is the bare command, so the launcher follows the $BIN_DIR symlink.
|
|
log "installing the 'Sick days' launcher into $APPS_DIR"
|
|
mkdir -p "$APPS_DIR"
|
|
install -m 644 "$REPO_DIR/sickdays.desktop" "$APPS_DIR/"
|
|
if command -v update-desktop-database >/dev/null; then
|
|
update-desktop-database "$APPS_DIR" || log "NOTE: update-desktop-database failed"
|
|
fi
|
|
}
|
|
|
|
install_timer() {
|
|
log "installing the sync timer"
|
|
mkdir -p "$UNIT_DIR"
|
|
install -m 644 "$REPO_DIR/systemd/sickdays-sync.service" "$UNIT_DIR/"
|
|
install -m 644 "$REPO_DIR/systemd/sickdays-sync.timer" "$UNIT_DIR/"
|
|
systemctl --user daemon-reload || fail "systemctl daemon-reload"
|
|
systemctl --user enable --now sickdays-sync.timer || fail "enabling the timer"
|
|
}
|
|
|
|
verify() {
|
|
# Two environments, either of which can be broken on its own: the CLI's
|
|
# venv, and the system python the gate apps import the library from.
|
|
log "verifying"
|
|
"$BIN_DIR/sickdays" list >/dev/null || fail "the sickdays CLI does not run"
|
|
"$VENV_DIR/bin/python" -c "import freedays" \
|
|
|| fail "freedays is missing from the venv; adding a sick day would refuse"
|
|
"$VENV_DIR/bin/python" -c "import tkinter" \
|
|
|| fail "the venv's python has no tkinter; 'sickdays gui' would not open"
|
|
"$SYSTEM_PYTHON" -c "import sickdays" 2>/dev/null || {
|
|
log "NOTE: the system python cannot import sickdays yet."
|
|
log " The gate apps need it there; their own install.sh does that."
|
|
}
|
|
if [[ "$INSTALL_TIMER" -eq 1 ]]; then
|
|
systemctl --user is-enabled sickdays-sync.timer >/dev/null \
|
|
|| fail "the sync timer is not enabled"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
build_venv
|
|
link_cli
|
|
install_launcher
|
|
if [[ "$INSTALL_TIMER" -eq 1 ]]; then
|
|
install_timer
|
|
fi
|
|
verify
|
|
|
|
echo "============================================================"
|
|
"$BIN_DIR/sickdays" list
|
|
echo "============================================================"
|
|
log "done. 'sickdays add' marks today sick, everywhere."
|
|
if [[ ":$PATH:" != *":$BIN_DIR:"* ]]; then
|
|
log "NOTE: $BIN_DIR is not on your PATH; add it to use 'sickdays' bare."
|
|
fi
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--no-timer)
|
|
INSTALL_TIMER=0
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
main "$@"
|