Scheduling: cron-based group runs via a daemon thread (scheduler.py) started at API startup. Schedules managed inline on the group edit form. last_fired_at persisted before run to prevent double-fire on restart. Requires croniter (added to requirements.txt); DB migration adds last_fired_at column to schedule table. Deploy: deploy.sh now creates the pipekit system user, chowns the repo, builds the venv as pipekit, and installs/enables the systemd unit. systemd/pipekit.service is now a production-ready unit (User=pipekit uncommented). pipekit secrets set preserves existing file permissions instead of resetting to 0600. Driver registration is now idempotent (upsert via get_driver_by_name + update_driver). Docs: CLAUDE.md and SPEC.md updated to reflect groups, scheduling, scheduler-in-API-process architecture, TUI deferred (not dropped), stop-on-failure tradeoff, jrunner as prerequisite, and deploy flow. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
140 lines
6.0 KiB
Bash
Executable File
140 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Pipekit deployment — idempotent. Re-run after any code update.
|
|
#
|
|
# What it does:
|
|
# 1. Creates the 'pipekit' system user (if absent)
|
|
# 2. Chowns /opt/pipekit to pipekit:pipekit
|
|
# 3. Creates Python venv (as pipekit) and installs requirements
|
|
# 4. Installs /usr/local/bin/pipekit launcher
|
|
# 5. Creates /etc/pipekit/secrets.env (mode 0640, group pipekit)
|
|
# 6. Runs 'pipekit init' to create/upgrade the SQLite schema
|
|
# 7. Registers JDBC driver rows for every jar shipped with jrunner
|
|
# 8. Installs and enables the systemd unit (does not start it)
|
|
#
|
|
# Usage:
|
|
# ./deploy.sh # re-execs itself with sudo if needed
|
|
#
|
|
# After deploy:
|
|
# sudo pipekit secrets set KEY VALUE # add connection passwords
|
|
# sudo systemctl start pipekit
|
|
|
|
set -euo pipefail
|
|
|
|
REPO_DIR="${PIPEKIT_REPO:-$(cd "$(dirname "$0")" && pwd)}"
|
|
VENV_DIR="$REPO_DIR/.venv"
|
|
LAUNCHER="/usr/local/bin/pipekit"
|
|
CONFIG_DIR="/etc/pipekit"
|
|
SECRETS_FILE="$CONFIG_DIR/secrets.env"
|
|
SERVICE_NAME="pipekit"
|
|
UNIT_SRC="$REPO_DIR/systemd/pipekit.service"
|
|
UNIT_DST="/etc/systemd/system/pipekit.service"
|
|
|
|
# Re-exec as root if needed
|
|
if [ "$EUID" -ne 0 ]; then
|
|
exec sudo -H -E "$0" "$@"
|
|
fi
|
|
|
|
echo "== pipekit deploy =="
|
|
echo "repo: $REPO_DIR"
|
|
echo "venv: $VENV_DIR"
|
|
echo "secrets: $SECRETS_FILE"
|
|
echo ""
|
|
|
|
# ── Prerequisites ─────────────────────────────────────────────────────────────
|
|
command -v python3 >/dev/null || { echo "ERROR: python3 not on PATH"; exit 1; }
|
|
command -v jrunner >/dev/null || { echo "ERROR: jrunner not on PATH — install /opt/jrunner first"; exit 1; }
|
|
|
|
# ── 1. System user ────────────────────────────────────────────────────────────
|
|
if ! id -u "$SERVICE_NAME" >/dev/null 2>&1; then
|
|
echo "Creating system user: $SERVICE_NAME"
|
|
useradd --system --no-create-home --shell /usr/sbin/nologin "$SERVICE_NAME"
|
|
else
|
|
echo "User $SERVICE_NAME already exists."
|
|
fi
|
|
|
|
# ── 2. Ownership ──────────────────────────────────────────────────────────────
|
|
echo "Setting ownership of $REPO_DIR to $SERVICE_NAME:$SERVICE_NAME"
|
|
chown -R "$SERVICE_NAME:$SERVICE_NAME" "$REPO_DIR"
|
|
|
|
# ── 3. Venv + deps ────────────────────────────────────────────────────────────
|
|
if [ -d "$VENV_DIR" ] && [ "$(stat -c '%U' "$VENV_DIR")" != "$SERVICE_NAME" ]; then
|
|
echo "Removing root-owned venv and recreating as $SERVICE_NAME"
|
|
rm -rf "$VENV_DIR"
|
|
fi
|
|
|
|
if [ ! -d "$VENV_DIR" ]; then
|
|
echo "Creating venv at $VENV_DIR"
|
|
sudo -u "$SERVICE_NAME" python3 -m venv "$VENV_DIR"
|
|
fi
|
|
|
|
echo "Installing Python dependencies"
|
|
sudo -u "$SERVICE_NAME" "$VENV_DIR/bin/pip" install --quiet --upgrade pip
|
|
sudo -u "$SERVICE_NAME" "$VENV_DIR/bin/pip" install --quiet -r "$REPO_DIR/requirements.txt"
|
|
echo "Dependencies installed."
|
|
|
|
# ── 4. Launcher ───────────────────────────────────────────────────────────────
|
|
chmod +x "$REPO_DIR/bin/pipekit"
|
|
ln -sf "$REPO_DIR/bin/pipekit" "$LAUNCHER"
|
|
echo "Launcher: $LAUNCHER -> $REPO_DIR/bin/pipekit"
|
|
|
|
# ── 5. Secrets file ───────────────────────────────────────────────────────────
|
|
install -d -m 0755 "$CONFIG_DIR"
|
|
if [ ! -f "$SECRETS_FILE" ]; then
|
|
install -m 0640 /dev/null "$SECRETS_FILE"
|
|
chown "root:$SERVICE_NAME" "$SECRETS_FILE"
|
|
cat > "$SECRETS_FILE" <<'EOF'
|
|
# pipekit secrets — loaded by the systemd unit as EnvironmentFile.
|
|
# Connection passwords are stored as $KEY references in the DB.
|
|
# Add entries with: sudo pipekit secrets set KEY VALUE
|
|
EOF
|
|
echo "Created $SECRETS_FILE"
|
|
else
|
|
# Ensure correct permissions even if file pre-existed
|
|
chown "root:$SERVICE_NAME" "$SECRETS_FILE"
|
|
chmod 0640 "$SECRETS_FILE"
|
|
echo "Keeping existing $SECRETS_FILE"
|
|
fi
|
|
|
|
# ── 6. Schema init ────────────────────────────────────────────────────────────
|
|
sudo -u "$SERVICE_NAME" "$LAUNCHER" init
|
|
echo "Schema initialised."
|
|
|
|
# ── 7. Driver registration ────────────────────────────────────────────────────
|
|
JR_LIB="$(dirname "$(readlink -f "$(command -v jrunner)")")/../lib"
|
|
register_jar() {
|
|
local kind="$1" pattern="$2"
|
|
local jar
|
|
jar="$(find "$JR_LIB" -maxdepth 1 -name "$pattern" 2>/dev/null | head -1)"
|
|
if [ -n "$jar" ]; then
|
|
sudo -u "$SERVICE_NAME" "$LAUNCHER" drivers register "$kind" --jar "$jar"
|
|
else
|
|
echo " (no $pattern in $JR_LIB — skipping $kind)"
|
|
fi
|
|
}
|
|
register_jar db2 "jt400-*.jar"
|
|
register_jar pg "postgresql-*.jar"
|
|
register_jar mssql "mssql-jdbc-*.jar"
|
|
|
|
# ── 8. Systemd unit ───────────────────────────────────────────────────────────
|
|
if [ ! -f "$UNIT_SRC" ]; then
|
|
echo "WARNING: $UNIT_SRC not found — skipping systemd install"
|
|
else
|
|
cp "$UNIT_SRC" "$UNIT_DST"
|
|
systemctl daemon-reload
|
|
systemctl enable "$SERVICE_NAME"
|
|
echo "Systemd unit installed and enabled."
|
|
fi
|
|
|
|
echo ""
|
|
echo "pipekit deployed."
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. Add connection passwords:"
|
|
echo " sudo pipekit secrets set DB2PW <value>"
|
|
echo " sudo pipekit secrets set PGPW <value>"
|
|
echo " 2. Start the service:"
|
|
echo " sudo systemctl start pipekit"
|
|
echo " 3. Check it:"
|
|
echo " sudo systemctl status pipekit"
|
|
echo " journalctl -u pipekit -f"
|