first commit
This commit is contained in:
143
scripts/deploy.sh
Normal file
143
scripts/deploy.sh
Normal 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"
|
||||
Reference in New Issue
Block a user