first commit
This commit is contained in:
21
scripts/create_venv.sh
Executable file
21
scripts/create_venv.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DEFAULT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
PROJECT_ROOT="${PROJECT_ROOT:-${DEFAULT_ROOT}}"
|
||||
PYTHON_BIN="${PYTHON_BIN:-python3}"
|
||||
|
||||
if ! command -v "${PYTHON_BIN}" >/dev/null 2>&1; then
|
||||
echo "Python executable not found: ${PYTHON_BIN}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
"${PYTHON_BIN}" -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -r backend/requirements.txt
|
||||
|
||||
echo "Virtualenv ready at ${PROJECT_ROOT}/.venv"
|
||||
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"
|
||||
84
scripts/install.sh
Executable file
84
scripts/install.sh
Executable file
@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SOURCE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
SERVICE_NAME="${SERVICE_NAME:-skd}"
|
||||
SERVICE_USER="${SERVICE_USER:-skd}"
|
||||
SERVICE_GROUP="${SERVICE_GROUP:-$SERVICE_USER}"
|
||||
INSTALL_DIR="${INSTALL_DIR:-/opt/sk}"
|
||||
ENV_DIR="/etc/${SERVICE_NAME}"
|
||||
ENV_FILE="${ENV_DIR}/env"
|
||||
SYSTEMD_PATH="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
||||
}
|
||||
|
||||
require_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "Required command not found: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
log "Ensuring service user/group (${SERVICE_USER}) exist..."
|
||||
if ! getent group "${SERVICE_GROUP}" >/dev/null 2>&1; then
|
||||
sudo groupadd -r "${SERVICE_GROUP}"
|
||||
fi
|
||||
if ! id -u "${SERVICE_USER}" >/dev/null 2>&1; then
|
||||
sudo useradd -r -s /usr/sbin/nologin -d "${INSTALL_DIR}" -g "${SERVICE_GROUP}" "${SERVICE_USER}"
|
||||
log "Created system user ${SERVICE_USER}"
|
||||
fi
|
||||
|
||||
require_cmd rsync
|
||||
|
||||
log "Syncing project to ${INSTALL_DIR}..."
|
||||
sudo mkdir -p "${INSTALL_DIR}"
|
||||
sudo rsync -a --delete --exclude '.venv' "${SOURCE_DIR}/" "${INSTALL_DIR}/"
|
||||
sudo chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "${INSTALL_DIR}"
|
||||
|
||||
log "Creating/refreshing virtualenv in ${INSTALL_DIR}..."
|
||||
sudo -u "${SERVICE_USER}" PROJECT_ROOT="${INSTALL_DIR}" "${SOURCE_DIR}/scripts/create_venv.sh"
|
||||
|
||||
log "Preparing environment file..."
|
||||
sudo mkdir -p "${ENV_DIR}"
|
||||
if [[ ! -f "${ENV_FILE}" ]]; then
|
||||
if [[ -f "${SOURCE_DIR}/env.example" ]]; then
|
||||
sudo cp "${SOURCE_DIR}/env.example" "${ENV_FILE}"
|
||||
log "Copied env.example to ${ENV_FILE}; edit SKD_AUTH_TOKEN and SKD_ALLOWED_USERS."
|
||||
else
|
||||
sudo touch "${ENV_FILE}"
|
||||
log "Created empty ${ENV_FILE}; populate required variables."
|
||||
fi
|
||||
sudo chmod 640 "${ENV_FILE}"
|
||||
sudo chown root:"${SERVICE_GROUP}" "${ENV_FILE}"
|
||||
fi
|
||||
|
||||
log "Writing systemd unit to ${SYSTEMD_PATH}..."
|
||||
sudo tee "${SYSTEMD_PATH}" >/dev/null <<EOF
|
||||
[Unit]
|
||||
Description=Safe Kiddo Daemon
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=${SERVICE_USER}
|
||||
Group=${SERVICE_GROUP}
|
||||
WorkingDirectory=${INSTALL_DIR}
|
||||
EnvironmentFile=${ENV_FILE}
|
||||
ExecStart=${INSTALL_DIR}/.venv/bin/uvicorn backend.app:app --host 0.0.0.0 --port 8000
|
||||
Restart=on-failure
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
log "Enabling and starting service..."
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable "${SERVICE_NAME}.service"
|
||||
sudo systemctl restart "${SERVICE_NAME}.service"
|
||||
|
||||
log "Installation finished. Check status: sudo systemctl status ${SERVICE_NAME}.service"
|
||||
14
scripts/run.sh
Executable file
14
scripts/run.sh
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
source .venv/bin/activate
|
||||
|
||||
UVICORN_CMD=${UVICORN_CMD:-uvicorn}
|
||||
HOST=${HOST:-0.0.0.0}
|
||||
PORT=${PORT:-8000}
|
||||
|
||||
exec "${UVICORN_CMD}" backend.app:app --host "${HOST}" --port "${PORT}"
|
||||
23
scripts/update.sh
Executable file
23
scripts/update.sh
Executable file
@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
DEFAULT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||
PROJECT_ROOT="${PROJECT_ROOT:-${DEFAULT_ROOT}}"
|
||||
SERVICE_NAME="${SERVICE_NAME:-skd}"
|
||||
SERVICE_USER="${SERVICE_USER:-skd}"
|
||||
BRANCH="${BRANCH:-main}"
|
||||
|
||||
cd "${PROJECT_ROOT}"
|
||||
|
||||
echo "[*] Fetching updates..."
|
||||
git fetch --all
|
||||
git reset --hard "origin/${BRANCH}"
|
||||
|
||||
echo "[*] Refreshing dependencies..."
|
||||
sudo -u "${SERVICE_USER}" PROJECT_ROOT="${PROJECT_ROOT}" "${SCRIPT_DIR}/create_venv.sh"
|
||||
|
||||
echo "[*] Restarting service..."
|
||||
sudo systemctl restart "${SERVICE_NAME}.service"
|
||||
|
||||
echo "[*] Update finished."
|
||||
Reference in New Issue
Block a user