144 lines
3.9 KiB
Bash
144 lines
3.9 KiB
Bash
#!/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"
|