#!/usr/bin/env bash # dxkit/runtime/native.runtime.sh # # Native runtime driver — runs services directly on the host # without containerization. Supports macOS (Homebrew) and Linux (systemd). # # To activate: # export RUNTIME_DRIVER=native # in globals.env or local.env # export NATIVE_WEB_SERVER=apache # apache | nginx (auto-detected if unset) # # Requirements: # macOS — Homebrew, httpd or nginx, php # Linux — systemd, apache2 or nginx, php-fpm # ============================================ # Platform + web server detection # ============================================ function runtime::native::_platform() { case "$(uname -s)" in Darwin) echo "macos" ;; Linux) echo "linux" ;; *) log::error "Unsupported platform: $(uname -s)" return 1 ;; esac } function runtime::native::_webserver() { if [[ -n "${NATIVE_WEB_SERVER:-}" ]]; then echo "$NATIVE_WEB_SERVER" return fi local platform platform="$(runtime::native::_platform)" if [[ "$platform" == "macos" ]]; then brew list httpd &>/dev/null && echo "apache" && return brew list nginx &>/dev/null && echo "nginx" && return else systemctl list-unit-files apache2.service &>/dev/null && echo "apache" && return systemctl list-unit-files nginx.service &>/dev/null && echo "nginx" && return fi log::error "No supported web server found (apache2 / nginx)" log::error "Install one or set NATIVE_WEB_SERVER=apache|nginx" return 1 } # ============================================ # Service management — platform dispatch # ============================================ function runtime::native::_service_start() { local service="$1" case "$(runtime::native::_platform)" in macos) brew services start "$service" ;; linux) sudo systemctl start "$service" ;; esac } function runtime::native::_service_stop() { local service="$1" case "$(runtime::native::_platform)" in macos) brew services stop "$service" ;; linux) sudo systemctl stop "$service" ;; esac } function runtime::native::_service_running() { local service="$1" case "$(runtime::native::_platform)" in macos) brew services list | awk -v s="$service" '$1==s{print $2}' | grep -q "started" ;; linux) systemctl is-active --quiet "$service" ;; esac } function runtime::native::_service_logs() { local service="$1" case "$(runtime::native::_platform)" in macos) local log_dir log_dir="$(brew --prefix)/var/log" tail -f "${log_dir}/${service}/error.log" "${log_dir}/${service}/access.log" 2>/dev/null \ || tail -f "${log_dir}/${service}.log" 2>/dev/null ;; linux) sudo journalctl -u "$service" -f --no-pager ;; esac } # ============================================ # Service name resolution # ============================================ function runtime::native::_apache_service() { case "$(runtime::native::_platform)" in macos) echo "httpd" ;; linux) echo "apache2" ;; esac } function runtime::native::_nginx_service() { echo "nginx"; } function runtime::native::_fpm_service() { local php_ver php_ver="$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;' 2>/dev/null || echo "8.3")" case "$(runtime::native::_platform)" in macos) echo "php@${php_ver}" ;; linux) echo "php${php_ver}-fpm" ;; esac } function runtime::native::_active_services() { local ws ws="$(runtime::native::_webserver)" case "$ws" in apache) echo "$(runtime::native::_apache_service)" ;; nginx) echo "$(runtime::native::_fpm_service)" echo "$(runtime::native::_nginx_service)" ;; esac } # ============================================ # Interface implementation # ============================================ function runtime::native::up() { log::info "Starting native runtime ($(runtime::native::_webserver))..." local service while IFS= read -r service; do runtime::native::_service_start "$service" done < <(runtime::native::_active_services) log::success "Stack up" } function runtime::native::down() { log::info "Stopping native runtime..." # Stop in reverse order local services=() while IFS= read -r service; do services+=("$service") done < <(runtime::native::_active_services) for ((i=${#services[@]}-1; i>=0; i--)); do runtime::native::_service_stop "${services[i]}" done } function runtime::native::ensure() { local service while IFS= read -r service; do if ! runtime::native::_service_running "$service"; then runtime::native::_service_start "$service" fi done < <(runtime::native::_active_services) } function runtime::native::logs() { local ws ws="$(runtime::native::_webserver)" case "$ws" in apache) runtime::native::_service_logs "$(runtime::native::_apache_service)" ;; nginx) runtime::native::_service_logs "$(runtime::native::_nginx_service)" ;; esac } function runtime::native::list() { printf "%-25s %s\n" "SERVICE" "STATUS" printf "%-25s %s\n" "-------" "------" local service while IFS= read -r service; do if runtime::native::_service_running "$service"; then printf "%-25s %s\n" "$service" "running" else printf "%-25s %s\n" "$service" "stopped" fi done < <(runtime::native::_active_services) } function runtime::native::status() { printf "%-20s %s\n" "Driver:" "native" printf "%-20s %s\n" "Platform:" "$(runtime::native::_platform)" printf "%-20s %s\n" "Web server:" "$(runtime::native::_webserver)" printf "%-20s %s\n" "App root:" "$(ctx::root)" echo "" runtime::native::list } function runtime::native::build() { # Native has no images to build — but vhost generation still needs to happen log::info "Native runtime has no build step (services run directly)" log::info "Reload configs with: dx proxy restart" } function runtime::native::is_inside() { # Native always runs on host — there's no "inside" concept return 1 } function runtime::native::exec() { # No container boundary — run directly in the app root cd "$(ctx::root)" && "$@" } function runtime::native::shell() { cd "$(ctx::root)" && exec "${SHELL:-bash}" }