#!/usr/bin/env bash # Core functions for the docker-compose plugin # Set strict mode set -eo pipefail [[ $DOKKU_TRACE ]] && set -x # Import dokku functions if [[ -f "/var/lib/dokku/core-plugins/available/common/functions" ]]; then source /var/lib/dokku/core-plugins/available/common/functions fi # Set default values if not already set PLUGIN_NAME=${PLUGIN_NAME:-docker-compose} PLUGIN_PATH=${PLUGIN_PATH:-$(cd "$(dirname "${BASH_SOURCE[0]}/..")" && pwd)} PLUGIN_DATA_ROOT=${PLUGIN_DATA_ROOT:-"/var/lib/dokku/data/$PLUGIN_NAME"} PLUGIN_LOG_FILE=${PLUGIN_LOG_FILE:-"$PLUGIN_DATA_ROOT/logs/plugin.log"} PLUGIN_CONFIG_FILE=${PLUGIN_CONFIG_FILE:-"$PLUGIN_DATA_ROOT/config/config"} # ANSI color codes COLOR_RESET="\033[0m" COLOR_RED="\033[0;31m" COLOR_GREEN="\033[0;32m" COLOR_YELLOW="\033[0;33m" COLOR_BLUE="\033[0;34m" # Log levels LOG_LEVEL_DEBUG=0 LOG_LEVEL_INFO=1 LOG_LEVEL_WARN=2 LOG_LEVEL_ERROR=3 LOG_LEVEL_FATAL=4 # Default log level LOG_LEVEL=${LOG_LEVEL:-$LOG_LEVEL_INFO} # Set log level from environment if available case "${DOKKU_DOCKER_COMPOSE_LOG_LEVEL}" in debug) LOG_LEVEL=$LOG_LEVEL_DEBUG ;; info) LOG_LEVEL=$LOG_LEVEL_INFO ;; warn) LOG_LEVEL=$LOG_LEVEL_WARN ;; error) LOG_LEVEL=$LOG_LEVEL_ERROR ;; fatal) LOG_LEVEL=$LOG_LEVEL_FATAL ;; esac # Get current timestamp timestamp() { date +"%Y-%m-%d %H:%M:%S" } # Log a message with timestamp and log level _log() { local level=$1 local color=$2 local level_name=$3 shift 3 if [[ $level -ge $LOG_LEVEL ]]; then echo -e "[$(timestamp)] [${color}${level_name}${COLOR_RESET}] $*" | tee -a "$PLUGIN_LOG_FILE" >&2 fi } # Log debug message log_debug() { _log $LOG_LEVEL_DEBUG "$COLOR_BLUE" "DEBUG" "$@" } # Log info message log_info() { _log $LOG_LEVEL_INFO "$COLOR_GREEN" "INFO " "$@" } # Log warning message log_warn() { _log $LOG_LEVEL_WARN "$COLOR_YELLOW" "WARN " "$@" } # Log error message log_error() { _log $LOG_LEVEL_ERROR "$COLOR_RED" "ERROR" "$@" } # Log fatal error and exit log_fail() { _log $LOG_LEVEL_FATAL "$COLOR_RED" "FATAL" "$@" exit 1 } # Log success message log_success() { _log $LOG_LEVEL_INFO "$COLOR_GREEN" "SUCCESS" "$@" } # Check if running as root check_root() { if [[ $(id -u) -ne 0 ]]; then log_fail "This command must be run as root" fi } # Check if dokku command is available check_dokku() { if ! command -v dokku >/dev/null 2>&1; then log_fail "Dokku is not installed or not in PATH" fi } # Load plugin configuration load_config() { if [[ -f "$PLUGIN_CONFIG_FILE" ]]; then # shellcheck source=/dev/null source "$PLUGIN_CONFIG_FILE" fi } # Save plugin configuration save_config() { mkdir -p "$(dirname "$PLUGIN_CONFIG_FILE")" local config config=$(declare -p | grep '^declare -x DOKKU_DOCKER_COMPOSE_' | sed 's/^declare -x //') echo "$config" > "$PLUGIN_CONFIG_FILE" chmod 600 "$PLUGIN_CONFIG_FILE" } # Show help show_help() { cat <&2 } error() { log " ! $*" >&2 exit 1 } warn() { log " ! WARNING: $*" >&2 } info() { [[ $QUIET ]] || log "-----> $*" } # Version checking check_dokku_version() { local required_version="${1:-0.30.0}" local current_version current_version=$(dokku --version | head -n 1 | cut -d' ' -f3) if [[ "$(printf "%s\n%s" "$required_version" "$current_version" | sort -V | head -n1)" != "$required_version" ]]; then error "Dokku version $required_version or higher is required. Current version: $current_version" fi } # Argument parsing parse_args() { local args=("$@") local i=0 while [[ $i -lt ${#args[@]} ]]; do case "${args[$i]}" in -f|--file) ((i++)) COMPOSE_FILE="${args[$i]}" ;; -p|--project) ((i++)) PROJECT_NAME="${args[$i]}" ;; --dry-run) DRY_RUN=true ;; -v|--verbose) set -x ;; -q|--quiet) QUIET=true ;; *) # Handle non-flag arguments if [[ -z "$COMMAND" ]]; then COMMAND="${args[$i]}" else error "Unknown argument: ${args[$i]}" fi ;; esac ((i++)) done } # Check if a command exists command_exists() { command -v "$1" >/dev/null 2>&1 } # Check if a file exists and is readable file_exists() { [[ -r "$1" ]] } # Get the project directory get_project_dir() { echo "${DOKKU_DOCKER_COMPOSE_PROJECT_DIR:-$(pwd)}" } # Get the compose file path get_compose_file() { local project_dir="$(get_project_dir)" echo "${COMPOSE_FILE:-${project_dir}/docker-compose.yml}" } # Validate compose file validate_compose_file() { local compose_file="$(get_compose_file)" if ! file_exists "$compose_file"; then error "Compose file not found: $compose_file" fi # Check if docker-compose or docker compose is available if ! command_exists docker-compose && ! command_exists docker compose; then error "Neither 'docker-compose' nor 'docker compose' command found" fi # Validate compose file syntax if command_exists docker-compose; then docker-compose -f "$compose_file" config -q || error "Invalid compose file: $compose_file" else docker compose -f "$compose_file" config -q || error "Invalid compose file: $compose_file" fi } # Main entry point main() { # Ensure dokku is available if ! command_exists dokku; then error "Dokku is not installed or not in PATH" fi # Check dokku version check_dokku_version "0.30.0" # Parse arguments parse_args "$@" # Execute command case "$COMMAND" in help|--help|-h) show_help ;; version|--version|-v) show_version ;; import) validate_compose_file import_compose_file ;; *) error "Unknown command: $COMMAND" ;; esac } # Import compose file (stub for now) import_compose_file() { local compose_file="$(get_compose_file)" info "Importing compose file: $compose_file" if [[ -n "$DRY_RUN" ]]; then info "Dry run: Would import $compose_file" return 0 fi # Actual import logic will be implemented here info "Import functionality coming soon!" } # Show help show_help() { cat </dev/null || echo 'unknown')" echo "dokku-docker-compose $version" } # Run main function if this file is executed directly if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then main "$@" fi