#!/usr/bin/env bash # # Silo Deployment Script # Pulls from git and deploys silod on the local machine # # Usage: # sudo ./scripts/deploy.sh [options] # # Options: # --no-pull Skip git pull (use current checkout) # --no-build Skip build (use existing binary) # --restart-only Only restart the service # --status Show service status and exit # --help Show this help message # # This script should be run on silo.kindred.internal as root or with sudo. set -euo pipefail # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration REPO_URL="${SILO_REPO_URL:-https://gitea.kindred.internal/kindred/silo-0062.git}" REPO_BRANCH="${SILO_BRANCH:-main}" INSTALL_DIR="/opt/silo" CONFIG_DIR="/etc/silo" BINARY_NAME="silod" SERVICE_NAME="silod" # Flags DO_PULL=true DO_BUILD=true RESTART_ONLY=false STATUS_ONLY=false # Functions log_info() { echo -e "${BLUE}[INFO]${NC} $*" } log_success() { echo -e "${GREEN}[OK]${NC} $*" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $*" } log_error() { echo -e "${RED}[ERROR]${NC} $*" >&2 } die() { log_error "$*" exit 1 } show_help() { head -18 "$0" | grep -E '^#' | sed 's/^# *//' exit 0 } check_root() { if [[ $EUID -ne 0 ]]; then die "This script must be run as root (use sudo)" fi } check_dependencies() { log_info "Checking dependencies..." local missing=() command -v git >/dev/null 2>&1 || missing+=("git") command -v go >/dev/null 2>&1 || missing+=("go (golang)") command -v systemctl >/dev/null 2>&1 || missing+=("systemctl") if [[ ${#missing[@]} -gt 0 ]]; then die "Missing required commands: ${missing[*]}" fi # Check Go version local go_version go_version=$(go version | grep -oP 'go\d+\.\d+' | head -1) log_info "Found Go version: ${go_version}" log_success "All dependencies available" } setup_directories() { log_info "Setting up directories..." # Create directories if they don't exist mkdir -p "${INSTALL_DIR}/bin" mkdir -p "${INSTALL_DIR}/src" mkdir -p "${CONFIG_DIR}/schemas" mkdir -p /var/log/silo # Create silo user if it doesn't exist if ! id -u silo >/dev/null 2>&1; then useradd -r -m -d "${INSTALL_DIR}" -s /sbin/nologin -c "Silo Service" silo log_info "Created silo user" fi log_success "Directories ready" } git_pull() { log_info "Pulling latest code from ${REPO_BRANCH}..." local src_dir="${INSTALL_DIR}/src" if [[ -d "${src_dir}/.git" ]]; then # Existing checkout - pull updates cd "${src_dir}" git fetch origin git checkout "${REPO_BRANCH}" git reset --hard "origin/${REPO_BRANCH}" log_success "Updated to $(git rev-parse --short HEAD)" else # Fresh clone log_info "Cloning repository..." rm -rf "${src_dir}" git clone --branch "${REPO_BRANCH}" "${REPO_URL}" "${src_dir}" cd "${src_dir}" log_success "Cloned $(git rev-parse --short HEAD)" fi # Show version info local version version=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD) log_info "Version: ${version}" } build_binary() { log_info "Building ${BINARY_NAME}..." local src_dir="${INSTALL_DIR}/src" cd "${src_dir}" # Get version from git local version version=$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD) local ldflags="-w -s -X main.Version=${version}" # Build CGO_ENABLED=0 go build -ldflags="${ldflags}" -o "${INSTALL_DIR}/bin/${BINARY_NAME}" ./cmd/silod if [[ ! -f "${INSTALL_DIR}/bin/${BINARY_NAME}" ]]; then die "Build failed: binary not found" fi chmod 755 "${INSTALL_DIR}/bin/${BINARY_NAME}" local size size=$(du -h "${INSTALL_DIR}/bin/${BINARY_NAME}" | cut -f1) log_success "Built ${BINARY_NAME} (${size})" } install_config() { log_info "Installing configuration..." local src_dir="${INSTALL_DIR}/src" # Install config file if it doesn't exist or is different if [[ ! -f "${CONFIG_DIR}/config.yaml" ]]; then cp "${src_dir}/deployments/config.prod.yaml" "${CONFIG_DIR}/config.yaml" chmod 644 "${CONFIG_DIR}/config.yaml" chown root:silo "${CONFIG_DIR}/config.yaml" log_info "Installed config.yaml" else log_info "Config file exists, not overwriting" fi # Install schemas (always update) rm -rf "${CONFIG_DIR}/schemas/"* cp -r "${src_dir}/schemas/"* "${CONFIG_DIR}/schemas/" chmod -R 644 "${CONFIG_DIR}/schemas/"* chown -R root:silo "${CONFIG_DIR}/schemas" log_success "Schemas installed" # Check environment file if [[ ! -f "${CONFIG_DIR}/silod.env" ]]; then cp "${src_dir}/deployments/systemd/silod.env.example" "${CONFIG_DIR}/silod.env" chmod 600 "${CONFIG_DIR}/silod.env" chown root:silo "${CONFIG_DIR}/silod.env" log_warn "Created ${CONFIG_DIR}/silod.env - EDIT THIS FILE WITH CREDENTIALS!" fi } install_systemd() { log_info "Installing systemd service..." local src_dir="${INSTALL_DIR}/src" local service_file="${src_dir}/deployments/systemd/silod.service" if [[ -f "${service_file}" ]]; then cp "${service_file}" /etc/systemd/system/silod.service chmod 644 /etc/systemd/system/silod.service systemctl daemon-reload log_success "Systemd service installed" else die "Service file not found: ${service_file}" fi } set_permissions() { log_info "Setting permissions..." chown -R silo:silo "${INSTALL_DIR}" chown root:silo "${CONFIG_DIR}" chmod 750 "${CONFIG_DIR}" chown silo:silo /var/log/silo chmod 750 /var/log/silo # Binary should be owned by root but executable by silo chown root:root "${INSTALL_DIR}/bin/${BINARY_NAME}" chmod 755 "${INSTALL_DIR}/bin/${BINARY_NAME}" log_success "Permissions set" } restart_service() { log_info "Restarting ${SERVICE_NAME} service..." # Enable if not already systemctl enable "${SERVICE_NAME}" >/dev/null 2>&1 || true # Restart systemctl restart "${SERVICE_NAME}" # Wait for startup sleep 2 if systemctl is-active --quiet "${SERVICE_NAME}"; then log_success "Service started successfully" else log_error "Service failed to start" journalctl -u "${SERVICE_NAME}" -n 20 --no-pager || true die "Deployment failed: service not running" fi } verify_deployment() { log_info "Verifying deployment..." # Wait a moment for service to fully start sleep 2 # Check health endpoint local health_status health_status=$(curl -sf http://localhost:8080/health 2>/dev/null || echo "FAILED") if [[ "${health_status}" == *"ok"* ]] || [[ "${health_status}" == *"healthy"* ]] || [[ "${health_status}" == "{}" ]]; then log_success "Health check passed" else log_warn "Health check returned: ${health_status}" fi # Check ready endpoint (includes DB and MinIO) local ready_status ready_status=$(curl -sf http://localhost:8080/ready 2>/dev/null || echo "FAILED") if [[ "${ready_status}" == *"ok"* ]] || [[ "${ready_status}" == *"ready"* ]] || [[ "${ready_status}" == "{}" ]]; then log_success "Readiness check passed (DB and MinIO connected)" else log_warn "Readiness check returned: ${ready_status}" log_warn "Check credentials in ${CONFIG_DIR}/silod.env" fi # Show version log_info "Deployed version: $("${INSTALL_DIR}/bin/${BINARY_NAME}" --version 2>/dev/null || echo 'unknown')" } show_status() { echo "" log_info "Service Status" echo "============================================" systemctl status "${SERVICE_NAME}" --no-pager -l || true echo "" echo "Recent logs:" journalctl -u "${SERVICE_NAME}" -n 10 --no-pager || true } # Parse arguments while [[ $# -gt 0 ]]; do case $1 in --no-pull) DO_PULL=false shift ;; --no-build) DO_BUILD=false shift ;; --restart-only) RESTART_ONLY=true shift ;; --status) STATUS_ONLY=true shift ;; --help|-h) show_help ;; *) die "Unknown option: $1" ;; esac done # Main execution main() { echo "" log_info "Silo Deployment Script" log_info "======================" echo "" check_root if [[ "${STATUS_ONLY}" == "true" ]]; then show_status exit 0 fi if [[ "${RESTART_ONLY}" == "true" ]]; then restart_service verify_deployment exit 0 fi check_dependencies setup_directories if [[ "${DO_PULL}" == "true" ]]; then git_pull else log_info "Skipping git pull (--no-pull)" cd "${INSTALL_DIR}/src" fi if [[ "${DO_BUILD}" == "true" ]]; then build_binary else log_info "Skipping build (--no-build)" fi install_config install_systemd set_permissions restart_service verify_deployment echo "" log_success "============================================" log_success "Deployment complete!" log_success "============================================" echo "" echo "Useful commands:" echo " sudo systemctl status silod # Check service status" echo " sudo journalctl -u silod -f # Follow logs" echo " curl http://localhost:8080/health # Health check" echo "" } main "$@"