first commit

This commit is contained in:
2025-12-15 12:07:32 +01:00
commit 6f49528bbd
21 changed files with 1258 additions and 0 deletions

143
scripts/deploy.sh Normal file
View File

@ -0,0 +1,143 @@
#!/usr/bin/env bash
set -euo pipefail
# Deploys the current working tree to a remote host using a small config file (YAML or JSON).
# Usage: scripts/deploy.sh <host-name> [config-file]
# Config default: deploy_hosts.yml with structure:
# hosts:
# - name: kid-laptop
# host: 192.168.13.14
# user: stephan
# port: 22
# install_dir: /opt/sk
# service_name: skd
# service_user: skd
# service_group: skd
if [[ $# -lt 1 ]]; then
echo "Usage: $0 <host-name> [config-file]" >&2
exit 1
fi
HOST_NAME="$1"
CONFIG_FILE="${2:-deploy_hosts.yml}"
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if [[ ! -f "${CONFIG_FILE}" ]]; then
echo "Config file not found: ${CONFIG_FILE}" >&2
exit 1
fi
read_config() {
python3 - <<'PY' "$HOST_NAME" "$CONFIG_FILE"
import importlib
import json
import pathlib
import shlex
import sys
host_name = sys.argv[1]
config_path = pathlib.Path(sys.argv[2])
text = config_path.read_text()
data = None
try:
data = json.loads(text)
except json.JSONDecodeError:
try:
yaml = importlib.import_module("yaml")
except ImportError:
sys.exit("Config is not JSON; install PyYAML (pip install pyyaml) or provide JSON.")
data = yaml.safe_load(text)
hosts = data.get("hosts") if isinstance(data, dict) else None
if not hosts:
sys.exit("Config missing 'hosts' list")
match = None
for entry in hosts:
if entry.get("name") == host_name:
match = entry
break
if not match:
sys.exit(f"No host named '{host_name}' in config")
def req(key, default=None):
val = match.get(key, default)
if val is None:
sys.exit(f"Missing required key '{key}' for host '{host_name}'")
return val
host = req("host")
user = req("user")
port = int(match.get("port", 22))
install_dir = req("install_dir", "/opt/sk")
service_name = match.get("service_name", "skd")
service_user = match.get("service_user", "skd")
service_group = match.get("service_group", service_user)
for key, val in {
"REMOTE_HOST": host,
"REMOTE_USER": user,
"REMOTE_PORT": str(port),
"INSTALL_DIR": install_dir,
"SERVICE_NAME": service_name,
"SERVICE_USER": service_user,
"SERVICE_GROUP": service_group,
}.items():
print(f'{key}={shlex.quote(val)}')
PY
}
eval "$(HOST_NAME="${HOST_NAME}" CONFIG_FILE="${CONFIG_FILE}" read_config)"
ARCHIVE="$(mktemp /tmp/skpkg.XXXXXX.tar.gz)"
trap 'rm -f "${ARCHIVE}"' EXIT
echo "[*] Packaging working tree (excluding .venv/.git)..."
tar czf "${ARCHIVE}" \
--exclude '.venv' \
--exclude '.git' \
--exclude 'sk_deploy.zip' \
-C "${PROJECT_ROOT}" .
REMOTE_PACKAGE="/tmp/skpkg.tar.gz"
echo "[*] Copying package to ${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PACKAGE} ..."
scp -P "${REMOTE_PORT}" "${ARCHIVE}" "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_PACKAGE}"
read -r -d '' REMOTE_CMD <<'RCMD'
set -euo pipefail
INSTALL_DIR="${INSTALL_DIR}"
SERVICE_NAME="${SERVICE_NAME}"
SERVICE_USER="${SERVICE_USER}"
SERVICE_GROUP="${SERVICE_GROUP}"
PKG="${REMOTE_PACKAGE}"
sudo mkdir -p "${INSTALL_DIR}"
sudo rm -rf "${INSTALL_DIR:?}"/*
sudo tar xzf "${PKG}" -C "${INSTALL_DIR}"
sudo chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "${INSTALL_DIR}"
if ! sudo -u "${SERVICE_USER}" test -x "${INSTALL_DIR}/.venv/bin/python3"; then
sudo -u "${SERVICE_USER}" python3 -m venv "${INSTALL_DIR}/.venv"
fi
sudo -u "${SERVICE_USER}" "${INSTALL_DIR}/.venv/bin/pip" install --upgrade pip
sudo -u "${SERVICE_USER}" "${INSTALL_DIR}/.venv/bin/pip" install -r "${INSTALL_DIR}/backend/requirements.txt"
sudo systemctl daemon-reload
sudo systemctl restart "${SERVICE_NAME}.service"
RCMD
echo "[*] Deploying on remote host..."
ssh -p "${REMOTE_PORT}" "${REMOTE_USER}@${REMOTE_HOST}" \
INSTALL_DIR="${INSTALL_DIR}" \
SERVICE_NAME="${SERVICE_NAME}" \
SERVICE_USER="${SERVICE_USER}" \
SERVICE_GROUP="${SERVICE_GROUP}" \
REMOTE_PACKAGE="${REMOTE_PACKAGE}" \
bash -s <<<"${REMOTE_CMD}"
echo "[*] Deployment finished. Check status on remote: sudo systemctl status ${SERVICE_NAME}.service"