#!/usr/bin/env bash # Pipekit deployment — idempotent. Re-run after any code update. # # What it does: # 1. Creates the 'pipekit' system user and group (if absent) # 2. Adds the invoking user to the 'pipekit' group # 3. Sets ownership of /opt/pipekit to pipekit:pipekit (group-writable) # 4. Creates Python venv (as pipekit) and installs requirements # 5. Installs /usr/local/bin/pipekit launcher # 6. Creates /etc/pipekit/secrets.env (mode 0640, group pipekit) # 7. Runs 'pipekit init' to create/upgrade the SQLite schema # 8. Registers JDBC driver rows for every jar shipped with jrunner # 9. 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" # Capture the invoking user before re-execing as root INVOKING_USER="${SUDO_USER:-${USER:-}}" # Re-exec as root if needed if [ "$EUID" -ne 0 ]; then exec sudo -E "$0" "$@" fi step() { echo ""; echo "── $* ──"; } echo "== pipekit deploy ==" echo "repo: $REPO_DIR" echo "venv: $VENV_DIR" echo "secrets: $SECRETS_FILE" echo "user: $SERVICE_NAME (invoking user: ${INVOKING_USER:-unknown})" # ── Prerequisites ───────────────────────────────────────────────────────────── step "Checking prerequisites" command -v python3 >/dev/null || { echo "ERROR: python3 not on PATH"; exit 1; } echo " python3: $(python3 --version)" command -v jrunner >/dev/null || { echo "ERROR: jrunner not on PATH — install /opt/jrunner first"; exit 1; } echo " jrunner: $(command -v jrunner)" # ── 1. System user ──────────────────────────────────────────────────────────── step "System user" if ! id -u "$SERVICE_NAME" >/dev/null 2>&1; then echo " Creating system user: $SERVICE_NAME" useradd --system --no-create-home --home-dir /nonexistent \ --shell /usr/sbin/nologin "$SERVICE_NAME" echo " Done." else echo " User '$SERVICE_NAME' already exists (uid=$(id -u "$SERVICE_NAME"))." fi # ── 2. Add invoking user to pipekit group ───────────────────────────────────── step "Group membership" if [ -n "$INVOKING_USER" ] && [ "$INVOKING_USER" != "$SERVICE_NAME" ]; then if id -nG "$INVOKING_USER" | grep -qw "$SERVICE_NAME"; then echo " $INVOKING_USER is already in the '$SERVICE_NAME' group." else echo " Adding $INVOKING_USER to the '$SERVICE_NAME' group." usermod -aG "$SERVICE_NAME" "$INVOKING_USER" echo " Done. Log out and back in for this to take effect." fi else echo " Skipping — could not determine invoking user." fi # ── 3. Ownership ────────────────────────────────────────────────────────────── step "File ownership and permissions" echo " Setting $REPO_DIR → $SERVICE_NAME:$SERVICE_NAME (group-writable)" chown -R "$SERVICE_NAME:$SERVICE_NAME" "$REPO_DIR" chmod -R g+w "$REPO_DIR" echo " Done." # ── 4. Venv + deps ──────────────────────────────────────────────────────────── step "Python venv" if [ -d "$VENV_DIR" ] && [ "$(stat -c '%U' "$VENV_DIR")" != "$SERVICE_NAME" ]; then echo " Venv exists but is not owned by $SERVICE_NAME — recreating." rm -rf "$VENV_DIR" fi if [ ! -d "$VENV_DIR" ]; then echo " Creating venv at $VENV_DIR" sudo -u "$SERVICE_NAME" HOME=/nonexistent python3 -m venv "$VENV_DIR" else echo " Venv already exists at $VENV_DIR." fi echo " Upgrading pip" sudo -u "$SERVICE_NAME" HOME=/nonexistent \ "$VENV_DIR/bin/pip" install --quiet --no-cache-dir --upgrade pip echo " Installing requirements" sudo -u "$SERVICE_NAME" HOME=/nonexistent \ "$VENV_DIR/bin/pip" install --quiet --no-cache-dir -r "$REPO_DIR/requirements.txt" echo " Done." # ── 5. Launcher ─────────────────────────────────────────────────────────────── step "Launcher" chmod +x "$REPO_DIR/bin/pipekit" ln -sf "$REPO_DIR/bin/pipekit" "$LAUNCHER" echo " $LAUNCHER -> $REPO_DIR/bin/pipekit" # ── 6. Secrets file ─────────────────────────────────────────────────────────── step "Secrets file" install -d -m 0755 "$CONFIG_DIR" if [ ! -f "$SECRETS_FILE" ]; then echo " Creating $SECRETS_FILE (mode 0640, group $SERVICE_NAME)" 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 else echo " $SECRETS_FILE already exists — keeping contents." chown "root:$SERVICE_NAME" "$SECRETS_FILE" chmod 0640 "$SECRETS_FILE" echo " Permissions ensured: 0640 group $SERVICE_NAME." fi # ── 7. Schema init ──────────────────────────────────────────────────────────── step "Database schema" echo " Running pipekit init" sudo -u "$SERVICE_NAME" HOME=/nonexistent "$LAUNCHER" init echo " Done." # ── 8. Driver registration ──────────────────────────────────────────────────── step "JDBC driver registration" JR_LIB="$(dirname "$(readlink -f "$(command -v jrunner)")")/../lib" echo " jrunner lib dir: $JR_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 echo " Registering $kind: $(basename "$jar")" sudo -u "$SERVICE_NAME" HOME=/nonexistent "$LAUNCHER" drivers register "$kind" --jar "$jar" else echo " No $pattern found in $JR_LIB — skipping $kind" fi } register_jar db2 "jt400-*.jar" register_jar pg "postgresql-*.jar" register_jar mssql "mssql-jdbc-*.jar" # ── 9. Systemd unit ─────────────────────────────────────────────────────────── step "Systemd unit" if [ ! -f "$UNIT_SRC" ]; then echo " WARNING: $UNIT_SRC not found — skipping" else echo " Installing $UNIT_DST" cp "$UNIT_SRC" "$UNIT_DST" echo " Running systemctl daemon-reload" systemctl daemon-reload echo " Enabling $SERVICE_NAME service" systemctl enable "$SERVICE_NAME" echo " Done." fi echo "" echo "════════════════════════════════════" echo " pipekit deployed successfully." echo "════════════════════════════════════" echo "" echo "Next steps:" echo " 1. Add connection passwords:" echo " sudo pipekit secrets set DB2PW " echo " sudo pipekit secrets set PGPW " echo " 2. Start the service:" echo " sudo systemctl start pipekit" echo " 3. Check it:" echo " sudo systemctl status pipekit" echo " journalctl -u pipekit -f" if [ -n "$INVOKING_USER" ]; then if ! id -nG "$INVOKING_USER" 2>/dev/null | grep -qw "$SERVICE_NAME"; then echo "" echo " NOTE: $INVOKING_USER was added to the '$SERVICE_NAME' group." echo " Log out and back in for write access to take effect." fi fi