#!/usr/bin/env bash # Modifying it manually is not recommended # All components that can be started in the DevBox container. [:COMPONENT_SETTINGS] DEVBOX_COMPONENTS=("devsvc" "notification" "content" "central_storage" "chat" "authentication") log_info() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*" } log_warn() { echo "[WARN] $(date '+%Y-%m-%d %H:%M:%S') $*" } log_error() { echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2 } # :wrapper.bash3_bouncer if [[ "${BASH_VERSINFO:-0}" -lt 3 ]]; then printf "bash version 3 or higher is required\n" >&2 exit 1 fi # :command.master_script # :command.version_command version_command() { echo "$version" } upper() { echo "$1" | tr '[:lower:]' '[:upper:]' } lower() { echo "$1" | tr '[:upper:]' '[:lower:]' } exit_with_message() { local message="$1" local code="${2:-1}" echo echo "============================================================================" echo if [[ $code -eq 0 ]]; then echo "[INFO] $message" else echo "[ERROR] $message" >&2 fi echo echo "============================================================================" echo exit $code } detect_os() { if [[ "$OSTYPE" == "darwin"* ]]; then echo "darwin" elif [[ "$OSTYPE" == "linux-gnu"* ]]; then echo "linux" elif grep -qi microsoft /proc/version; then echo "wsl2" else echo "unknown" fi } # Add a key-value pair to the args array add_arg() { local key="$1" local value="$2" args_keys+=("$key") args_values+=("$value") } # Get the value of a key from the args array get_arg() { local key="$1" local default="${2:-}" local i for i in "${!args_keys[@]}"; do if [ "${args_keys[$i]}" = "$key" ]; then echo "${args_values[$i]}" return 0 fi done echo "$default" return 1 } devbox_init_guidance() { printf "Welcome to DevBox CLI!\n\n" # if $1 is empty, then ask user select a choice or match user input $1 action to be product_id if [[ -z $1 ]]; then # Guide user to select use freeleaps env or custom repository for source code printf "Please choose an option:\n" printf " 1. Use Freeleaps.com repository\n" printf " 2. Use custom repository\n" read -p "Enter your choice (1 or 2): " choice else log_info "Your will start with init product $1 develop environment" product_id=$1 case "$product_id" in freeleaps) choice=1 ;; *) choice=2 ;; esac fi case "$choice" in 1) freeleaps_username="" freeleaps_password="" use_local_component=false use_custom_repository="" freeleaps_components="" git_branch="main" # Ask user for Freeleaps.com username and password read -p "Enter your Freeleaps.com username: " freeleaps_username read -s -p "Enter your Freeleaps.com password: " freeleaps_password echo read -p "Use local component dev environment? (y/n): " choose_local_component if [[ $choose_local_component == "y" ]]; then use_local_component=true fi # Ask user for git branch read -p "Enter git branch to checkout (default: main): " input_git_branch if [[ -n $input_git_branch ]]; then git_branch=$input_git_branch fi add_arg "--freeleaps-username" "$freeleaps_username" add_arg "--freeleaps-password" "$freeleaps_password" add_arg "--use-local-component" "$use_local_component" add_arg "--use-custom-repository" "$use_custom_repository" add_arg "--git-branch" "$git_branch" ;; 2) # Check if product_id is empty if [[ -z $product_id ]]; then # Ask user for product_id read -p "Enter your product_id: " product_id if [[ -z $product_id ]]; then exit_with_message "Product ID is required, please provide a valid product ID." 1 fi fi # Ask user for Freeleaps.com username and password read -p "Enter your Freeleaps.com username: " freeleaps_username read -s -p "Enter your Freeleaps.com password: " freeleaps_password echo # Ask user for git branch read -p "Enter git branch to checkout (default: main): " git_branch if [[ -z $git_branch ]]; then git_branch="main" fi add_arg "--freeleaps-username" "$freeleaps_username" add_arg "--freeleaps-password" "$freeleaps_password" add_arg "--git-branch" "$git_branch" ENCODING_FREELEAPS_USERNAME=$(url_encode "$freeleaps_username") ENCODEING_FREELEAPS_PASSWORD=$(url_encode "$freeleaps_password") use_custom_repository="https://$ENCODING_FREELEAPS_USERNAME:$ENCODEING_FREELEAPS_PASSWORD@gitea.freeleaps.mathmast.com/products/$product_id.git" # Test the repository connection if ! git ls-remote "$use_custom_repository"; then exit_with_message " Failed to connect to the repository. Please check your username and password." 1 fi printf "Repository connection successfully.\n" add_arg "--use-custom-repository" "$use_custom_repository" ;; *) exit_with_message "Invalid choice. Please enter 1 or 2." 1 ;; esac } # :command.usage devbox_usage() { printf "Command\n" printf " devbox : DevBox Command Line Tool for managing the local development environment.\n\n" printf "Usage\n" printf " devbox : Interactive mode to initialize development environment\n" printf " devbox freeleaps : Initialize Freeleaps.com development environment\n" printf " devbox : Initialize specific product development environment\n\n" printf "Arguments\n" printf " COMMAND [Required] : Specify the command to execute.\n" printf " init, i : Initialize the local development environment.\n" printf " deinit, d : De-initialize the local development environment.\n" printf " start, s : Start services in the local development environment.\n" printf " stop, p : Stop services in the local development environment.\n" printf " status, t : Display status of services in the local environment.\n" printf " restart, r : Restart services in the local environment.\n\n" printf "Global Arguments\n" printf " --help, -h : Show this help message and exit.\n" printf " --version, -v : Show version number.\n\n" printf "Examples\n" printf " Initialize development environment interactively:\n" printf " devbox\n\n" printf " Initialize Freeleaps.com development environment:\n" printf " devbox freeleaps\n\n" printf " Initialize specific product development environment:\n" printf " devbox \n\n" printf " Initialize development environment with command:\n" printf " devbox init\n\n" printf " Display help for the 'init' command:\n" printf " devbox init --help\n\n" printf " Start all services:\n" printf " devbox start\n\n" printf " Start specific service:\n" printf " devbox start --component=backend\n\n" printf " Display version information:\n" printf " devbox --version\n" echo } # :command.usage devbox_init_usage() { if [[ -n $long_usage ]]; then printf "Command\n" printf " devbox init : Initialize the local development environment based on DevBox container.\n\n" printf "Arguments\n" printf " --os -o [Optional] : Specifies the operating system. Default: auto.\n" printf " --arch -a [Optional] : Specifies the architecture. Default: auto.\n" printf " --working-home -w [Optional] : Specifies the working home of DevBox CLI. Default: %s/devbox\n" "$HOME" printf " --devbox-container-name -n [Optional] : Specifies the DevBox container name. Default: devbox.\n" printf " --devbox-container-port -p [Optional] : Specifies the container port for DevBox SSH access. Default: 22222.\n" printf " --devbox-nginx-port [Optional] : Specifies the host port for Nginx reverse proxy access. Default: 8080.\n" # add Nginx port description printf " --devbox-backend-port -b [Optional] : Specifies the container port for DevBox backend access. Default: 8002.\n" printf " --devbox-image-repo -r [Optional] : Specifies the DevBox container image repository. Default: docker.io/freeleaps.\n" printf " --devbox-image-name -i [Optional] : Specifies the DevBox container image name. Default: devbox.\n" printf " --devbox-image-tag -t [Optional] : Specifies the DevBox container image tag. Default: latest.\n" printf " --freeleaps-username -u [Optional] : Specifies the Freeleaps.com repository username.\n" printf " --freeleaps-password -x [Optional] : Specifies the Freeleaps.com repository password.\n" printf " --use-local-component -l [Optional] : Check if using local component or online dev environment. Default: false.\n" printf " --use-custom-repository -c [Optional] : Specifies the custom git repository for source code.\n" printf " --git-branch -g [Optional] : Specifies the git branch to checkout. Default: main.\n" printf " --freeleaps-components -m [Optional] : Specifies the Freeleaps.com components to start in the DevBox container.\n" printf " --force -f [Optional] : Force initialization even if resources already exist.\n\n" printf "Global Arguments\n" printf " --help -h : Show this help message and exit.\n\n" printf "Examples\n" printf " Initialize DevBox with Linux OS and ARM64 architecture:\n" printf " devbox init --os linux --arch arm64 --freeleaps-username alice --freeleaps-password secret\n" printf " Initialize with custom container settings:\n" printf " devbox init --devbox-container-name custom-devbox --devbox-container-port 22222 --freeleaps-username alice --freeleaps-password secret\n" printf " Initialize with a specific git branch:\n" printf " devbox init --git-branch develop --freeleaps-username alice --freeleaps-password secret\n" else printf "devbox init - Initialize the local development environment based on DevBox container.\n\n" fi } # :command.usage devbox_deinit_usage() { if [[ -n $long_usage ]]; then printf "Command\n" printf " devbox deinit : De-initialize the local development environment based on DevBox container.\n\n" printf "Arguments\n" printf " --working-home -w [Optional] : Specifies the working home of DevBox CLI. Default: %s/devbox\n" "$HOME" printf " --clear-logs -l [Optional] : Specifies whether to clear log files. Default: true\n" printf " --clear-repo -r [Optional] : Specifies whether to delete the source repository. Default: false\n\n" printf " --clear-all -a [Optional] : Specifies whether to clear all resources. Default: false\n\n" printf "Global Arguments\n" printf " --help, -h : Show this help message and exit.\n\n" printf "Examples\n" printf " De-initialize the local development environment.\n" printf " devbox deinit\n" printf " De-initialize with custom working home and options.\n" printf " devbox deinit --working-home=/tmp/devbox --clear-logs=false --clear-repo=true\n" printf " Clear all resources.\n" printf " devbox deinit --clear-all=true\n" else printf "devbox deinit - De-initialize the local development environment based on DevBox container.\n\n" fi } # :command.usage devbox_start_usage() { if [[ -n $long_usage ]]; then printf "Command\n" printf " devbox start : Start the local development environment based on DevBox container.\n\n" printf "Arguments\n" printf " --component COMPONENT [Optional] : Specifies the name of the component to start (e.g., mongodb, rabbitmq, devsvc, content, central_storage, notification, chat, authentication).\n\n" printf " --freeleaps-endpoint -e [Optional] : Specifies the Freeleaps.com endpoint backend & frontend to start in the DevBox container.\n\n" printf "Global Arguments\n" printf " --help, -h : Show this help message and exit.\n\n" printf "Examples\n" printf " Start all components:\n" printf " devbox start\n" printf " Start a specific component (e.g., backend):\n" printf " devbox start --component=backend\n" else printf "devbox start - Start the local development environment based on DevBox container.\n\n" fi printf "Alias: s\n" echo } # :command.usage devbox_stop_usage() { if [[ -n $long_usage ]]; then printf "Command\n" printf " devbox stop : Stop the local development environment based on DevBox container.\n\n" printf "Arguments\n" printf " --component -c [Optional] : Specifies the name of the component to stop (e.g., mongodb, rabbitmq, devbox, devsvc, content, central_storage, notification, chat, authentication).\n\n" printf " --freeleaps-endpoint -e [Optional] : Specifies the Freeleaps.com endpoint backend & frontend to stop in the DevBox container.\n\n" printf "Global Arguments\n" printf " --help -h : Show this help message and exit.\n\n" printf "Examples\n" printf " stop freeleaps backend service:\n" printf " devbox stop --freeleaps-endpoint=backend\n" printf " stop all services:\n" printf " devbox stop --freeleaps-endpoint=all\n" printf " Stop all components:\n" printf " devbox stop\n\n" printf " Stop a specific component (e.g., backend):\n" printf " devbox stop --component=backend\n" else printf "devbox stop - Stop the local development environment based on DevBox container.\n" fi echo } # :command.usage devbox_status_usage() { if [[ -n $long_usage ]]; then printf "Command\n" printf " devbox status : Display the status of the local development environment based on DevBox container.\n\n" printf "Arguments\n" printf " --component -c [Optional] : Specifies the component to show status (e.g., devbox, devsvc, content, central_storage, notification, chat, authentication).\n\n" printf "Global Arguments\n" printf " --help -h : Show this help message and exit.\n\n" printf "Examples\n" printf " Display status for all components:\n" printf " devbox status\n\n" printf " Display status for a specific component:\n" printf " devbox status --component=content\n" else printf "devbox status - Display the status of the local development environment based on DevBox container.\n" fi echo } # :command.usage devbox_restart_usage() { if [[ -n $long_usage ]]; then printf "Command\n" printf " devbox restart : Restart the local development environment based on DevBox container.\n\n" printf "Arguments\n" printf " --component -c [Optional] : Specifies the component to restart (e.g., devbox, devsvc, content, central_storage, notification, chat, authentication).\n" printf " --freeleaps-endpoint -e [Optional] : Specifies the Freeleaps.com backend & frontend to restart in the DevBox container.\n" printf " --force -f [Optional] : Force the restart operation without prompt.\n\n" printf "Global Arguments\n" printf " --help -h : Show this help message and exit.\n" printf "Examples\n" printf " Restart all components:\n" printf " devbox restart\n\n" printf " Restart a specific component (e.g., backend):\n" printf " devbox restart --component=backend\n" else printf "devbox restart - Restart the local development environment based on DevBox container.\n\n" fi printf "Alias: r\n" echo } # :command.normalize_input # :command.normalize_input_function normalize_input() { local arg passthru flags passthru=false regex='^--([a-zA-Z0-9_\-]+)=(.+)$' regex2='^(-[a-zA-Z0-9])=(.+)$' regex3='^-([a-zA-Z0-9][a-zA-Z0-9]+)$' while [[ $# -gt 0 ]]; do arg="$1" if [[ $passthru == true ]]; then input+=("$arg") elif [[ $arg =~ $regex ]]; then input+=("${BASH_REMATCH[1]}") input+=("${BASH_REMATCH[2]}") elif [[ $arg =~ $regex2 ]]; then input+=("${BASH_REMATCH[1]}") input+=("${BASH_REMATCH[2]}") elif [[ $arg =~ $regex3 ]]; then flags="${BASH_REMATCH[1]}" for ((i = 0; i < ${#flags}; i++)); do input+=("-${flags:i:1}") done elif [[ "$arg" == "--" ]]; then passthru=true input+=("$arg") else input+=("$arg") fi shift done } # :command.inspect_args inspect_args() { # Check and output the simulated args associative array (using args_keys and args_values) if [ ${#args_keys[@]} -gt 0 ]; then # 利用 printf 和 sort 对键进行排序 sorted_keys=$(printf "%s\n" "${args_keys[@]}" | sort) log_info "args:" for key in $sorted_keys; do value="" # Find the value based on the key for i in $(seq 0 $((${#args_keys[@]} - 1))); do if [ "${args_keys[$i]}" = "$key" ]; then value="${args_values[$i]}" break fi done done else log_info "args: none" fi # Check and output the simulated deps associative array (using deps_keys and deps_values) if [ ${#deps_keys[@]} -gt 0 ]; then sorted_keys=$(printf "%s\n" "${deps_keys[@]}" | sort) echo log_info "deps:" for key in $sorted_keys; do value="" for i in $(seq 0 $((${#deps_keys[@]} - 1))); do if [ "${deps_keys[$i]}" = "$key" ]; then value="${deps_values[$i]}" break fi done log_info "- \$deps[$key] = $value" done fi # Check and output the simulated env_vars associative array (using env_var_names) if [ ${#env_var_names[@]} -gt 0 ]; then sorted_names=$(printf "%s\n" "${env_var_names[@]}" | sort) echo log_info "environment variables:" for name in $sorted_names; do # Look up the value based on the name log_info "- \$${name} = ${!name:-}" done fi } install_docker() { log_info "Installing Docker..." # Check if Docker is already installed if command -v docker &>/dev/null; then log_info "Docker is already installed." return 0 fi # Install Docker using the official script if command -v curl &>/dev/null || command -v wget &>/dev/null; then if command -v curl &>/dev/null; then curl -fsSL https://get.docker.com -o get-docker.sh elif command -v wget &>/dev/null; then wget -qO get-docker.sh https://get.docker.com fi if [ -f get-docker.sh ]; then sudo sh get-docker.sh rm -f get-docker.sh return 0 fi fi # Install Docker using package manager if command -v apt-get &>/dev/null; then sudo apt-get update sudo apt-get install -y docker.io return 0 fi if command -v yum &>/dev/null; then sudo yum install -y docker sudo systemctl start docker sudo systemctl enable docker return 0 fi if command -v dnf &>/dev/null; then sudo dnf install -y docker sudo systemctl start docker sudo systemctl enable docker return 0 fi if command -v zypper &>/dev/null; then sudo zypper install -y docker sudo systemctl start docker sudo systemctl enable docker return 0 fi if command -v apk &>/dev/null; then sudo apk add docker sudo rc-update add docker boot sudo service docker start return 0 fi if command -v pacman &>/dev/null; then sudo pacman -S --noconfirm docker sudo systemctl start docker sudo systemctl enable docker return 0 fi if command -v brew &>/dev/null; then brew install docker return 0 fi if command -v snap &>/dev/null; then sudo snap install docker return 0 fi log_info "ERROR: Unable to install Docker automatically. Please install Docker manually." return 1 } check_docker_running() { log_info "Checking if Docker service is running..." # if Docker CLI is installed and Docker daemon is running if docker info >/dev/null 2>&1; then log_info "Docker is running." return 0 fi # if running on WSL, check for Docker socket if grep -qi microsoft /proc/version; then log_info "Detected WSL environment. Verifying /var/run/docker.sock..." if [ -S /var/run/docker.sock ]; then log_info "Docker socket found. Docker should be available via Docker Desktop." return 0 else log_error "Docker socket not found in WSL environment." return 1 fi fi log_info "Docker is not running. Attempting to start it..." # Start Docker service using systemctl or service command if command -v systemctl &>/dev/null; then if systemctl list-units --type=service | grep -q "docker.service"; then log_info "Starting Docker with systemctl..." sudo systemctl start docker && log_info "Docker started successfully." && return 0 fi fi if command -v service &>/dev/null; then if service --status-all | grep -q "docker"; then log_info "Starting Docker with service..." sudo service docker start && log_info "Docker started successfully." && return 0 fi fi if command -v snap &>/dev/null && snap list | grep -q "docker"; then log_info "Starting Docker with snap..." sudo snap start docker && log_info "Docker started successfully." && return 0 fi log_error "Unable to start Docker automatically. Please start it manually." return 1 } build_local_image() { local dockerfile_path="$1" log_info "[Build] use Dockerfile: $(grep '^FROM' "$dockerfile_path")" # Check if the image already exists docker rmi -f $devbox_full_image 2>/dev/null || true # Build the image if ! docker buildx build \ --platform linux/amd64 \ --build-arg BUILDARCH="x86-64-v3" \ --no-cache \ -t $devbox_full_image \ -f "$dockerfile_path" . 2>&1 | tee "$WORKING_HOME/build.log"; then exit_with_message " Image build failed, please check the log file: $WORKING_HOME/build.log" 1 fi # Check if the image is built successfully if ! docker inspect $devbox_full_image | grep -q 'amd64'; then exit_with_message " Image build failed, please check the log file: $WORKING_HOME/build.log" 1 fi } # used for repository username and password encoding url_encode() { echo "$1" | sed 's/@/%40/g' } ############################################### # Initialize the development environment ############################################### init_compile_env() { # Update for export environments [] docker exec -i "$DEVBOX_NAME" bash < /home/devbox/freeleaps/apps/.env export MONGODB_NAME=freeleaps2 export MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ export MONGODB_PORT=27017 export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" export RABBITMQ_HOSTNAME=freeleaps2 export RABBITMQ_HOST=freeleaps2-rabbitmq export RABBITMQ_PORT=5672 export FREELEAPS_ENV=dev export REDIS_URL=redis://freeleaps2-redis:6379/0 export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd export STRIPE_WEBHOOK_SECRET=whsec_S6ZWjSAdR5Cpsn2USH6ZRBqbdBIENjTC export STRIPE_ACCOUNT_WEBHOOK_SECRET=whsec_PgPnkWGhEUiQfnV8aIb5Wmruz7XETJLm export SITE_URL_ROOT=http://localhost export FREELEAPS_DEVSVC_ENDPOINT=http://devsvc:8007/api/devsvc/ export FREELEAPS_CHAT_ENDPOINT=http://chat:8012/api/chat/ export FREELEAPS_CONTENT_ENDPOINT=http://content:8013/api/content/ export FREELEAPS_NOTIFICATION_ENDPOINT=http://notification:8003/api/notification/ export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://central_storage:8005/api/central_storage/ export FREELEAPS_AUTHENTICATION_ENDPOINT=http://authentication:8004/api/auth/ export FREELEAPS_AILAB_ENDPOINT=https://localhost:8009/api/ export KAFKA_SERVER_URL='' export JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b export EMAIL_FROM=freeleaps@freeleaps.com export VITE_PROXY_WEBSOCKET_CHAT_URL=ws://localhost:8012 export VITE_PROXY_API_CHAT_URL=http://localhost:8012 EOFinner # Update set VITE_PROXY_WEBSOCKET_CHAT_URL and VITE_PROXY_API_CHAT_URL in frontend/.env.development echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Update VITE_PROXY_WEBSOCKET_CHAT_URL and VITE_PROXY_API_CHAT_URL in frontend/.env.development" sed -i "s|VITE_PROXY_WEBSOCKET_CHAT_URL=.*|VITE_PROXY_WEBSOCKET_CHAT_URL=ws://chat:8012|g" /home/devbox/freeleaps/frontend/freeleaps/.env.development sed -i "s|VITE_PROXY_API_CHAT_URL=.*|VITE_PROXY_API_CHAT_URL=http://chat:8012|g" /home/devbox/freeleaps/frontend/freeleaps/.env.development else # Online component environment variables echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Use online component dev environment." cat << 'EOFinner' > /home/devbox/freeleaps/apps/.env export MONGODB_NAME=freeleaps2 export MONGODB_PORT=27017 export MONGODB_URI='mongodb+srv://jetli:8IHKx6dZK8BfugGp@freeleaps2.hanbj.mongodb.net/' export RABBITMQ_HOSTNAME=freeleaps2 export RABBITMQ_HOST=52.149.35.244 export RABBITMQ_PORT=5672 export FREELEAPS_ENV=dev export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd export STRIPE_WEBHOOK_SECRET=whsec_S6ZWjSAdR5Cpsn2USH6ZRBqbdBIENjTC export STRIPE_ACCOUNT_WEBHOOK_SECRET=whsec_PgPnkWGhEUiQfnV8aIb5Wmruz7XETJLm export SITE_URL_ROOT=http://localhost/ export FREELEAPS_DEVSVC_ENDPOINT=https://devsvc.freeleaps-alpha.com/api/devsvc/ export FREELEAPS_CHAT_ENDPOINT=https://chat.freeleaps-alpha.com/api/chat/ export FREELEAPS_CONTENT_ENDPOINT=https://content.freeleaps-alpha.com/api/content/ export FREELEAPS_NOTIFICATION_ENDPOINT=https://notification.freeleaps-alpha.com/api/notification/ export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=https://central-storage.freeleaps-alpha.com/api/central_storage/ export FREELEAPS_AUTHENTICATION_ENDPOINT=https://authentication.freeleaps-alpha.com/api/auth/ export FREELEAPS_AILAB_ENDPOINT=https://as010-w2-re-vm.mathmast.com:8009/api/ export KAFKA_SERVER_URL='' export EMAIL_FROM=freeleaps@freeleaps.com export JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b export REDIS_URL=redis://freeleaps2-redis:6379/0 export VITE_PROXY_WEBSOCKET_CHAT_URL=wss://freeleaps-alpha.com export VITE_PROXY_API_CHAT_URL=https://freeleaps-alpha.com EOFinner # Update set VITE_PROXY_WEBSOCKET_CHAT_URL and VITE_PROXY_API_CHAT_URL in frontend/.env.development echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Update VITE_PROXY_WEBSOCKET_CHAT_URL and VITE_PROXY_API_CHAT_URL in frontend/.env.development" sed -i "s|VITE_PROXY_WEBSOCKET_CHAT_URL=.*|VITE_PROXY_WEBSOCKET_CHAT_URL=wss://freeleaps-alpha.com|g" /home/devbox/freeleaps/frontend/freeleaps/.env.development sed -i "s|VITE_PROXY_API_CHAT_URL=.*|VITE_PROXY_API_CHAT_URL=https://freeleaps-alpha.com|g" /home/devbox/freeleaps/frontend/freeleaps/.env.development echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Online component dev environment variables set." fi if true ; then # Git configuration to skip worktree for .env file pushd /home/devbox/freeleaps > /dev/null git config --global core.sparseCheckout true git config --global --add safe.directory /home/devbox/freeleaps git update-index --skip-worktree /home/devbox/freeleaps/frontend/freeleaps/.env.development popd > /dev/null # Load the environment variables source /home/devbox/freeleaps/apps/.env # Create the logs directory if it does not exist mkdir -p /home/devbox/logs # Install the net-tools package for ifconfig sudo apt install net-tools -y ##################################### # Initialize the backend environment, including Python3.11 and venv ##################################### echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Check Python3.11 environment..." sudo apt update sudo apt install python3.11 python3.11-venv -y if ! command -v python3.11 &>/dev/null; then echo echo "============================================" echo echo "[ERROR] Python3.11 is failed to install, please check the log." echo echo "============================================" echo exit 1 fi echo " [INIT] Upgrade pip and ensurepip..." python3.11 -m ensurepip --upgrade python3.11 -m pip install --upgrade pip # If the venv_t directory does not exist, create it pushd /home/devbox/freeleaps/apps > /dev/null if [ ! -d "venv_t" ]; then echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Create Python3.11 virtual environment..." python3.11 -m venv venv_t || { echo "[ERROR] Python3.11 virtual environment creation failed."; exit 1; } sleep 5 fi popd > /dev/null # Install pipreqs for generating requirements.txt python3.11 -m pip install pipreqs echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Backend environment initialization completed." ##################################### # Initialize the frontend environment, including Node.js and npm ##################################### pushd /home/devbox/freeleaps/frontend > /dev/null npm update # 1️⃣ Install pnpm globally npm install -g pnpm # 2️⃣ Verify installation pnpm --version # 3️⃣ Clean up old dependencies if [ -f "pnpm-lock.yaml" ]; then cp pnpm-lock.yaml /tmp/pnpm-lock.yaml.bak fi # Remove node_modules directory rm -rf node_modules # 4️⃣ Install dependencies pnpm store prune # 4️⃣ Install dependencies (ensuring lockfile updates) pnpm install --no-frozen-lockfile \\ --shamefully-hoist \\ --link-workspace-packages false \\ --store-dir /home/tmp/.pnpm-store # 4️⃣ Build the frontend pnpm run build popd > /dev/null # Create the logs directory if it does not exist mkdir -p /home/devbox/logs # Install Nginx and net-tools echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Installing Nginx and net-tools..." sudo apt-get update sudo apt-get install -y nginx net-tools # Stop nginx if it started automatically after installation if sudo service nginx status > /dev/null 2>&1; then echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Stopping Nginx service after installation..." sudo service nginx stop fi # Configure Nginx echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Configuring Nginx..." if [ -f "/home/devbox/freeleaps/frontend/devbox_nginx.conf" ]; then # overwrite the default configuration sudo cp /home/devbox/freeleaps/frontend/devbox_nginx.conf /etc/nginx/nginx.conf if sudo nginx -t; then echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Nginx configuration syntax check successful." else echo "[ERROR] \$(date '+%Y-%m-%d %H:%M:%S') Nginx configuration syntax error. Please check /home/devbox/freeleaps/frontend/devbox_nginx.conf." >&2 exit 1 fi else echo "[WARN] \$(date '+%Y-%m-%d %H:%M:%S') Nginx configuration file /home/devbox/freeleaps/frontend/devbox_nginx.conf not found. Skipping Nginx configuration." fi fi EOF } stop_backend_service() { echo "[BACKEND] $(date '+%Y-%m-%d %H:%M:%S') Stopping backend service..." devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" DEVBOX_NAME=$(cat "$devbox_container_id_file_path") docker exec -i "$DEVBOX_NAME" bash </dev/null || true rm -f /home/devbox/.frontend.pid else echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Frontend process PID file not found or process not running." fi echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Stopping Nginx service..." if sudo service nginx status > /dev/null 2>&1; then sudo service nginx stop echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Nginx stopped." else echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Nginx service is not running." fi EOF } ############################################### # Backend compilation and startup logic ############################################### compile_backend_service() { echo "[BACKEND] $(date '+%Y-%m-%d %H:%M:%S') Start backend service at home path $WORKING_HOME." devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" DEVBOX_NAME=$(cat "$devbox_container_id_file_path") devbox_backend_port_file_path="${WORKING_HOME}/.devbox-backend-port" DEVBOX_BACKEND_PORT=$(cat "$devbox_backend_port_file_path") echo "[BACKEND] $(date '+%Y-%m-%d %H:%M:%S') Start backend service from $DEVBOX_NAME." docker exec -i "$DEVBOX_NAME" bash < /dev/null; then echo echo "============================================================================================" echo echo "[BACKEND] [WARNING] Backend service is running with PID: \$backend_pid, if you want to restart, please stop it first or run devbox restart -e backend." echo echo "============================================================================================" echo exit 0 fi fi fi echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Starting backend compilation and startup..." pushd /home/devbox/freeleaps/apps > /dev/null # Record the git status baseline before compilation baseline_backend=\$(mktemp) git config --global --add safe.directory /home/devbox/freeleaps git status -s > "\$baseline_backend" echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Recorded baseline before compilation: \$baseline_backend" # CHeck if the virtual environment is created if [ ! -f "venv_t/bin/activate" ]; then echo echo "============================================" echo echo "[BACKEND] [ERROR] The virtual environment cannot be created. Please check the log for more information." echo echo "============================================" echo # rm baseline_backend rm -f "\$baseline_backend" exit 1 fi echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Start to activate virtual environment." source venv_t/bin/activate source /home/devbox/freeleaps/apps/.env # Verify the virtual environment is activated if [[ "\$VIRTUAL_ENV" != "" ]]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Virtual environment activate: \$VIRTUAL_ENV" else echo echo "============================================" echo echo "[BACKEND] [ERROR] The virtual environment cannot be startup \$VIRTUAL_ENV, please check the log for more information." echo echo "============================================" echo rm -f "\$baseline_backend" exit 1 fi # Check if it's the first time by verifying if the backend dependencies have been installed if [ ! -f "/home/devbox/.backend_deps_installed" ]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Install backend dependencies..." pip install -r /home/devbox/freeleaps/apps/freeleaps/requirements.txt if ! pip show async_timeout; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') async_timeout is missing. Installing..." pip install async_timeout fi # Generate /home/devbox/tmp/requirements.txt mkdir -p /home/devbox/tmp ## Backup the requirements.txt file cp /home/devbox/freeleaps/apps/freeleaps/requirements.txt /home/devbox/tmp/requirements.txt.bak ORIGINAL_REQ="/home/devbox/freeleaps/apps/freeleaps/requirements.txt" NEW_REQ="/home/devbox/tmp/requirements.txt" # Check if /home/devbox/tmp/requirements.txt exists, if yes, remove it if [ -f "/home/devbox/tmp/requirements.txt" ]; then rm /home/devbox/tmp/requirements.txt fi # Generate /home/devbox/tmp/requirements.txt pipreqs /home/devbox/freeleaps/apps --ignore venv_t --force --use-local --savepath /home/devbox/tmp/requirements.txt if [ ! -f "\$ORIGINAL_REQ" ]; then mv "\$NEW_REQ" "\$ORIGINAL_REQ" else IS_NEW_REQ_ADDED=0 while IFS= read -r line; do # Revome the version number from the line pkg=\$(echo "\$line" | cut -d '=' -f 1 | tr -d ' ') # Check if the package is already in the requirements.txt file if ! grep -i -E "^\${pkg}([=]|$)" "\$ORIGINAL_REQ" > /dev/null; then echo "\$line" >> "\$ORIGINAL_REQ" echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Added package: \${pkg}" IS_NEW_REQ_ADDED=1 else echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Package \${pkg} already exists in requirements.txt" fi done < "\$NEW_REQ" fi if [ \$IS_NEW_REQ_ADDED -eq 1 ]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Reinstalling dependencies..." pip install -r /home/devbox/freeleaps/apps/freeleaps/requirements.txt fi # Undo update for /home/devbox/freeleaps/apps/requirements.txt rm /home/devbox/freeleaps/apps/freeleaps/requirements.txt mv /home/devbox/tmp/requirements.txt.bak /home/devbox/freeleaps/apps/freeleaps/requirements.txt touch /home/devbox/.backend_deps_installed echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Run backend service..." ./start_webapi.sh > /home/devbox/logs/backend.logs 2>&1 & else echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Backend dependencies already installed. Skipping installation." # Check if all dependencies are installed, if not, install them if ! pip check; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Some dependencies are missing. Reinstalling..." pip install -r /home/devbox/freeleaps/apps/freeleaps/requirements.txt fi # pip install async_timeout if not installed if ! pip show async_timeout; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') async_timeout is missing. Installing..." pip install async_timeout fi # Generate /home/devbox/tmp/requirements.txt mkdir -p /home/devbox/tmp ## Backup the requirements.txt file cp /home/devbox/freeleaps/apps/freeleaps/requirements.txt /home/devbox/tmp/requirements.txt.bak ORIGINAL_REQ="/home/devbox/freeleaps/apps/requirements.txt" NEW_REQ="/home/devbox/tmp/requirements.txt" # Check if /home/devbox/tmp/requirements.txt exists, if yes, remove it if [ -f "/home/devbox/tmp/requirements.txt" ]; then rm /home/devbox/tmp/requirements.txt fi pipreqs /home/devbox/freeleaps/apps --ignore venv_t --force --use-local --savepath /home/devbox/tmp/requirements.txt if [ ! -f "\$ORIGINAL_REQ" ]; then mv "\$NEW_REQ" "\$ORIGINAL_REQ" else IS_NEW_REQ_ADDED=0 while IFS= read -r line; do # Revome the version number from the line pkg=\$(echo "\$line" | cut -d '=' -f 1 | tr -d ' ') # Check if the package is already in the requirements.txt file if ! grep -i -E "^\${pkg}([=]|$)" "\$ORIGINAL_REQ" > /dev/null; then echo "\$line" >> "\$ORIGINAL_REQ" echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Added package: \${pkg}" IS_NEW_REQ_ADDED=1 else echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Package \${pkg} already exists in requirements.txt" fi done < "\$NEW_REQ" fi if [ \$IS_NEW_REQ_ADDED -eq 1 ]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Reinstalling dependencies..." pip install -r /home/devbox/freeleaps/apps/requirements.txt fi # Undo update for /home/devbox/freeleaps/apps/requirements.txt rm /home/devbox/freeleaps/apps/freeleaps/requirements.txt mv /home/devbox/tmp/requirements.txt.bak /home/devbox/freeleaps/apps/freeleaps/requirements.txt # Check if the backend service is already running SERVICE_API_ACCESS_PORT=\$(cat /home/devbox/.devbox-backend-port) uvicorn freeleaps.webapi.main:app --reload --host 0.0.0.0 --port \$SERVICE_API_ACCESS_PORT > /home/devbox/logs/backend.logs 2>&1 & fi # Remove tempory file /home/devbox/tmp/requirements.txt if it exists if [ -f "/home/devbox/tmp/requirements.txt" ]; then rm /home/devbox/tmp/requirements.txt fi # Check the health of the backend service: poll to detect HTTP status MAX_ATTEMPTS=30 ATTEMPT=0 echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Checking backend service startup..." while [ \$ATTEMPT -lt \$MAX_ATTEMPTS ]; do echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Backend url http://localhost:${DEVBOX_BACKEND_PORT}/docs" HTTP_CODE=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${DEVBOX_BACKEND_PORT}/docs") if [ "\$HTTP_CODE" -eq 200 ]; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Service started successfully (HTTP \$HTTP_CODE)" # Get the backend service PID by checking the process port with netstat -tulnp | grep ${DEVBOX_BACKEND_PORT} BACKEND_PID=\$(netstat -tulnp 2>/dev/null | grep "${DEVBOX_BACKEND_PORT}" | awk '{print \$7}' | awk -F'/' '{print \$1}') echo "\$BACKEND_PID" > /home/devbox/.backend.pid break else echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Waiting for backend service... Attempt \$((ATTEMPT+1)) with HTTP \$HTTP_CODE" ATTEMPT=\$((ATTEMPT+1)) sleep 10 fi done if [ \$ATTEMPT -eq \$MAX_ATTEMPTS ]; then echo echo "============================================================================================" echo echo echo "[BACKEND] [ERROR] Backend service startup failed. Please check the logs for more information. Logs: ${WORKING_HOME}/logs/backend.logs" echo echo echo "============================================================================================" echo exit 1 fi # Restore git changes caused by compilation current_backend=\$(mktemp) git config --global --add safe.directory /home/devbox/freeleaps git status -s > "\$current_backend" echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Checking git changes after compilation..." while read -r line; do file=\$(echo "\$line" | awk '{print \$2}') if ! grep -q "[[:space:]]\${file}$" "\$baseline_backend"; then echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Restore file \$file" git reset HEAD "\$file" git checkout -- "\$file" fi done < "\$current_backend" rm "\$baseline_backend" "\$current_backend" popd > /dev/null echo "[BACKEND] \$(date '+%Y-%m-%d %H:%M:%S') Backend compilation and startup completed." EOF } ############################################### # Frontend compilation and startup logic ############################################### compile_frontend_service() { echo "[FRONTEND] $(date '+%Y-%m-%d %H:%M:%S') start frontend service at home path $WORKING_HOME." devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" if [ ! -f "$devbox_container_id_file_path" ]; then # Check if devbox container exists by checking the container name of devbox if ! docker ps -a --format "{{.Names}}" | grep -q "devbox"; then exit_with_message "DevBox container is not running. Please start the DevBox container first." 1 fi fi DEVBOX_NAME=$(cat "$devbox_container_id_file_path") DEVBOX_NGINX_HOST_PORT=$(cat "$WORKING_HOME/.devbox-nginx-port") # Nginx Host Port docker exec -i "$DEVBOX_NAME" bash < /dev/null 2>&1; then nginx_running=true fi frontend_running=false if [ -f /home/devbox/.frontend.pid ]; then frontend_pid=\$(cat /home/devbox/.frontend.pid) if [ -n "\$frontend_pid" ] && ps -p "\$frontend_pid" > /dev/null; then frontend_running=true fi fi if [[ "\$nginx_running" == "true" || "\$frontend_running" == "true" ]]; then echo echo "============================================================================================" echo echo "[FRONTEND/NGINX] [WARNING] Frontend process (PID: \${frontend_pid:-N/A}) or Nginx is already running." echo "[FRONTEND/NGINX] [WARNING] If you want to restart, please stop it first (devbox stop -e frontend) or run devbox restart -e frontend." echo echo "============================================================================================" echo exit 1 fi USE_LOCAL_COMPONENT_FLAG="/home/devbox/.use-local-component" USE_LOCAL_COMPONENT_VAL="false" if [ -f "\$USE_LOCAL_COMPONENT_FLAG" ]; then # Read the value from the file USE_LOCAL_COMPONENT_VAL=\$(cat "\$USE_LOCAL_COMPONENT_FLAG") fi pushd /home/devbox/freeleaps/frontend > /dev/null # Record the git status baseline before compilation baseline_frontend=\$(mktemp) git config --global --add safe.directory /home/devbox/freeleaps git status -s > "\$baseline_frontend" echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Recorded baseline before compilation: \$baseline_frontend" # Check if the frontend service is already running according to the package.json and pnpm-lock.yaml files timestamps # Get the timestamps of the package.json and pnpm-lock.yaml files lock_time=\$(stat -c "%Y" pnpm-lock.yaml) modules_time=\$(stat -c "%Y" node_modules) # Calculate the absolute value of the time difference between the lock file and the modules file time_diff=\$(( lock_time - modules_time )) if [ \$time_diff -lt 0 ]; then time_diff=\$(( -time_diff )) fi # Set the threshold for the time difference threshold=150 # Frontend environment variable settings. [:COMPONENT_SETTINGS] echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') USE_LOCAL_COMPONENT_VAL: \$USE_LOCAL_COMPONENT_VAL" if [[ "\$USE_LOCAL_COMPONENT_VAL" == "true" ]]; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Use local component dev environment." sed -i 's#VITE_PROXY_WEBSOCKET_CHAT_URL=ws://localhost:8012#VITE_PROXY_WEBSOCKET_CHAT_URL=ws://chat:8012#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development sed -i 's#VITE_PROXY_API_CHAT_URL=http://localhost:8012#VITE_PROXY_API_CHAT_URL=http://chat:8012#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development else echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Use online component dev environment." sed -i 's#VITE_PROXY_WEBSOCKET_CHAT_URL=wss://localhost:8012#VITE_PROXY_WEBSOCKET_CHAT_URL=wss://freeleaps-alpha.com#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development sed -i 's#VITE_PROXY_API_CHAT_URL=http://localhost:8012#VITE_PROXY_API_CHAT_URL=https://freeleaps-alpha.com#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development fi pushd /home/devbox/freeleaps > /dev/null git update-index --skip-worktree /home/devbox/freeleaps/frontend/freeleaps/.env.development popd > /dev/null if [[ ! -d "node_modules" || "package.json" -nt "node_modules" || \$time_diff -gt \$threshold ]]; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Installing/Updating frontend dependencies..." # Clean up old dependencies rm -rf node_modules # Clean up old pnpm store pnpm store prune # Backup the pnpm-lock.yaml file if [ -f "pnpm-lock.yaml" ]; then mv pnpm-lock.yaml /tmp/pnpm-lock.yaml.bak fi # if /home/tmp/.pnpm-store exists, remove it if [ -d "/home/tmp/.pnpm-store" ]; then rm -rf /home/tmp/.pnpm-store fi # Install dependencies pnpm install --no-frozen-lockfile \\ --shamefully-hoist \\ --link-workspace-packages false \\ --store-dir /home/tmp/.pnpm-store || { echo echo "============================================================================================" echo echo "[FRONTEND] [ERROR] Failed to install dependencies. Please check the logs for more information." echo echo "============================================================================================" echo # rm baseline_frontend if it exists if [ -f "\$baseline_frontend" ]; then rm "\$baseline_frontend" fi exit 1 } fi # Check pnpm result if node_modules exists or if node_modules has not file inside and contains the required dependencies if [[ ! -d "node_modules" || ! -f "node_modules/.bin/vite" ]]; then # Check if the frontend dependencies are installed correctly pnpm install --no-frozen-lockfile || { echo echo "============================================================================================" echo echo "[FRONTEND] [ERROR] Frontend dependencies are not installed correctly. Please check the logs for more information." echo echo "============================================================================================" echo if [ -f "\$baseline_frontend" ]; then rm "\$baseline_frontend" fi exit 1 } # Check if the frontend dependencies are installed correctly if [[ ! -d "node_modules" || ! -f "node_modules/.bin/vite" ]]; then echo echo "============================================================================================" echo echo "[FRONTEND] [ERROR] Frontend dependencies are not installed correctly. Please check the logs for more information." echo echo "============================================================================================" echo if [ -f "\$baseline_frontend" ]; then rm "\$baseline_frontend" fi exit 1 fi fi echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Starting frontend compilation and startup..." # Start the frontend service (pnpm run dev starts both Vue and Nuxt) echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Starting frontend applications (Vue & Nuxt) via pnpm run dev..." # make sure the output of pnpm run dev is redirected to the log file nohup pnpm run dev > /home/devbox/logs/frontend.logs 2>&1 & PNPM_PID=\$! # get the PID of the pnpm run dev process echo "\$PNPM_PID" > /home/devbox/.frontend.pid # save the PID of the pnpm run dev process echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Frontend applications started (PID: \$PNPM_PID). Logs: /home/devbox/logs/frontend.logs" # Check the health of the frontend services (check Nginx endpoint after starting it) # Health check now needs to wait for Nginx to be up and proxying correctly MAX_ATTEMPTS=25 ATTEMPT=0 APPS_READY=false echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Waiting for frontend applications to become ready..." # poll the internal port VUE_PORT=5173 NUXT_PORT=3001 VUE_READY=false NUXT_READY=false while [ \$ATTEMPT -lt \$MAX_ATTEMPTS ]; do # check the Vue port if ! \$VUE_READY && curl --output /dev/null --silent --head --fail http://localhost:\${VUE_PORT}; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Vue app (port \${VUE_PORT}) seems ready." VUE_READY=true fi # check the Nuxt port if ! \$NUXT_READY && curl --output /dev/null --silent --head --fail http://localhost:\${NUXT_PORT}/home/; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Nuxt app (port \${NUXT_PORT}) seems ready." NUXT_READY=true fi if \$VUE_READY && \$NUXT_READY; then APPS_READY=true break fi echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Waiting for apps... Vue Ready: \$VUE_READY, Nuxt Ready: \$NUXT_READY (Attempt \$((ATTEMPT+1))/\$MAX_ATTEMPTS)" ATTEMPT=\$((ATTEMPT+1)) sleep 5 done if [ "\$APPS_READY" = false ]; then echo echo "============================================================================================" echo echo "[FRONTEND] [ERROR] Frontend applications (Vue/Nuxt) did not become ready within the time limit." echo "[FRONTEND] [ERROR] Please check the logs: /home/devbox/logs/frontend.logs" echo echo "============================================================================================" echo # stop the already started pnpm process kill -9 \$PNPM_PID 2>/dev/null || true rm -f /home/devbox/.frontend.pid exit 1 fi # Start Nginx service echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Starting Nginx service..." if sudo service nginx start; then echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Nginx started successfully." sleep 2 # wait for Nginx to be fully started else echo echo "============================================================================================" echo echo "[NGINX] [ERROR] Failed to start Nginx service. Check Nginx logs (/var/log/nginx/) and configuration." echo echo "============================================================================================" echo # stop the already started pnpm process kill -9 \$PNPM_PID 2>/dev/null || true rm -f /home/devbox/.frontend.pid exit 1 fi # Check Nginx health (check port 80 inside container) MAX_ATTEMPTS_NGINX=10 ATTEMPT_NGINX=0 NGINX_READY=false echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Checking Nginx service health..." while [ \$ATTEMPT_NGINX -lt \$MAX_ATTEMPTS_NGINX ]; do HTTP_CODE=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:80/") # check the Nginx internal port 80 if [ "\$HTTP_CODE" -eq 200 ] || [ "\$HTTP_CODE" -eq 404 ]; then # 200 or 404 (if the root path proxy application is not fully started) are considered OK echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Nginx service responding (HTTP \$HTTP_CODE)" NGINX_READY=true break else echo "[NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Waiting for Nginx service... Attempt \$((ATTEMPT_NGINX+1)) (HTTP \$HTTP_CODE)" ATTEMPT_NGINX=\$((ATTEMPT_NGINX+1)) sleep 3 fi done if [ "\$NGINX_READY" = false ]; then echo echo "============================================================================================" echo echo "[NGINX] [ERROR] Nginx service startup failed or did not respond correctly." echo "[NGINX] [ERROR] Please check Nginx logs (/var/log/nginx/) and frontend logs (/home/devbox/logs/frontend.logs)." echo echo "============================================================================================" echo # stop the already started pnpm process and Nginx sudo service nginx stop 2>/dev/null || true kill -9 \$PNPM_PID 2>/dev/null || true rm -f /home/devbox/.frontend.pid exit 1 fi # if /tmp/pnpm-lock.yaml.bak exists, restore it if [ -f "/tmp/pnpm-lock.yaml.bak" ]; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Restore pnpm-lock.yaml..." rm -rf pnpm-lock.yaml mv /tmp/pnpm-lock.yaml.bak pnpm-lock.yaml echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') pnpm-lock.yaml restored." fi # Restore git changes caused by compilation current_frontend=\$(mktemp) git config --global --add safe.directory /home/devbox/freeleaps git status -s > "\$current_frontend" echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Checking git changes after compilation..." while read -r line; do file=\$(echo "\$line" | awk '{print \$2}') if ! grep -q "[[:space:]]\${file}$" "\$baseline_frontend"; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Restore file \$file" git reset HEAD "\$file" git checkout -- "\$file" fi done < "\$current_frontend" rm "\$baseline_frontend" "\$current_frontend" popd > /dev/null if [ \$ATTEMPT -eq \$MAX_ATTEMPTS ]; then echo echo "============================================================================================" echo echo "[FRONTEND] [ERROR] Frontend service startup failed. Please check the logs for more information. Logs: ${WORKING_HOME}/logs/frontend.logs" echo echo "============================================================================================" echo exit 1 fi popd > /dev/null echo echo "[FRONTEND/NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Frontend applications and Nginx proxy started successfully." echo "[FRONTEND/NGINX] \$(date '+%Y-%m-%d %H:%M:%S') Access via host port: ${DEVBOX_NGINX_HOST_PORT}" echo "[FRONTEND/NGINX] \$(date '+%Y-%m-%d %H:%M:%S') - Vue App: http://localhost:${DEVBOX_NGINX_HOST_PORT}/" echo "[FRONTEND/NGINX] \$(date '+%Y-%m-%d %H:%M:%S') - Nuxt App: http://localhost:${DEVBOX_NGINX_HOST_PORT}/home/" echo # Check frontend git checkout status echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Checking git status after compilation..." pushd /home/devbox/freeleaps/frontend > /dev/null git config --global --add safe.directory /home/devbox/freeleaps git status -s if [ -n "\$(git status --porcelain)" ]; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Uncommitted changes found. Resetting..." git reset --hard HEAD else echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') No uncommitted changes found." fi echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Checking for untracked files..." EOF } reset_freeleaps_repo() { echo "[INIT] $(date '+%Y-%m-%d %H:%M:%S') Resetting FreeLeaps repository..." devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" DEVBOX_NAME=$(cat "$devbox_container_id_file_path") docker exec -i "$DEVBOX_NAME" bash < /dev/null git config --global --add safe.directory /home/devbox/freeleaps git reset --hard HEAD echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Checking for uncommitted changes..." git status -s if [ -n "\$(git status --porcelain)" ]; then echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Uncommitted changes found. Resetting..." git reset --hard HEAD else echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') No uncommitted changes found." fi echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') Checking for untracked files..." popd > /dev/null echo "[INIT] \$(date '+%Y-%m-%d %H:%M:%S') FreeLeaps repository reset completed." USE_LOCAL_COMPONENT_FLAG="/home/devbox/.use-local-component" USE_LOCAL_COMPONENT_VAL="false" if [ -f "\$USE_LOCAL_COMPONENT_FLAG" ]; then # Read the value from the file USE_LOCAL_COMPONENT_VAL=\$(cat "\$USE_LOCAL_COMPONENT_FLAG") fi if [[ "\$USE_LOCAL_COMPONENT_VAL" == "true" ]]; then echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Use local component dev environment." sed -i 's#VITE_PROXY_WEBSOCKET_CHAT_URL=.*#VITE_PROXY_WEBSOCKET_CHAT_URL=ws://chat:8012#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development sed -i 's#VITE_PROXY_API_CHAT_URL=.*#VITE_PROXY_API_CHAT_URL=http://chat:8012#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development else echo "[FRONTEND] \$(date '+%Y-%m-%d %H:%M:%S') Use online component dev environment." sed -i 's#VITE_PROXY_WEBSOCKET_CHAT_URL=.*#VITE_PROXY_WEBSOCKET_CHAT_URL=wss://freeleaps-alpha.com#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development sed -i 's#VITE_PROXY_API_CHAT_URL=http://localhost:8012#VITE_PROXY_API_CHAT_URL=https://freeleaps-alpha.com#g' /home/devbox/freeleaps/frontend/freeleaps/.env.development fi EOF } # :command.command_functions # :command.function devbox_init_command() { log_info "[INIT] Starting DevBox environment initialization..." echo # ------------------------------------------------------------------- # 1. Get parameters from command line arguments # ------------------------------------------------------------------- local OS="$args_os" # --os local ARCH="$args_arch" # --arch local DEVBOX_NAME="$args_devbox_container_name" # --devbox-container-name local DEVBOX_PORT="$args_devbox_container_port" # --devbox-container-port local DEVBOX_FRONTEND_PORT="$args_devbox_frontend_port" # --devbox-frontend-port local DEVBOX_BACKEND_PORT="$args_devbox_backend_port" # --devbox-backend-port local DEVBOX_REPO="$args_devbox_image_repo" # --devbox-image-repo local DEVBOX_IMAGE="$args_devbox_image_name" # --devbox-image-name local DEVBOX_TAG="$args_devbox_image_tag" # --devbox-image-tag local WORKING_HOME="${args_working_home:-${WORKING_HOME:-${HOME}/devbox}}" local FREELEAPS_USERNAME="$args_freeleaps_username" # --freeleaps-username local FREELEAPS_PASSWORD="$args_freeleaps_password" # --freeleaps-password local USE_LOCAL_COMPONENT="$args_use_local_component" # --use-local-component local FREELEAPS_COMPONENTS="$args_freeleaps_components" # --freeleaps-components local USE_CUSTOM_REPOSITORY="$args_use_custom_repository" # --use-custom-repository local GIT_BRANCH="$args_git_branch" # --git-branch local DEVBOX_NGINX_PORT="$(get_arg '--devbox-nginx-port')" # --devbox-nginx-port # --force flag to overwrite existing resources local FORCE_INIT="${args_force}" local OS="$(get_arg '--os')" local ARCH="$(get_arg '--arch')" local DEVBOX_NAME="$(get_arg '--devbox-container-name')" local DEVBOX_PORT="$(get_arg '--devbox-container-port')" local DEVBOX_FRONTEND_PORT="$(get_arg '--devbox-frontend-port')" local DEVBOX_BACKEND_PORT="$(get_arg '--devbox-backend-port')" local DEVBOX_REPO="$(get_arg '--devbox-image-repo')" local DEVBOX_IMAGE="$(get_arg '--devbox-image-name')" local DEVBOX_TAG="$(get_arg '--devbox-image-tag')" local WORKING_HOME="$(get_arg '--working-home' "${WORKING_HOME:-${HOME}/devbox}")" local FREELEAPS_USERNAME="$(get_arg '--freeleaps-username')" local FREELEAPS_PASSWORD="$(get_arg '--freeleaps-password')" local USE_LOCAL_COMPONENT="$(get_arg '--use-local-component')" local FREELEAPS_COMPONENTS="$(get_arg '--freeleaps-components')" local USE_CUSTOM_REPOSITORY="$(get_arg '--use-custom-repository')" local GIT_BRANCH="$(get_arg '--git-branch')" local FORCE_INIT="$(get_arg '--force')" local is_pull_all_components=true # set local components to content of DEVBOX_COMPONENTS local components=("${DEVBOX_COMPONENTS[@]}") local start_components=() # Check if using local components USE_LOCAL_COMPONENT_VAL=false if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then USE_LOCAL_COMPONENT_VAL=true fi # if use online components, check if any component image repo is specified if [[ $USE_LOCAL_COMPONENT_VAL == false ]]; then is_pull_all_components=false fi # split FREELEAPS_COMPONENTS freeleaps_components=() if [[ -n "$FREELEAPS_COMPONENTS" ]]; then freeleaps_components=($(echo "$FREELEAPS_COMPONENTS" | tr ',' ' ')) fi # Check if freeleaps_components is not empty, then check if freeleaps_components is valid component if [ ${#freeleaps_components[@]} -gt 0 ]; then for component in "${freeleaps_components[@]}"; do found=false for valid_component in "${components[@]}"; do if [ "$component" = "$valid_component" ]; then found=true break fi done if [ "$found" = false ]; then exit_with_message " Invalid component: $component, please check the component name." 1 fi done start_components=("${freeleaps_components[@]}") else start_components=("${components[@]}") fi log_info "init arch value is : $ARCH " # If is_pull_all_components is true, then pull all components if [[ "$is_pull_all_components" == true ]]; then start_components=("${components[@]}") log_info "Pulling all components..." log_info "start components: ${start_components[@]}" fi # Remove duplicated components start_components=($(echo "${start_components[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' ')) echo "===================================================== " log_info "Parameters:" log_info " OS = $OS" log_info " ARCH = $ARCH" log_info " DEVBOX_NAME = $DEVBOX_NAME" log_info " DEVBOX_PORT = $DEVBOX_PORT" log_info " DEVBOX_FRONTEND_PORT = $DEVBOX_FRONTEND_PORT" log_info " DEVBOX_BACKEND_PORT = $DEVBOX_BACKEND_PORT" log_info " DEVBOX_REPO = $DEVBOX_REPO" log_info " DEVBOX_IMAGE = $DEVBOX_IMAGE" log_info " DEVBOX_TAG = $DEVBOX_TAG" log_info " WORKING_HOME = $WORKING_HOME" log_info " FREELEAPS_USERNAME= $FREELEAPS_USERNAME" log_info " (FREELEAPS_PASSWORD is hidden for security)" log_info " USE_LOCAL_COMPONENT= $USE_LOCAL_COMPONENT" log_info " FREELEAPS_COMPONENTS= ${start_components[@]}" log_info " FORCE_INIT = $FORCE_INIT" log_info " DEVBOX_NGINX_PORT = $DEVBOX_NGINX_PORT (Host Port for Nginx)" log_info " DEVBOX_FRONTEND_PORT = $DEVBOX_FRONTEND_PORT (Internal Vue3 Port)" echo # ------------------------------------------------------------------- # 2. Check OS/ARCH support # (if using auto, detect current system, here just show simple check) # ------------------------------------------------------------------- if [[ "$OS" != "auto" && "$OS" != "linux" && "$OS" != "darwin" && "$OS" != "wsl2" ]]; then exit_with_message " Unsupported OS: $OS, please check the OS." 1 fi # Auto detected $OS if [[ "$OS" == "auto" ]]; then log_info "Auto detecting OS..." OS="$(detect_os)" log_info "Detected OS: $OS" fi if [[ "$ARCH" != "auto" && "$ARCH" != "amd64" && "$ARCH" != "arm64" ]]; then exit_with_message " Unsupported architecture: $ARCH, please check the architecture." 1 fi # Check ARCH match current device ARCH_MICRO="" if [[ "$ARCH" == "auto" ]]; then ARCH="$(uname -m)" if [[ "$ARCH" == "x86_64" ]]; then # Check if the CPU supports AVX2 if grep -q avx2 /proc/cpuinfo; then ARCH="amd64" ARCH_MICRO="v3" log_info "Detected AMD64 architecture." else ARCH="amd64" fi elif [[ "$ARCH" == "aarch64" ]] || [[ "$ARCH" == "arm64" ]]; then ARCH="arm64" fi fi component_tag="latest-linux-arm64" if [[ "$ARCH" == "amd64" ]]; then component_tag="latest-linux-amd64" fi # Default arch tag value if Arch is amd64 then latest-linux-amd64 else latest-linux-arm64 local arch_tag="latest-linux-${ARCH}" # ------------------------------------------------------------------- # 3. Check environment requirements # ------------------------------------------------------------------- # 3.1 Docker Check if ! command -v docker &>/dev/null; then # ------------------------------------------------------------------- # 3.1.1.install docker and check docker running # ------------------------------------------------------------------- if ! install_docker; then exit_with_message " Failed to install Docker or Docker service is not running. Please install Docker and start Docker service." 1 fi if ! check_docker_running; then exit_with_message " Docker service is not running. Please start Docker service." 1 fi sudo usermod -aG docker $USER sudo apt-get update -y sudo apt-get install docker-compose -y fi # 3.2 Check disk space local free_space_kb free_space_kb="$(df -Pk "$HOME" | awk 'END{print $4}')" # 若无法获取或小于 10GB (10485760 KB),报错 if [[ -z "$free_space_kb" || $free_space_kb -lt 10485760 ]]; then exit_with_message " Insufficient disk space (need >10GB). Please free up some space." 1 fi # 3.3 WORKING_HOME Check if ! mkdir -p "$WORKING_HOME" 2>/dev/null; then exit_with_message " Can't create or write to WORKING_HOME: $WORKING_HOME, please check the path." 1 fi # 3.4 Network to docker.com(sample:ping docker.com) if ! ping -c 1 docker.com &>/dev/null; then exit_with_message " Network to Docker.com is not available. Please check your network connection." 1 fi # 3.5 Check if the user has permission to write to WORKING_HOME if [[ -f "$WORKING_HOME/.devbox-instance" && -z "$FORCE_INIT" ]]; then echo read -p "DevBox instance already exists. Do you want to force remove it? (y/N): " ans case "$ans" in [Yy]*) FORCE_INIT=true ;; *) exit_with_message "DevBox instance already exists. Use --force to remove it." 1 ;; esac fi echo # Check if any component is running on the host when force init is not set if [ -z "$FORCE_INIT" ]; then running_containers=$(docker ps --format '{{.Names}}') # Check if any component is running on the host components_to_check=("devbox" "freeleaps2-gitea" "freeleaps2-mongodb" "freeleaps2-rabbitmq" "freeleaps2-redis") components_to_check+=("${DEVBOX_COMPONENTS[@]}") for comp in "${components_to_check[@]}"; do if echo "$running_containers" | grep -qx "$comp"; then echo read -p "Container '$comp' is already running. Do you want to force update it? (y/N): " ans case "$ans" in [Yy]*) FORCE_INIT=true break ;; *) exit_with_message " Container '$comp' is already running. Use --force to override." 1 ;; esac fi done fi echo log_info "Current OS is $OS" # Check if Installed the net-tools netstat under MacOS, WSL2 and Linux OS if [[ "$OS" == "darwin" || "$OS" == "wsl2" || "$OS" == "linux" ]]; then if ! command -v netstat &>/dev/null; then log_info "Installing net-tools package..." if [[ "$OS" == "darwin" ]]; then brew install net-tools elif [[ "$OS" == "wsl2" ]]; then sudo apt-get update -y sudo apt-get install net-tools -y elif [[ "$OS" == "linux" ]]; then sudo apt-get update -y sudo apt-get install net-tools -y else exit_with_message " Failed install net-tools package on OS: $OS, please install it manually." 1 fi else log_info "net-tools package already installed." fi fi # Check if force init is set, if not, check if the ports are in use if [[ -z "$FORCE_INIT" ]]; then log_info "Checking if the ports are in use..." # Check if the gittea, mongodb, rabbitmq, redis ports are in use if netstat -tuln | grep -q ":3000"; then exit_with_message " gitea port 3000 is already in use, please stop the service." 1 fi if netstat -tuln | grep -q ":27017"; then exit_with_message " mongodb port 27017 is already in use, please stop the service." 1 fi if netstat -tuln | grep -q ":5672"; then exit_with_message " rabbitmq port 5672 is already in use, please stop the service." 1 fi if netstat -tuln | grep -q ":15672"; then exit_with_message " rabbitmq port 15672 is already in use, please stop the service." 1 fi if netstat -tuln | grep -q ":6379"; then exit_with_message " redis port 6379 is already in use, please stop the service." 1 fi fi local devbox_full_image="${DEVBOX_REPO}/${DEVBOX_IMAGE}:${DEVBOX_TAG}" # Check local and remote version. User doesn't need to rebuild devbox if local version is consistent with remote version if [[ -n "$DEVBOX_REPO" && -n "$DEVBOX_IMAGE" && -n "$DEVBOX_TAG" ]]; then if docker images --format '{{.Repository}}:{{.Tag}}' | grep -q "^${DEVBOX_REPO}/${DEVBOX_IMAGE}:${DEVBOX_TAG}\$"; then log_info "DevBox image $devbox_full_image already exists." # Check if the local image is not used by any container local devbox_full_image="${DEVBOX_REPO}/${DEVBOX_IMAGE}:${DEVBOX_TAG}" local local_image_id remote_image_id # Get the local image ID log_info "DevBox image $devbox_full_image already exists." local_image_id=$(docker images --format '{{.ID}}' | grep "^${devbox_full_image}\$") remote_image_id=$(docker images --format '{{.ID}}' "$devbox_full_image" | head -n 1) if [[ "$local_image_id" != "$remote_image_id" ]]; then timestamp=$(date +%Y%m%d%H%M%S) local backup_tag="${DEVBOX_TAG}-$timestamp" if docker tag "$local_image_id" "${DEVBOX_REPO}/${DEVBOX_IMAGE}:${backup_tag}"; then log_info "Backup local image $local_image_id to ${DEVBOX_REPO}/${DEVBOX_IMAGE}:${backup_tag}" else log_warn " Failed to backup local image $local_image_id to ${DEVBOX_REPO}/${DEVBOX_IMAGE}:${backup_tag}" fi # Check if the local image is not used by any container if docker ps -a --format '{{.Image}}' | grep -q "^${local_image_id}\$"; then log_info "Local image $local_image_id is used by a container. Stopping it first..." docker ps -a --filter "ancestor=$local_image_id" --format '{{.ID}}' | while read -r container_id; do docker stop "$container_id" &>/dev/null || true docker rm "$container_id" &>/dev/null || true done fi # Delete local image by image id if docker rmi "$local_image_id" &>/dev/null; then log_info "Deleted local image $local_image_id" else log_warn " Failed to delete local image $local_image_id" fi # Pull the latest image from the remote repository log_info "Pulling DevBox image $devbox_full_image..." docker pull "$devbox_full_image" || { exit_with_message " Failed to pull DevBox image $devbox_full_image, please check the image name and tag." 1 } else log_info "The correct version of devbox image exists and use local image." fi else log_info "Pulling DevBox image $devbox_full_image..." docker pull "$devbox_full_image" fi else exit_with_message " DevBox image repository, name, or tag is not specified, please check the parameters." 1 fi # If container with same name exists, remove it if docker ps -a --format '{{.Names}}' | grep -q "^${DEVBOX_NAME}\$"; then if [[ -n "$FORCE_INIT" ]]; then log_info "Removing existing container named $DEVBOX_NAME ..." docker stop "$DEVBOX_NAME" &>/dev/null || true docker rm "$DEVBOX_NAME" &>/dev/null || true # Remove .devbox-instance file rm -f "$WORKING_HOME/.devbox-instance" # Remove .backend.pid .frontend.pid rm -f "$WORKING_HOME/.backend.pid" rm -f "$WORKING_HOME/.frontend.pid" else exit_with_message " Container named $DEVBOX_NAME already exists. Use --force to remove it." 1 fi fi # Create Docker network for DevBox container. TODO: if the network need to be configured in the docker-compose file add some logic to load it from the file DEVBOX_FREELEAPS2_NETWORK="devbox_freeleaps2-network" log_info ' Starting DevBox environment initialization...' # Check if docker network create devbox_freeleaps2-network if ! docker network ls | grep -q "$DEVBOX_FREELEAPS2_NETWORK"; then log_info "Creating Docker network: $DEVBOX_FREELEAPS2_NETWORK" docker network create "$DEVBOX_FREELEAPS2_NETWORK" else log_info "Docker network devbox_freeleaps2-network already exists." fi # Check if use custom repository if [[ -n "$USE_CUSTOM_REPOSITORY" ]]; then log_info "[INIT] Using custom repository." elif [[ -z "$FREELEAPS_USERNAME" || -z "$FREELEAPS_PASSWORD" ]]; then exit_with_message " Username or password is missing. Please provide a valid username and password for freeleaps.com repository." 1 fi log_info ' [INIT] Starting DevBox container...' # Create and start DevBox container local container_id container_id="$( docker run -d \ --name "$DEVBOX_NAME" \ -p "${DEVBOX_PORT}:22" \ -p "0.0.0.0:${DEVBOX_NGINX_PORT}:80" \ -p "0.0.0.0:${DEVBOX_BACKEND_PORT}:8002" \ -v "$WORKING_HOME:/home/devbox" \ -v /var/run/docker.sock:/var/run/docker.sock \ --network "$DEVBOX_FREELEAPS2_NETWORK" \ "$devbox_full_image" 2>/dev/null )" if [[ -z "$container_id" ]]; then exit_with_message " Failed to create DevBox container." 1 fi # record container id, DEVBOX_FRONTEND_PORT, DEVBOX_BACKEND_PORT echo "$container_id" >"$WORKING_HOME/.devbox-instance" echo "$DEVBOX_FRONTEND_PORT" >"$WORKING_HOME/.devbox-frontend-port" echo "$DEVBOX_BACKEND_PORT" >"$WORKING_HOME/.devbox-backend-port" echo "$DEVBOX_NGINX_PORT" >"$WORKING_HOME/.devbox-nginx-port" DOVBOX_CLI_DIR=$(pwd) ECHO_USE_CUSTOM_REPOSITORY="" # Check if USE_CUSTOM_REPOSITORY is empty if [[ -z "$USE_CUSTOM_REPOSITORY" ]]; then ENCODEING_FREELEAPS_USERNAME=$(url_encode "$FREELEAPS_USERNAME") ENCODEING_FREELEAPS_PASSWORD=$(url_encode "$FREELEAPS_PASSWORD") # Test if the user can access the freeleaps.com repository log_info "Testing access to freeleaps.com repository..." if ! git ls-remote "https://$ENCODEING_FREELEAPS_USERNAME:$ENCODEING_FREELEAPS_PASSWORD@gitea.freeleaps.mathmast.com/products/freeleaps.git" &>/dev/null; then exit_with_message " Failed to access freeleaps.com repository. Please check your username and password." 1 fi FREELEAPS_DIR="$WORKING_HOME/freeleaps" FRONTEND_GIT_URL="https://$ENCODEING_FREELEAPS_USERNAME:$ENCODEING_FREELEAPS_PASSWORD@gitea.freeleaps.mathmast.com/products/freeleaps.git" # Check if freeleaps2-frontend exists, if not git clone it if [ ! -d "$FREELEAPS_DIR" ]; then pushd "$WORKING_HOME" >/dev/null log_info "Git cloning gitea.freeleaps.mathmast.com/products/freeleaps.git to $FREELEAPS_DIR" git clone --depth 5 $FRONTEND_GIT_URL # Checkout the specified branch if [[ -n "$GIT_BRANCH" && "$GIT_BRANCH" != "main" ]]; then pushd "$FREELEAPS_DIR" >/dev/null log_info "Checking out branch: $GIT_BRANCH" if ! git checkout $GIT_BRANCH; then log_warn "Failed to checkout branch $GIT_BRANCH, falling back to main branch" git checkout main fi popd >/dev/null fi else pushd "$FREELEAPS_DIR" >/dev/null # Check $WORKING_HOME/freeleaps exists and it is a git repository, if not git clone it if ! git rev-parse --is-inside-work-tree &>/dev/null; then popd >/dev/null # Exit from $FREELEAPS_DIR rm -rf "$FREELEAPS_DIR" # Remove $FREELEAPS_DIR rmdir "$FREELEAPS_DIR" # Remove $FREELEAPS_DIR # Git clone freeleaps.com:3443/products/freeleaps.git log_info "Cloning repository again: $FRONTEND_GIT_URL" uid=$(id -u) gid=$(id -g) sudo chown -R ${uid}:${gid} "$WORKING_HOME" git clone --depth 5 "$FRONTEND_GIT_URL" # Checkout the specified branch if [[ -n "$GIT_BRANCH" && "$GIT_BRANCH" != "main" ]]; then pushd "$FREELEAPS_DIR" >/dev/null log_info "Checking out branch: $GIT_BRANCH" if ! git checkout $GIT_BRANCH; then log_warn "Failed to checkout branch $GIT_BRANCH, falling back to main branch" git checkout main fi popd >/dev/null fi else log_info "Git pulling gitea.freeleaps.mathmast.com/products/freeleaps.git" git fetch --all # Checkout the specified branch if [[ -n "$GIT_BRANCH" ]]; then log_info "Checking out branch: $GIT_BRANCH" # try to checkout the local branch directly if git checkout $GIT_BRANCH 2>/dev/null; then log_info "Checked out local branch: $GIT_BRANCH" git pull origin $GIT_BRANCH # if the local branch does not exist, try to create and checkout the remote branch elif git checkout -b $GIT_BRANCH origin/$GIT_BRANCH 2>/dev/null; then log_info "Checked out remote branch: $GIT_BRANCH" else log_warn "Failed to checkout branch $GIT_BRANCH, falling back to main branch" git checkout main fi else git pull fi fi popd >/dev/null fi else if ! echo "$USE_CUSTOM_REPOSITORY" | grep -Eq '^(https:\/\/|git@|git:\/\/|file:\/\/\/)[^ ]+\.git$'; then exit_with_message " Invalid custom repository URL. Please provide a valid URL." 1 fi # Check if the custom repository is a git repository # Test if the user can access the custom repository log_info "Testing access to custom repository..." if ! git ls-remote "$USE_CUSTOM_REPOSITORY" &>/dev/null; then exit_with_message " Failed to access custom repository. Please check the repository URL." 1 fi ECHO_USE_CUSTOM_REPOSITORY=$(echo "$USE_CUSTOM_REPOSITORY" | sed 's/\(https:\/\/[^:]*\):[^@]*@/\1:****@/') CUSTOM_FOLDER_NAME=$(basename "$USE_CUSTOM_REPOSITORY" .git) CUSTOM_DIR="$WORKING_HOME/$CUSTOM_FOLDER_NAME" if [ ! -d "$CUSTOM_DIR" ]; then pushd "$WORKING_HOME" >/dev/null log_info "Git cloning custom repository: $ECHO_USE_CUSTOM_REPOSITORY" git clone --depth 5 "$USE_CUSTOM_REPOSITORY" # Checkout the specified branch if [[ -n "$GIT_BRANCH" && "$GIT_BRANCH" != "main" ]]; then pushd "$CUSTOM_DIR" >/dev/null log_info "Checking out branch: $GIT_BRANCH" if ! git checkout $GIT_BRANCH; then log_warn "Failed to checkout branch $GIT_BRANCH, falling back to main branch" git checkout main fi popd >/dev/null fi else pushd "$CUSTOM_DIR" >/dev/null # Check $WORKING_HOME/custom exists and it is a git repository, if not git clone it if ! git rev-parse --is-inside-work-tree &>/dev/null; then popd >/dev/null # Exit from $CUSTOM_DIR rm -rf "$CUSTOM_DIR" # Remove $CUSTOM_DIR rmdir "$CUSTOM_DIR" # Remove $CUSTOM_DIR # Git clone custom repository log_info "Cloning repository again: $ECHO_USE_CUSTOM_REPOSITORY" uid=$(id -u) gid=$(id -g) sudo chown -R ${uid}:${gid} "$WORKING_HOME" git clone --depth 5 "$USE_CUSTOM_REPOSITORY" # Checkout the specified branch if [[ -n "$GIT_BRANCH" && "$GIT_BRANCH" != "main" ]]; then pushd "$CUSTOM_DIR" >/dev/null log_info "Checking out branch: $GIT_BRANCH" if ! git checkout $GIT_BRANCH; then log_warn "Failed to checkout branch $GIT_BRANCH, falling back to main branch" git checkout main fi popd >/dev/null fi else log_info "Git pulling custom repository" git fetch # Checkout the specified branch if [[ -n "$GIT_BRANCH" ]]; then log_info "Checking out branch: $GIT_BRANCH" if ! git checkout $GIT_BRANCH; then log_warn "Failed to checkout branch $GIT_BRANCH, falling back to main branch" git checkout main else git pull origin $GIT_BRANCH fi else git pull fi fi popd >/dev/null fi fi pushd $DOVBOX_CLI_DIR >/dev/null # ------------------------------------------------------------------- # 6. linbwang: pull and start other components # ------------------------------------------------------------------- log_info "[INIT] Starting Freeleaps services... Use Local component $USE_LOCAL_COMPONENT_VAL" export ARCH="$ARCH" export WORKING_HOME="$WORKING_HOME" # Save $USE_CUSTOM_REPOSITORY url to .custom-repository file echo "$USE_CUSTOM_REPOSITORY" >"$WORKING_HOME/.custom-repository" # If USE_CUSTOM_REPOSITORY is not empty, initialize the custom repository completed if [[ -n "$USE_CUSTOM_REPOSITORY" ]]; then # Remove the ':' and password from USE_CUSTOM_REPOSITORY echo echo "===========================================================" echo log_info "[INIT] Custom repository initialization completed." log_info "Custom repository is located at: ${WORKING_HOME}/${CUSTOM_FOLDER_NAME}" log_info "Custom repository URL: $ECHO_USE_CUSTOM_REPOSITORY" log_info "Custom repository is ready for use." echo echo "===========================================================" echo exit 0 fi # Check if docker-compose command exists log_info "Cehck if docker-compose command exists" # Check if docker-compose is installed local DC_CMD if command -v docker-compose >/dev/null 2>&1; then DC_CMD="docker-compose" # 如果没有找到 docker-compose,再检查 docker compose(v2 插件) elif docker compose version >/dev/null 2>&1; then DC_CMD="docker compose" else DC_CMD="" log_error "docker-compose is not installed." fi if [[ "$DC_CMD" == "" ]]; then exit_with_message "Please install docker-compose or docker compose (v2) and try again." 1 fi # If USE_LOCAL_COMPONENT is true, then use local components if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then log_info ' = Using local components for Freeleaps services.' export DEVSVC_IMAGE_TAG="$component_tag" export CONTENT_IMAGE_TAG="$component_tag" export CENTRAL_STORAGE_IMAGE_TAG="$component_tag" export AUTHENTICATION_IMAGE_TAG="$component_tag" export NOTIFICATION_IMAGE_TAG="$component_tag" export CHAT_IMAGE_TAG="$component_tag" # Check if gitea_data_backup.tar.gz exists at current script directory, if not exit if [[ ! -f "gitea_data_backup.tar.gz" ]]; then exit_with_message " gitea_data_backup.tar.gz not found. Please make sure it exists in the current directory." 1 fi # Sudo force sudo tar -xzvf gitea_data_backup.tar.gz sudo tar -xzvf gitea_data_backup.tar.gz # Check if data/git, data/gitea, data/ssh directories exist after extracting gitea_data_backup.tar.gz if [[ ! -d "data/git" || ! -d "data/gitea" || ! -d "data/ssh" ]]; then exit_with_message " Failed to extract gitea data backup, please check the backup file." 1 fi # Copy gitea data to the gitea container GITEA_HOST_DIR="${WORKING_HOME}/freeleaps2-gitea" # Remove existing data directories sudo rm -rf ${GITEA_HOST_DIR}/git sudo rm -rf ${GITEA_HOST_DIR}/gitea sudo rm -rf ${GITEA_HOST_DIR}/ssh # Move data directories to the gitea container sudo mv data/git ${GITEA_HOST_DIR}/ sudo mv data/gitea ${GITEA_HOST_DIR}/ sudo mv data/ssh ${GITEA_HOST_DIR}/ # Change the current user of the gitea data directories uid=$(id -u) gid=$(id -g) sudo chown -R ${uid}:${gid} ${GITEA_HOST_DIR} log_info "Gitea data copying is done" # Check if gitea data directories exist in the gitea container if [[ ! -d "${GITEA_HOST_DIR}/gitea" ]]; then exit_with_message " Failed to copy gitea data, please check the data directories." 1 fi mkdir -p ${WORKING_HOME}/logs # for each component create log directory for component in "${start_components[@]}"; do if [[ ! -d "${WORKING_HOME}/logs/${component}" ]]; then mkdir -p "${WORKING_HOME}/logs/${component}" fi done # Check if FORCE_INIT is set, if not just docker compose up or docker-compose up --force # Start Gitea, MongoDB, RabbitMQ and other components containers log_info "start Gitea, MongoDB, RabbitMQ and other components containers" if [[ -z "$FORCE_INIT" ]]; then log_info "Starting Gitea, MongoDB, RabbitMQ and other components containers..." $DC_CMD -f docker-compose.yaml up -d mongodb rabbitmq gitea redis "${start_components[@]}" else log_info "Force starting Gitea, MongoDB, RabbitMQ and other components containers..." $DC_CMD -f docker-compose.yaml up --force-recreate -d mongodb rabbitmq gitea redis "${start_components[@]}" fi gitea_container_id=$(docker ps --no-trunc -a --filter "name=^freeleaps2-gitea$" --format "{{.ID}}") echo "$gitea_container_id" >"$WORKING_HOME/.gitea-instance" mongo_container_id=$(docker ps --no-trunc -a --filter "name=^freeleaps2-mongodb$" --format "{{.ID}}") echo "$mongo_container_id" >"$WORKING_HOME/.mongodb-instance" rabbitmq_container_id=$(docker ps --no-trunc -a --filter "name=^freeleaps2-rabbitmq$" --format "{{.ID}}") echo "$rabbitmq_container_id" >"$WORKING_HOME/.rabbitmq-instance" redis_container_id=$(docker ps --no-trunc -a --filter "name=^freeleaps2-redis$" --format "{{.ID}}") echo "$redis_container_id" >"$WORKING_HOME/.redis-instance" # Get all components container ids and save to .component-instance file for component in "${start_components[@]}"; do tmp_container_id=$(docker ps --no-trunc -a --filter "name=^$component$" --format "{{.ID}}") echo "$tmp_container_id" >"$WORKING_HOME/.${component}-instance" done log_info "${component} container created: $component_container_id" # Check all components are started for component in "${start_components[@]}"; do if [[ -z "$(docker ps -a --format '{{.Names}}' | grep "^$component\$")" ]]; then exit_with_message " Failed to start $component container. Please check the logs for more information." 1 fi done else echo '============================================================' log_info 'Using online components for Freeleaps services.' echo '============================================================' # Start Gitea, MongoDB, RabbitMQ containers if [[ -z "$FORCE_INIT" ]]; then log_info "Starting Gitea, MongoDB, RabbitMQ and other components containers..." $DC_CMD -f docker-compose.yaml up -d gitea mongodb rabbitmq redis else log_info "Force starting Gitea, MongoDB, RabbitMQ and other components containers..." $DC_CMD -f docker-compose.yaml up --force-recreate -d gitea mongodb rabbitmq redis fi # Save MongoDB and RabbitMQ container ids to .mongodb-instance and .rabbitmq-instance mongo_container_id=$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-mongodb\$") echo "$mongo_container_id" >"$WORKING_HOME/.mongodb-instance" rabbitmq_container_id=$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-rabbitmq\$") echo "$rabbitmq_container_id" >"$WORKING_HOME/.rabbitmq-instance" redis_container_id=$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-redis\$") echo "$redis_container_id" >"$WORKING_HOME/.redis-instance" fi # Save $USE_LOCAL_COMPONENT false/true to $WORKING_HOME/.use-local-component echo "$USE_LOCAL_COMPONENT" >"$WORKING_HOME/.use-local-component" pushd $WORKING_HOME IS_START_FRONTEND=false # Make a user input (Y/N) to continue pull freeleaps.com code and start if N then exit echo read -p "Do you want to continue to pull freeleaps.com code and start the services? (Y/N): " user_input echo # Initialize the compile environment init_compile_env if [[ "$user_input" == "N" || "$user_input" == "n" ]]; then # Echo as init job completed and exit reset_freeleaps_repo echo echo "===========================================================" log_info "DevBox init completed successfully!" log_info "DevBox Environment Details:" log_info "DevBox container name: $DEVBOX_NAME" log_info "DevBox container ID: $WORKING_HOME/.devbox-instance" echo "===========================================================" echo exit 0 fi IS_START_FRONTEND=true # Run banckend service and frontend service in the container compile_backend_service compile_frontend_service reset_freeleaps_repo # ------------------------------------------------------------------- # 10. Final notification # ------------------------------------------------------------------- echo echo "===========================================================" echo "DevBox init completed successfully!" echo "DevBox Environment Details:" echo "1. Code repository is located at: ${WORKING_HOME}/freeleaps" echo "2. Access frontend applications via Nginx proxy:" echo " - Vue3 App (Default): http://localhost:${DEVBOX_NGINX_PORT}/" echo " - Nuxt App (/home): http://localhost:${DEVBOX_NGINX_PORT}/home/" echo "3. Log files can be viewed at:" echo " - Backend logs: ${WORKING_HOME}/logs/backend.logs" echo " - Frontend logs: ${WORKING_HOME}/logs/frontend.logs (Contains output from pnpm run dev for both apps)" echo " - Nginx logs inside container: /var/log/nginx/" echo "DevBox container ID: $WORKING_HOME/.devbox-instance" echo "Backend PID: $WORKING_HOME/.backend.pid" echo "Frontend Process PID: $WORKING_HOME/.frontend.pid (pnpm run dev process)" echo "===========================================================" echo # Save $GIT_BRANCH to .git-branch file echo "$GIT_BRANCH" >"$WORKING_HOME/.git-branch" } # :command.function devbox_deinit_command() { # src/deinit_command.sh log_info "It contains the implementation for the 'devbox deinit' command." local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" local CLEAR_LOGS="$(get_arg '--clear-logs')" local CLEAR_REPO="$(get_arg '--clear-repo')" local CLEAR_ALL="$(get_arg '--clear-all')" # print the parameters log_info "Deinitialization parameters:" log_info " WORKING_HOME = $WORKING_HOME" log_info " CLEAR_LOGS = $CLEAR_LOGS" log_info " CLEAR_REPO = $CLEAR_REPO" log_info " CLEAR_ALL = $CLEAR_ALL" log_info "Starting DevBox deinitialization..." # Stop and remove DevBox container if [[ -f "$WORKING_HOME/.devbox-instance" ]]; then local container_id container_id=$(cat "$WORKING_HOME/.devbox-instance") log_info "Stopping and removing DevBox container: $container_id" docker stop "$container_id" &>/dev/null || true docker rm "$container_id" &>/dev/null || true rm -f "$WORKING_HOME/.devbox-instance" # Remove the frontend and backend ports if the file exists rm -f "$WORKING_HOME/.devbox-frontend-port" rm -f "$WORKING_HOME/.devbox-backend-port" # Remove the backend and frontend process IDs rm -f "$WORKING_HOME/.backend.pid" rm -f "$WORKING_HOME/.frontend.pid" else log_info "DevBox container is not running." if [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^devbox\$")" ]]; then # Get the container ID of the DevBox container log_info "DevBox container is stopped." local container_id container_id=$(docker ps -a --format '{{.Names}}' | grep "^devbox\$") log_info "Stopping and removing DevBox container: $container_id" docker stop "$container_id" &>/dev/null || true docker rm "$container_id" &>/dev/null || true fi fi # Check if devbox container is running if [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^devbox\$")" ]]; then # Get the container ID of the DevBox container log_info "DevBox container is stopped." local container_id container_id=$(docker ps -a --format '{{.Names}}' | grep "^devbox\$") exit_with_message " Failed to stop and remove DevBox container when deinitializing. Please try deinit again or manually stop and remove the container." 1 fi if [[ -f "$WORKING_HOME/.gitea-instance" ]]; then local gitea_container_id gitea_container_id=$(cat "$WORKING_HOME/.gitea-instance") log_info "Stopping and removing Gitea container: $gitea_container_id" docker stop "$gitea_container_id" &>/dev/null || true docker rm "$gitea_container_id" &>/dev/null || true # Check if the Gitea container is still running, then use docker compose down to stop and remove the container if [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-gitea\$")" ]]; then log_info "Using docker-compose down to stop and remove Gitea container." $DC_CMD -f docker-compose.yaml down gitea fi rm -f "$WORKING_HOME/.gitea-instance" elif [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-gitea\$")" ]]; then # If the Gitea container is still running, stop and remove it log_info "Gitea container is stopped." local gitea_container_id gitea_container_id=$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-gitea\$") log_info "Stopping and removing Gitea container: $gitea_container_id" docker stop "$gitea_container_id" &>/dev/null || true docker rm "$gitea_container_id" &>/dev/null || true fi # Stop and remove MongoDB container if [[ -f "$WORKING_HOME/.mongodb-instance" ]]; then local mongodb_container_id mongodb_container_id=$(cat "$WORKING_HOME/.mongodb-instance") log_info "Stopping and removing MongoDB container: $mongodb_container_id" docker stop "$mongodb_container_id" &>/dev/null || true docker rm "$mongodb_container_id" &>/dev/null || true # Check if the MongoDB container is still running, then use docker compose down to stop and remove the container if [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-mongodb\$")" ]]; then log_info "Using docker-compose down to stop and remove MongoDB container." $DC_CMD -f docker-compose.yaml down mongodb fi rm -f "$WORKING_HOME/.mongodb-instance" elif [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-mongodb\$")" ]]; then # If the MongoDB container is still running, stop and remove it log_info "MongoDB container is stopped." local mongodb_container_id mongodb_container_id=$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-mongodb\$") log_info "Stopping and removing MongoDB container: $mongodb_container_id" docker stop "$mongodb_container_id" &>/dev/null || true docker rm "$mongodb_container_id" &>/dev/null || true fi if [[ -f "$WORKING_HOME/.redis-instance" ]]; then local redis_container_id redis_container_id=$(cat "$WORKING_HOME/.redis-instance") log_info "Stopping and removing Redis container: $redis_container_id" docker stop "$redis_container_id" &>/dev/null || true docker rm "$redis_container_id" &>/dev/null || true # Check if the Redis container is still running, then use docker compose down to stop and remove the container if [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-redis\$")" ]]; then log_info "Using docker-compose down to stop and remove Redis container." $DC_CMD -f docker-compose.yaml down redis fi rm -f "$WORKING_HOME/.redis-instance" elif [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-redis\$")" ]]; then # If the Redis container is still running, stop and remove it log_info "Redis container is stopped." local redis_container_id redis_container_id=$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-redis\$") log_info "Stopping and removing Redis container: $redis_container_id" docker stop "$redis_container_id" &>/dev/null || true docker rm "$redis_container_id" &>/dev/null || true fi # Stop and remove RabbitMQ container if [[ -f "$WORKING_HOME/.rabbitmq-instance" ]]; then local rabbitmq_container_id rabbitmq_container_id=$(cat "$WORKING_HOME/.rabbitmq-instance") log_info "Stopping and removing RabbitMQ container: $rabbitmq_container_id" docker stop "$rabbitmq_container_id" &>/dev/null || true docker rm "$rabbitmq_container_id" &>/dev/null || true # Check if the RabbitMQ container is still running, then use docker compose down to stop and remove the container if [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-rabbitmq\$")" ]]; then log_info "Using docker-compose down to stop and remove RabbitMQ container." $DC_CMD -f docker-compose.yaml down rabbitmq fi rm -f "$WORKING_HOME/.rabbitmq-instance" elif [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-rabbitmq\$")" ]]; then # If the RabbitMQ container is still running, stop and remove it log_info "RabbitMQ container is stopped." local rabbitmq_container_id rabbitmq_container_id=$(docker ps -a --format '{{.Names}}' | grep "^freeleaps2-rabbitmq\$") log_info "Stopping and removing RabbitMQ container: $rabbitmq_container_id" docker stop "$rabbitmq_container_id" &>/dev/null || true docker rm "$rabbitmq_container_id" &>/dev/null || true fi # Stop and remove other components local components=("${DEVBOX_COMPONENTS[@]}") for component in "${components[@]}"; do if [[ -f "$WORKING_HOME/.${component}-instance" ]]; then local component_container_id component_container_id=$(cat "$WORKING_HOME/.${component}-instance") log_info "Stopping and removing ${component} container: $component_container_id" docker stop "$component_container_id" &>/dev/null || true docker rm "$component_container_id" &>/dev/null || true # Check if the component is still running, then use docker compose down to stop and remove the container if [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^$component\$")" ]]; then log_info "Using docker-compose down to stop and remove $component container." $DC_CMD -f docker-compose.yaml down "$component" fi rm -f "$WORKING_HOME/.${component}-instance" elif [[ -n "$(docker ps -a --format '{{.Names}}' | grep "^$component\$")" ]]; then # If the component is still running, stop and remove it log_info "${component} container is stopped." local component_container_id component_container_id=$(docker ps -a --format '{{.Names}}' | grep "^$component\$") log_info "Stopping and removing ${component} container: $component_container_id" docker stop "$component_container_id" &>/dev/null || true docker rm "$component_container_id" &>/dev/null || true fi done # Clear the DevBox container logs if [[ "$CLEAR_LOGS" == "true" ]]; then log_info "Clearing logs in $WORKING_HOME/logs..." if [[ -d "$WORKING_HOME/logs" ]]; then uid=$(id -u) gid=$(id -g) sudo chown -R ${uid}:${gid} "$WORKING_HOME/logs" rm -rf "$WORKING_HOME/logs" 2>/dev/null || true mkdir -p "$WORKING_HOME/logs" 2>/dev/null || true fi else log_info "Skipping log clearing." fi # Clear the source repository if [[ "$CLEAR_REPO" == "true" && -d "$WORKING_HOME/freeleaps" ]]; then log_info "Deleting source repository at $WORKING_HOME/freeleaps" uid=$(id -u) gid=$(id -g) sudo chown -R ${uid}:${gid} "$WORKING_HOME/freeleaps" rm -rf "$WORKING_HOME/freeleaps" 2>/dev/null || true rmdir "$WORKING_HOME/freeleaps" 2>/dev/null || true else log_info "Skipping repository deletion." fi if [[ "$CLEAR_LOGS" == "true" ]]; then # Check if logs directory is removed if [[ -d "$WORKING_HOME/logs" ]]; then log_warn " $WORKING_HOME/logs still exists after removal." rm -rf "$WORKING_HOME/logs" else log_info "Logs directory removed successfully." fi fi if [[ "$CLEAR_ALL" == "true" ]]; then # Check Y/N to remove the working home directory echo read -p "Do you want to delete the working home directory? This will permanently remove all your local code and environment. (Y/N): " user_input echo if [[ "$user_input" == "Y" || "$user_input" == "y" ]]; then REMOVE_WORKING_HOME=true # Remove the working home directory log_info "Removing working home directory: $WORKING_HOME" if [[ -d "$WORKING_HOME" ]]; then uid=$(id -u) gid=$(id -g) sudo chown -R ${uid}:${gid} "$WORKING_HOME" rm -rf "$WORKING_HOME" 2>/dev/null || true rmdir "$WORKING_HOME" 2>/dev/null || true fi log_info "Working home directory removed successfully." else REMOVE_WORKING_HOME=false log_info "Skipping working home directory removal." fi fi # Sleep 5 seconds to allow the services to stop, for each second echo 5 seconds increase from 1 to 5 in each second by - echo -n "[INFO] $(date '+%Y-%m-%d %H:%M:%S') Stopping services" for i in {1..10}; do echo -n "." sleep 0.5 done echo # Remove the use-local-component file rm -f "$WORKING_HOME/.use-local-component" exit_with_message "DevBox deinitialization completed." 0 } # :command.function devbox_start_command() { local COMPONENT="$(get_arg '--component')" local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" local FREELEAPS_ENDPOINT="$(get_arg '--freeleaps-endpoint')" if [[ "$FREELEAPS_ENDPOINT" == "all" ]]; then export START_FRONTEND=true export START_BACKEND=true elif [[ "$FREELEAPS_ENDPOINT" == "frontend" ]]; then export START_FRONTEND=true export START_BACKEND=false elif [[ "$FREELEAPS_ENDPOINT" == "backend" ]]; then export START_FRONTEND=false export START_BACKEND=true else # Default behavior can be adjusted if needed export START_FRONTEND=false export START_BACKEND=false fi # Check if the DevBox container is running local devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" if [[ ! -f "$devbox_container_id_file_path" ]]; then exit_with_message " DevBox container is not running. Please run 'devbox init' first." 1 fi local devbox_container_id=$(cat "$devbox_container_id_file_path") # Check if use local component if [[ -f "$WORKING_HOME/.use-local-component" ]]; then USE_LOCAL_COMPONENT=$(cat "$WORKING_HOME/.use-local-component") else USE_LOCAL_COMPONENT="false" fi if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then log_info "Using local components for Freeleaps services." USE_LOCAL_COMPONENT="true" else log_info "Using online components for Freeleaps services." USE_LOCAL_COMPONENT="false" fi if [[ "$USE_LOCAL_COMPONENT" == "true" ]]; then # If no component is specified, start all components if [[ -z "$COMPONENT" ]]; then COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis" "devbox") COMPONENTS+=("${DEVBOX_COMPONENTS[@]}") else COMPONENTS=("$COMPONENT") fi else # If no component is specified, start all components if [[ -z "$COMPONENT" ]]; then COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis" "devbox") else # Check if the component is a local component from DEVBOX_COMPONENT found=false for item in "${DEVBOX_COMPONENTS[@]}"; do if [ "$item" = "$COMPONENT" ]; then found=true break fi done if [ "$found" = true ]; then exit_with_message "Remote component $COMPONENT cannot be restarted, please use local components." 1 fi COMPONENTS=("$COMPONENT") fi fi BASIC_COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis" "devbox") for comp in "${COMPONENTS[@]}"; do should_start=false # Check if it's in BASIC_COMPONENTS for basic in "${BASIC_COMPONENTS[@]}"; do if [ "$comp" = "$basic" ]; then should_start=true break fi done # Check if it's in DEVBOX_COMPONENTS if [ "$should_start" = false ]; then for dev in "${DEVBOX_COMPONENTS[@]}"; do if [ "$comp" = "$dev" ]; then should_start=true break fi done fi if [ "$should_start" = true ]; then log_info "Starting $comp service..." local component_container_id_file_path="${WORKING_HOME}/.${comp}-instance" if [ ! -f "$component_container_id_file_path" ]; then log_error "$comp container is not running. Please run 'devbox init' first." else local component_container_id=$(cat "$component_container_id_file_path") if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${component_container_id}\$"; then log_info "$comp container is not running, starting container..." if ! docker start "${component_container_id}"; then log_error "Failed to start $comp container." else log_info "$comp container started successfully." fi else log_info "$comp container is already running." fi fi else exit_with_message " Unknown component: $comp, please check the component name." 1 fi done # Check if $FREELEAPS_ENDPOINT is not empty and start the frontend and backend services if [[ "$FREELEAPS_ENDPOINT" != "" ]]; then # Sleep for 10 seconds to allow the services to start and echo 10 seconds increase from 1 to 10 in each second for i in {1..20}; do if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then break fi echo -n "-" sleep 1 done if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then log_info "Starting Freeleaps frontend and backend services..." # Check if start backend service if [[ "${START_BACKEND}" == "true" ]]; then compile_backend_service fi # Start the frontend service if [[ "${START_FRONTEND}" == "true" ]]; then compile_frontend_service fi else exit_with_message " DevBox container is not running, please run 'devbox init' first." 1 fi fi success_message=" Freeleaps services started successfully. " frontend_port=$(cat "${WORKING_HOME}/.devbox-frontend-port") if [[ "${START_FRONTEND}" == "true" && "${START_BACKEND}" == "true" ]]; then success_message=" Frontend and backend services started successfully. Open up the frontend by visiting: http://localhost:${frontend_port}" elif [[ "${START_FRONTEND}" == "true" ]]; then success_message=" Frontend service started successfully. Open up the frontend by visiting: http://localhost:${frontend_port}" elif [[ "${START_BACKEND}" == "true" ]]; then success_message=" Backend service started successfully." fi exit_with_message "$success_message" 0 } # :command.function devbox_stop_command() { local COMPONENT="$(get_arg '--component')" local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" local FREELEAPS_ENDPOINT="$(get_arg '--freeleaps-endpoint')" if [[ "$FREELEAPS_ENDPOINT" == "all" ]]; then export STOP_FRONTEND=true export STOP_BACKEND=true elif [[ "$FREELEAPS_ENDPOINT" == "frontend" ]]; then export STOP_FRONTEND=true export STOP_BACKEND=false elif [[ "$FREELEAPS_ENDPOINT" == "backend" ]]; then export STOP_FRONTEND=false export STOP_BACKEND=true else # Default behavior can be adjusted if needed export STOP_FRONTEND=false export STOP_BACKEND=false fi if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then local devbox_container_id devbox_container_id=$(cat "${WORKING_HOME}/.devbox-instance") # If the DevBox container is not running, exit if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then exit_with_message " DevBox container is not running, please run 'devbox init' first." 1 fi # Check if STOP_BACKEND is true, stop the backend service if [[ "${STOP_BACKEND}" == "true" ]]; then if [[ -f "${WORKING_HOME}/.backend.pid" ]]; then stop_backend_service else log_info "Backend service is not running." fi fi # Check if STOP_FRONTEND is true, stop the frontend service if [[ "${STOP_FRONTEND}" == "true" ]]; then if [[ -f "${WORKING_HOME}/.frontend.pid" ]]; then stop_frontend_service else log_info "Frontend service is not running." fi fi fi # if any of STOP_FRONTEND and STOP_BACKEND is true, then completed stop process if [[ "${STOP_FRONTEND}" == "true" || "${STOP_BACKEND}" == "true" ]]; then stoped_freeleaps_service_names=() if [[ "${STOP_FRONTEND}" == "true" ]]; then stoped_freeleaps_service_names+=("frontend") fi if [[ "${STOP_BACKEND}" == "true" ]]; then stoped_freeleaps_service_names+=("backend") fi # Combine the stoped_freeleaps_service_names array to a string with "and" if there are more than one service if [[ "${#stoped_freeleaps_service_names[@]}" -gt 1 ]]; then stoped_freeleaps_service_names="frontend and backend" else stoped_freeleaps_service_names="${stoped_freeleaps_service_names[0]}" fi exit_message="Stopped Freeleaps $stoped_freeleaps_service_names services successfully." exit_with_message "$exit_message" 0 fi # If the DevBox container is not running, exit if [[ -z "$COMPONENT" ]]; then COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis" "devbox") COMPONENTS+=("${DEVBOX_COMPONENTS[@]}") else COMPONENTS=("$COMPONENT") fi # Stop the specified components BASIC_COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis") for comp in "${COMPONENTS[@]}"; do if [ "$comp" = "devbox" ]; then if [ -f "${WORKING_HOME}/.devbox-instance" ]; then local container_id container_id=$(cat "${WORKING_HOME}/.devbox-instance") if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then log_info "Stopping devbox..." docker stop "$container_id" &>/dev/null || true rm -f "${WORKING_HOME}/.backend.pid" rm -f "${WORKING_HOME}/.frontend.pid" else log_info "DevBox container is not running." fi else log_info "Backend service is not running." fi continue fi should_stop=false for basic in "${BASIC_COMPONENTS[@]}"; do if [ "$comp" = "$basic" ]; then should_stop=true break fi done if [ "$should_stop" = false ]; then for dev in "${DEVBOX_COMPONENTS[@]}"; do if [ "$comp" = "$dev" ]; then should_stop=true break fi done fi if [ "$should_stop" = true ]; then if [ -f "${WORKING_HOME}/.${comp}-instance" ]; then local container_id container_id=$(cat "${WORKING_HOME}/.${comp}-instance") if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then log_info "Stopping $comp service..." docker stop "$container_id" &>/dev/null || true else log_info "$comp service is not running." fi else log_info "$comp service is not running." fi else exit_with_message " Unknown component: $comp, please check the component name." 1 fi done exit_with_message "Stopped Freeleaps services successfully. " 0 } # :command.function devbox_status_command() { log_info "Checking DevBox services status..." local COMPONENT="$(get_arg '--component')" local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" # Check if .devbox-instance file exists if [[ ! -f "${WORKING_HOME}/.devbox-instance" ]]; then exit_with_message " DevBox container is not running. Please run 'devbox init' first." 1 fi local devbox_container_id=$(cat "${WORKING_HOME}/.devbox-instance") # If the DevBox container devbox_container_id is not running, exit if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then exit_with_message " DevBox container is not running. Please run 'devbox init' first." 1 fi # If no component is specified, check all components if [[ -z "$COMPONENT" ]]; then COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis" "devbox") COMPONENTS+=("${DEVBOX_COMPONENTS[@]}") else COMPONENTS=("$COMPONENT") fi # Check the status of the specified components BASIC_COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis") ALL_COMPONENTS=("${BASIC_COMPONENTS[@]}" "${DEVBOX_COMPONENTS[@]}" "devbox") for comp in "${COMPONENTS[@]}"; do case_checked=false # devsvc, notification, content... if [ "$case_checked" = false ]; then for svc in "${ALL_COMPONENTS[@]}"; do if [ "$comp" = "$svc" ]; then log_info "Checking $comp service status..." if [ -f "${WORKING_HOME}/.${comp}-instance" ]; then local container_id container_id=$(cat "${WORKING_HOME}/.${comp}-instance") if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then log_info "[RESULT]: $comp service is running." else log_info "[RESULT]: $comp service is not running." fi else log_info "[RESULT]: $comp service is not running." fi case_checked=true break fi done fi # unknown if [ "$case_checked" = false ]; then exit_with_message " Unknown component: $comp, please check the component name." 1 fi done exit_with_message " DevBox services status checked successfully." 0 } # :command.function devbox_restart_command() { log_info "Restarting DevBox services..." local COMPONENT="$(get_arg '--component')" local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" local FREELEAPS_ENDPOINT="$(get_arg '--freeleaps-endpoint')" # Check if --freeleaps-endpoint is not empty and start the frontend and backend services restart_services=() if [[ "$FREELEAPS_ENDPOINT" == "all" ]]; then export START_FRONTEND=true export START_BACKEND=true restart_services=("frontend" "backend") elif [[ "$FREELEAPS_ENDPOINT" == "frontend" ]]; then export START_FRONTEND=true export START_BACKEND=false restart_services=("frontend") elif [[ "$FREELEAPS_ENDPOINT" == "backend" ]]; then export START_FRONTEND=false export START_BACKEND=true restart_services=("backend") else # Default behavior can be adjusted if needed export START_FRONTEND=false export START_BACKEND=false fi # Check if --freeleaps-endpoint is not empty and start the frontend and backend services if [[ "$FREELEAPS_ENDPOINT" != "" ]]; then devbox_container_id=$(cat "${WORKING_HOME}/.devbox-instance") log_info "docker ps --no-trunc --format ${devbox_container_id} " if docker ps --no-trunc --format '{{.ID}}' | grep -q "${devbox_container_id}\$"; then log_info "Starting Freeleaps frontend and backend services..." # Check if start backend service if [[ "${START_BACKEND}" == "true" ]]; then stop_backend_service compile_backend_service fi # Start the frontend service if [[ "${START_FRONTEND}" == "true" ]]; then stop_frontend_service compile_frontend_service fi else exit_with_message " DevBox container is not running." 1 fi frontend_port=$(cat "${WORKING_HOME}/.devbox-frontend-port") success_message="Freeleaps $restart_services services restarted successfully." if [[ "${START_FRONTEND}" == "true" ]]; then success_message+=" Open up the frontend by visiting: http://localhost:${frontend_port}" fi exit_with_message "$success_message" 0 fi # Check devbox container file path local devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" if [[ ! -f "$devbox_container_id_file_path" ]]; then exit_with_message " DevBox container is not running. Please run 'devbox init' first." 1 fi local devbox_container_id=$(cat "$devbox_container_id_file_path") # Check if current environment is using local components USE_LOCAL_COMPONENT=$(cat "${WORKING_HOME}/.use-local-component" 2>/dev/null || true) if [[ "$USE_LOCAL_COMPONENT" == "true" ]]; then log_info "Using local components..." if [[ -z "$COMPONENT" ]]; then COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis" "devbox") COMPONENTS+=("${DEVBOX_COMPONENTS[@]}") else COMPONENTS=("$COMPONENT") fi else log_info "Using remote components..." if [[ -z "$COMPONENT" ]]; then COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis" "devbox") else found=false for item in "${DEVBOX_COMPONENTS[@]}"; do if [ "$item" = "$COMPONENT" ]; then found=true break fi done if [ "$found" = true ]; then exit_with_message "Remote component $COMPONENT cannot be restarted, please use local components." 1 fi COMPONENTS=("$COMPONENT") fi fi # Stop the specified components BASIC_COMPONENTS=("mongodb" "rabbitmq" "gitea" "redis") ALL_COMPONENTS=("${BASIC_COMPONENTS[@]}" "${DEVBOX_COMPONENTS[@]}" "devbox") # Stop the specified components for comp in "${COMPONENTS[@]}"; do if [ "$comp" = "devbox" ]; then if [ -f "${WORKING_HOME}/.devbox-instance" ]; then local container_id container_id=$(cat "${WORKING_HOME}/.devbox-instance") log_info "Stopping devbox service..." docker stop "$container_id" &>/dev/null || true rm -f "${WORKING_HOME}/.backend.pid" rm -f "${WORKING_HOME}/.frontend.pid" else log_info "Devbox is not running." fi continue fi should_stop=false for known in "${BASIC_COMPONENTS[@]}" "${DEVBOX_COMPONENTS[@]}"; do if [ "$comp" = "$known" ]; then should_stop=true break fi done if [ "$should_stop" = true ]; then if [ -f "${WORKING_HOME}/.${comp}-instance" ]; then local container_id container_id=$(cat "${WORKING_HOME}/.${comp}-instance") log_info "Stopping $comp service..." docker stop "$container_id" &>/dev/null || true else log_info "$comp service is not running." fi else exit_with_message " Unknown component: $comp, please check the component name." 1 fi done # Sleep 5 seconds to allow the services to stop, for each second echo 5 seconds increase from 1 to 5 in each second by - for comp in "${COMPONENTS[@]}"; do is_known=false for known in "${ALL_COMPONENTS[@]}"; do if [ "$comp" = "$known" ]; then is_known=true break fi done if [ "$is_known" = true ]; then log_info "Restarting $comp service..." if [ -f "${WORKING_HOME}/.${comp}-instance" ]; then local container_id container_id=$(cat "${WORKING_HOME}/.${comp}-instance") docker start "$container_id" &>/dev/null || true else log_info "$comp service is not running." fi else exit_with_message " Unknown component: $comp, please check the component name." 1 fi done exit_with_message " DevBox services restarted successfully." 0 } # :command.parse_requirements parse_requirements() { # :command.fixed_flags_filter while [[ $# -gt 0 ]]; do key="$1" case "$key" in --version | -v) version_command exit ;; --help | -h) long_usage=yes devbox_usage exit ;; *) break ;; esac done # :command.environment_variables_filter env_var_names+=("FREELEAPS_USERNAME") env_var_names+=("FREELEAPS_PASSWORD") env_var_names+=("WORKING_HOME") # :command.command_filter action=${1:-} case "$action" in -*) ;; init | i) action="init" shift devbox_init_parse_requirements "$@" shift $# ;; deinit | d) action="deinit" shift devbox_deinit_parse_requirements "$@" shift $# ;; start | s) action="start" shift devbox_start_parse_requirements "$@" shift $# ;; stop | p) action="stop" shift devbox_stop_parse_requirements "$@" shift $# ;; status | t) action="status" shift devbox_status_parse_requirements "$@" shift $# ;; restart | r) action="restart" shift devbox_restart_parse_requirements "$@" shift $# ;; "") devbox_init_guidance >&2 action="init" devbox_init_parse_requirements "$@" shift $# ;; *) # pass * parameter to devbox_init_guidance devbox_init_guidance $action >&2 action="init" shift devbox_init_parse_requirements "$@" shift $# ;; esac # :command.parse_requirements_while while [[ $# -gt 0 ]]; do key="$1" case "$key" in -?*) printf "invalid option: %s\n" "$key" >&2 exit 1 ;; *) # :command.parse_requirements_case # :command.parse_requirements_case_simple printf "invalid argument: %s\n" "$key" >&2 exit 1 ;; esac done } # :command.parse_requirements devbox_init_parse_requirements() { # :command.fixed_flags_filter while [[ $# -gt 0 ]]; do key="$1" case "$key" in --help | -h) long_usage=yes devbox_init_usage exit ;; *) break ;; esac done # :command.command_filter action="init" # :command.parse_requirements_while while [[ $# -gt 0 ]]; do key="$1" case "$key" in # :flag.case --os | -o) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--os' "$2" shift 2 else printf "%s\n" "--os requires an argument: --os OS" >&2 exit 1 fi ;; # :flag.case --arch | -a) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--arch' "$2" shift 2 else printf "%s\n" "--arch requires an argument: --arch ARCH" >&2 exit 1 fi ;; # :flag.case --devbox-container-name | -n) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-container-name' "$2" shift 2 else printf "%s\n" "--devbox-container-name requires an argument: --devbox-container-name DEVBOX_CONTAINER_NAME" >&2 exit 1 fi ;; # :flag.case --devbox-container-port | -p) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-container-port' "$2" shift 2 else printf "%s\n" "--devbox-container-port requires an argument: --devbox-container-port DEVBOX_CONTAINER_PORT" >&2 exit 1 fi ;; --devbox-frontend-port | -q) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-frontend-port' "$2" shift 2 else printf "%s\n" "--devbox-frontend-port requires an argument: --devbox-frontend-port DEVBOX_FRONTEND_PORT (Internal Vue3 Port)" >&2 exit 1 fi ;; --devbox-backend-port | -b) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-backend-port' "$2" shift 2 else printf "%s\n" "--devbox-backend-port requires an argument: --devbox-backend-port DEVBOX_BACKEND_PORT" >&2 exit 1 fi ;; # :flag.case --devbox-image-repo | -r) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-image-repo' "$2" shift 2 else printf "%s\n" "--devbox-image-repo requires an argument: --devbox-image-repo DEVBOX_IMAGE_REPO" >&2 exit 1 fi ;; # :flag.case --devbox-image-name | -i) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-image-name' "$2" shift 2 else printf "%s\n" "--devbox-image-name requires an argument: --devbox-image-name DEVBOX_IMAGE_NAME" >&2 exit 1 fi ;; # :flag.case --devbox-image-tag | -t) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-image-tag' "$2" shift 2 else printf "%s\n" "--devbox-image-tag requires an argument: --devbox-image-tag DEVBOX_IMAGE_TAG" >&2 exit 1 fi ;; # :flag.case --working-home | -w) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--working-home' "$2" shift 2 else printf "%s\n" "--working-home requires an argument: --working-home WORKING_HOME" >&2 exit 1 fi ;; # :flag.case --freeleaps-username | -u) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--freeleaps-username' "$2" shift 2 else printf "%s\n" "--freeleaps-username requires an argument: --freeleaps-username FREELEAPS_USERNAME" >&2 exit 1 fi ;; # :flag.case --freeleaps-password | -x) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--freeleaps-password' "$2" shift 2 else printf "%s\n" "--freeleaps-password requires an argument: --freeleaps-password FREELEAPS_PASSWORD" >&2 exit 1 fi ;; --use-custom-repository | -c) if [[ -n ${2+x} ]]; then add_arg '--use-custom-repository' "$2" shift 2 else printf "%s\n" "--use-custom-repository requires an argument: --use-custom-repository USE_CUSTOM_REPOSITORY" >&2 exit 1 fi ;; # :flag.case --use-local-component | -l) if [[ -n ${2+x} ]]; then add_arg '--use-local-component' "$2" shift 2 else printf "%s\n" "--use-local-component requires an argument: --use-local-component USING_LOCAL_COMPONENT" >&2 exit 1 fi ;; --freeleaps-components | -m) if [[ -n ${2+x} ]]; then add_arg '--freeleaps-components' "$2" shift 2 else printf "%s\n" "--freeleaps-components requires an argument: --freeleaps-components FREELEAPS_COMPONENTS" >&2 exit 1 fi ;; # :flag.case --force | -f) # :flag.case_no_arg add_arg '--force' '1' shift ;; # :flag.case --git-branch | -g) if [[ -n ${2+x} ]]; then add_arg '--git-branch' "$2" shift 2 else printf "%s\n" "--git-branch requires an argument: --git-branch GIT_BRANCH" >&2 exit 1 fi ;; --devbox-nginx-port) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-nginx-port' "$2" shift 2 else printf "%s\n" "--devbox-nginx-port requires an argument: --devbox-nginx-port DEVBOX_NGINX_PORT (Host Port)" >&2 exit 1 fi ;; -?*) printf "invalid option: %s\n" "$key" >&2 exit 1 ;; *) # :command.parse_requirements_case # :command.parse_requirements_case_simple printf "invalid argument: %s\n" "$key" >&2 exit 1 ;; esac done # :command.default_assignments if [ -z "$(get_arg '--os')" ]; then add_arg '--os' "auto" fi if [ -z "$(get_arg '--arch')" ]; then add_arg '--arch' "auto" fi # :command.default_assignments current_arch=$(get_arg '--arch') if [ "$current_arch" = "auto" ]; then detected_arch=$(uname -m) if [ "$detected_arch" = "x86_64" ]; then current_arch="amd64" elif [ "$detected_arch" = "aarch64" ] || [ "$detected_arch" = "arm64" ]; then current_arch="arm64" else echo "ERROR: Unsupported architecture detected: $detected_arch" exit 1 fi # Update the arch argument accordingly add_arg '--arch' "$current_arch" fi if [ -z "$(get_arg '--devbox-container-name')" ]; then add_arg '--devbox-container-name' "devbox" fi if [ -z "$(get_arg '--devbox-container-port')" ]; then add_arg '--devbox-container-port' "22222" fi if [ -z "$(get_arg '--devbox-frontend-port')" ]; then add_arg '--devbox-frontend-port' "5173" fi if [ -z "$(get_arg '--devbox-backend-port')" ]; then add_arg '--devbox-backend-port' "8002" fi if [ -z "$(get_arg '--devbox-image-repo')" ]; then add_arg '--devbox-image-repo' "docker.io/freeleaps" fi if [ -z "$(get_arg '--devbox-image-name')" ]; then add_arg '--devbox-image-name' "devbox_v1" fi if [ -z "$(get_arg '--devbox-image-tag')" ]; then add_arg '--devbox-image-tag' "devbox_local" fi if [ -z "$(get_arg '--use-custom-repository')" ]; then add_arg '--use-custom-repository' "" fi if [ -z "$(get_arg '--working-home')" ]; then add_arg '--working-home' "${HOME}/devbox" fi if [ -z "$(get_arg '--use-local-component')" ]; then add_arg '--use-local-component' "false" fi if [ -z "$(get_arg '--git-branch')" ]; then add_arg '--git-branch' "main" fi if [ -z "$(get_arg '--devbox-nginx-port')" ]; then add_arg '--devbox-nginx-port' "8888" fi } # :command.parse_requirements devbox_deinit_parse_requirements() { # :command.fixed_flags_filter while [[ $# -gt 0 ]]; do key="$1" case "$key" in --help | -h) long_usage=yes devbox_deinit_usage exit ;; *) break ;; esac done # :command.command_filter action="deinit" # :command.parse_requirements_while while [[ $# -gt 0 ]]; do key="$1" case "$key" in # :flag.case --working-home | -w) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--working-home' "$2" shift shift else printf "%s\n" "--working-home requires an argument: --working-home WORKING_HOME" >&2 exit 1 fi ;; # :flag.case --clear-logs | -l) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--clear-logs' "$2" shift shift else printf "%s\n" "--clear-logs requires an argument: --clear-logs CLEAR_LOGS" >&2 exit 1 fi ;; # :flag.case --clear-repo | -r) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--clear-repo' "$2" shift shift else printf "%s\n" "--clear-repo requires an argument: --clear-repo CLEAR_REPO" >&2 exit 1 fi ;; --clear-all | -a) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--clear-all' "$2" shift shift else printf "%s\n" "--clear-all requires an argument: --clear-all CLEAR_ALL" >&2 exit 1 fi ;; -?*) printf "invalid option: %s\n" "$key" >&2 exit 1 ;; *) # :command.parse_requirements_case # :command.parse_requirements_case_simple printf "invalid argument: %s\n" "$key" >&2 exit 1 ;; esac done # :command.default_assignments if [ -z "$(get_arg '--working-home')" ]; then add_arg '--working-home' "${HOME}/devbox" fi if [ -z "$(get_arg '--clear-logs')" ]; then add_arg '--clear-logs' "false" fi if [ -z "$(get_arg '--clear-repo')" ]; then add_arg '--clear-repo' "false" fi if [ -z "$(get_arg '--clear-all')" ]; then add_arg '--clear-all' "false" fi } # :command.parse_requirements devbox_start_parse_requirements() { # :command.fixed_flags_filter while [[ $# -gt 0 ]]; do key="$1" case "$key" in --help | -h) long_usage=yes devbox_start_usage exit ;; *) break ;; esac done # :command.command_filter action="start" # :command.parse_requirements_while while [[ $# -gt 0 ]]; do key="$1" case "$key" in # :flag.case --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--component' "$2" shift shift else printf "%s\n" "--component requires an argument: --component COMPONENT" >&2 exit 1 fi ;; --freeleaps-endpoint | -e) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--freeleaps-endpoint' "$2" shift shift else printf "%s\n" "--freeleaps-endpoint requires an argument: --freeleaps-endpoint FREELEAPS_ENDPOINT" >&2 exit 1 fi ;; -?*) printf "invalid option: %s\n" "$key" >&2 exit 1 ;; *) # :command.parse_requirements_case # :command.parse_requirements_case_simple printf "invalid argument: %s\n" "$key" >&2 exit 1 ;; esac done # :command.default_assignments if [ -z "$(get_arg '--freeleaps-endpoint')" ]; then add_arg '--freeleaps-endpoint' "" fi } # :command.parse_requirements devbox_stop_parse_requirements() { # :command.fixed_flags_filter while [[ $# -gt 0 ]]; do key="$1" case "$key" in --help | -h) long_usage=yes devbox_stop_usage exit ;; *) break ;; esac done # :command.command_filter action="stop" # :command.parse_requirements_while while [[ $# -gt 0 ]]; do key="$1" case "$key" in # :flag.case --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--component' "$2" shift shift else printf "%s\n" "--component requires an argument: --component COMPONENT" >&2 exit 1 fi ;; --freeleaps-endpoint | -e) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--freeleaps-endpoint' "$2" shift shift else printf "%s\n" "--freeleaps-endpoint requires an argument: --freeleaps-endpoint all/backend/frontend" >&2 exit 1 fi ;; -?*) printf "invalid option: %s\n" "$key" >&2 exit 1 ;; *) # :command.parse_requirements_case # :command.parse_requirements_case_simple printf "invalid argument: %s\n" "$key" >&2 exit 1 ;; esac done } # :command.parse_requirements devbox_status_parse_requirements() { # :command.fixed_flags_filter while [[ $# -gt 0 ]]; do key="$1" case "$key" in --help | -h) long_usage=yes devbox_status_usage exit ;; *) break ;; esac done # :command.command_filter action="status" # :command.parse_requirements_while while [[ $# -gt 0 ]]; do key="$1" case "$key" in # :flag.case --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--component' "$2" shift shift else printf "%s\n" "--component requires an argument: --component COMPONENT" >&2 exit 1 fi ;; -?*) printf "invalid option: %s\n" "$key" >&2 exit 1 ;; *) # :command.parse_requirements_case # :command.parse_requirements_case_simple printf "invalid argument: %s\n" "$key" >&2 exit 1 ;; esac done } # :command.parse_requirements devbox_restart_parse_requirements() { # :command.fixed_flags_filter while [[ $# -gt 0 ]]; do key="$1" case "$key" in --help | -h) long_usage=yes devbox_restart_usage exit ;; *) break ;; esac done # :command.command_filter action="restart" # :command.parse_requirements_while while [[ $# -gt 0 ]]; do key="$1" case "$key" in # :flag.case --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--component' "$2" shift shift else printf "%s\n" "--component requires an argument: --component COMPONENT" >&2 exit 1 fi ;; --freeleaps-endpoint | -e) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--freeleaps-endpoint' "$2" shift shift else printf "%s\n" "--freeleaps-endpoint requires an argument: --freeleaps-endpoint all/backend/frontend" >&2 exit 1 fi ;; -?*) printf "invalid option: %s\n" "$key" >&2 exit 1 ;; *) # :command.parse_requirements_case # :command.parse_requirements_case_simple printf "invalid argument: %s\n" "$key" >&2 exit 1 ;; esac done # :command.default_assignments if [ -z "$(get_arg '--freeleaps-endpoint')" ]; then add_arg '--freeleaps-endpoint' "" fi } initialize() { version="1.0.0" long_usage="" set -e # Use the following variables to define the environment variables and input parameters args_keys=() args_values=() deps_keys=() deps_values=() # For each environment variable, add a line like the following: env_var_names=() input=() } # :command.run run() { normalize_input "$@" parse_requirements "${input[@]}" case "$action" in "init") devbox_init_command ;; "deinit") devbox_deinit_command ;; "start") devbox_start_command ;; "stop") devbox_stop_command ;; "status") devbox_status_command ;; "restart") devbox_restart_command ;; esac } initialize run "$@"