freeleaps-pub/devbox/devbox.local/devbox
2025-02-20 23:00:03 -08:00

2896 lines
94 KiB
Bash
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Modifying it manually is not recommended
# :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:]'
}
# 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
}
# :command.usage
devbox_usage() {
printf "Command\n"
printf " devbox : DevBox Command Line Tool for managing the local development environment.\n\n"
printf "Arguments\n"
printf " COMMAND [Required] : Specify the command to execute (e.g., init, deinit, start, stop, status, restart).\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 " Display help for the 'init' command:\n"
printf " devbox init --help\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-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 " --devbox-frontend-port -F [Optional] : Specifies the container port for DevBox frontend access. Default: 5173.\n"
printf " --devbox-backend-port -B [Optional] : Specifies the container port for DevBox backend access. Default: 8002.\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 -u [Optional] : Check if using local component or online dev environment. Default: false.\n"
printf " --devsvc-image-repo -D [Optional] : Specifies the repository for devsvc component.\n"
printf " --devsvc-image-name -M [Optional] : Specifies the image name for devsvc component.\n"
printf " --devsvc-image-tag -G [Optional] : Specifies the image tag for devsvc component. Default: latest.\n"
printf " --notification-image-repo -Y [Optional] : Specifies the repository for notification component.\n"
printf " --notification-image-name -K [Optional] : Specifies the image name for notification component.\n"
printf " --notification-image-tag -Z [Optional] : Specifies the image tag for notification component. Default: latest.\n"
printf " --content-image-repo -C [Optional] : Specifies the repository for content component.\n"
printf " --content-image-name -E [Optional] : Specifies the image name for content component.\n"
printf " --content-image-tag -H [Optional] : Specifies the image tag for content component. Default: latest.\n"
printf " --central_storage-image-repo -S [Optional] : Specifies the repository for central_storage component.\n"
printf " --central_storage-image-name -J [Optional] : Specifies the image name for central_storage component.\n"
printf " --central_storage-image-tag -Q [Optional] : Specifies the image tag for central_storage component. Default: latest.\n"
printf " --authentication-image-repo -V [Optional] : Specifies the repository for authentication component.\n"
printf " --authentication-image-name -L [Optional] : Specifies the image name for authentication component.\n"
printf " --authentication-image-tag -W [Optional] : Specifies the image tag for authentication component. Default: latest.\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"
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 "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"
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, backend, frontend, devsvc, content, central_storage, authentication).\n\n"
printf " --freeleaps-endpoint -e [Optional] : Specifies the Freeleaps.com endpoint 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, authentication).\n\n"
printf "Global Arguments\n"
printf " --help -h : Show this help message and exit.\n\n"
printf "Examples\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, etc.).\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=backend\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., backend, frontend, etc.).\n"
printf " --freeleaps-endpoint -e [Optional] : Specifies the Freeleaps.com endpoint to restart in the DevBox container.\n"
printf " --force -f : 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
while [[ $# -gt 0 ]]; do
arg="$1"
if [[ $passthru == true ]]; then
input+=("$arg")
elif [[ $arg =~ ^(--[a-zA-Z0-9_\-]+)=(.+)$ ]]; then
input+=("${BASH_REMATCH[1]}")
input+=("${BASH_REMATCH[2]}")
elif [[ $arg =~ ^(-[a-zA-Z0-9])=(.+)$ ]]; then
input+=("${BASH_REMATCH[1]}")
input+=("${BASH_REMATCH[2]}")
elif [[ $arg =~ ^-([a-zA-Z0-9][a-zA-Z0-9]+)$ ]]; 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)
echo "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
echo "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
echo "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
echo "- \$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
echo "environment variables:"
for name in $sorted_names; do
# Look up the value based on the name
echo "- \$${name} = ${!name:-}"
done
fi
}
install_docker() {
echo "[INFO] Checking Docker installation..."
# Check if Docker CLI is installed
if ! command -v docker >/dev/null 2>&1; then
echo "[ERROR] Docker CLI is not installed."
return 1
fi
echo "[INFO] Docker CLI is installed. Checking daemon..."
# Check if Docker daemon is running
if docker info >/dev/null 2>&1; then
echo "[OK] Docker daemon is running."
return 0
fi
# Check if running on WSL
if grep -qi microsoft /proc/version; then
echo "[INFO] Detected WSL environment. Skipping service startup."
if [ -S /var/run/docker.sock ]; then
echo "[OK] Docker socket found (/var/run/docker.sock)."
return 0
else
echo "[ERROR] Docker socket /var/run/docker.sock not found."
echo "[SOLUTION] Please start Docker Desktop on Windows and enable WSL integration, then mount /var/run/docker.sock into the container."
return 1
fi
fi
# Check if running on macOS
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
echo "[INFO] Running on Linux. Attempting to start Docker service..."
if command -v systemctl >/dev/null 2>&1; then
echo "[INFO] Starting Docker with systemctl..."
sudo systemctl start docker
sleep 2
if systemctl is-active --quiet docker; then
echo "[OK] Docker service started successfully via systemctl."
return 0
else
echo "[ERROR] Failed to start Docker using systemctl."
return 1
fi
elif command -v service >/dev/null 2>&1; then
echo "[INFO] systemctl not found, trying to start Docker using service..."
sudo service docker start
sleep 2
if docker info >/dev/null 2>&1; then
echo "[OK] Docker service started successfully via service command."
return 0
else
echo "[ERROR] Failed to start Docker using service command."
return 1
fi
else
echo "[ERROR] Neither systemctl nor service command found. Please start Docker manually."
return 1
fi
fi
return 1
}
check_docker_running() {
echo "==> Checking if Docker service is running..."
# if Docker CLI is installed and Docker daemon is running
if docker info >/dev/null 2>&1; then
echo "==> Docker is running."
return 0
fi
# if running on WSL, check for Docker socket
if grep -qi microsoft /proc/version; then
echo "[INFO] Detected WSL environment. Verifying /var/run/docker.sock..."
if [ -S /var/run/docker.sock ]; then
echo "==> Docker socket found. Docker should be available via Docker Desktop."
return 0
else
echo "[ERROR] Docker socket not found in WSL environment."
return 1
fi
fi
echo "==> 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
echo "==> Starting Docker with systemctl..."
sudo systemctl start docker && echo "==> Docker started successfully." && return 0
fi
fi
if command -v service &>/dev/null; then
if service --status-all | grep -q "docker"; then
echo "==> Starting Docker with service..."
sudo service docker start && echo "==> Docker started successfully." && return 0
fi
fi
if command -v snap &>/dev/null && snap list | grep -q "docker"; then
echo "==> Starting Docker with snap..."
sudo snap start docker && echo "==> Docker started successfully." && return 0
fi
echo "ERROR: Unable to start Docker automatically. Please start it manually."
return 1
}
# Define the local components and their corresponding ports
local_components_ports_keys=("devsvc" "notification" "content" "central_storage" "authentication")
local_components_ports_values=("8007" "8003" "8013" "8005" "8004")
# Get the port number for a local component
get_port() {
local comp="$1"
local port=""
for i in "${!local_components_ports_keys[@]}"; do
if [ "${local_components_ports_keys[i]}" = "$comp" ]; then
port="${local_components_ports_values[i]}"
break
fi
done
echo "$port"
}
# :command.command_functions
# :command.function
devbox_init_command() {
#!/usr/bin/env bash
echo "==> [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 DEVSVC_REPO="$args_devsvc_image_repo" # --devsvc-image-repo
local DEVSVC_IMAGE="$args_devsvc_image_image" # --devsvc-image-image
local DEVSVC_TAG="$args_devsvc_image_tag" # --devsvc-image-tag
local CONTENT_REPO="$args_content_image_repo" # --content-image-repo
local CONTENT_IMAGE="$args_content_image_image" # --content-image-image
local CONTENT_TAG="$args_content_image_tag" # --content-image-tag
local CENTRAL_STORAGE_REPO="$args_central_storage_image_repo" # --central_storage-image-repo
local CENTRAL_STORAGE_IMAGE="$args_central_storage_image_image" # --central_storage-image-image
local CENTRAL_STORAGE_TAG="$args_central_storage_image_tag" # --central_storage-image-tag
local AUTHENTICATION_REPO="$args_authentication_image_repo" # --authentication-image-repo
local AUTHENTICATION_IMAGE="$args_authentication_image_image" # --authentication-image-image
local AUTHENTICATION_TAG="$args_authentication_image_tag" # --authentication-image-tag
local NOTIFICATION_REPO="$args_notification_image_repo" # --notification-image-repo
local NOTIFICATION_IMAGE="$args_notification_image_image" # --notification-image-image
local NOTIFICATION_TAG="$args_notification_image_tag" # --notification-image-tag
local CUSTOM_GIT_REPO="$args_custom_git_repo" # --custom-git-repo
# --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 DEVSVC_REPO="$(get_arg '--devsvc-image-repo')"
local DEVSVC_IMAGE="$(get_arg '--devsvc-image-name')"
local DEVSVC_TAG="$(get_arg '--devsvc-image-tag')"
local CONTENT_REPO="$(get_arg '--content-image-repo')"
local CONTENT_IMAGE="$(get_arg '--content-image-name')"
local CONTENT_TAG="$(get_arg '--content-image-tag')"
local CENTRAL_STORAGE_REPO="$(get_arg '--central_storage-image-repo')"
local CENTRAL_STORAGE_IMAGE="$(get_arg '--central_storage-image-name')"
local CENTRAL_STORAGE_TAG="$(get_arg '--central_storage-image-tag')"
local AUTHENTICATION_REPO="$(get_arg '--authentication-image-repo')"
local AUTHENTICATION_IMAGE="$(get_arg '--authentication-image-name')"
local AUTHENTICATION_TAG="$(get_arg '--authentication-image-tag')"
local NOTIFICATION_REPO="$(get_arg '--notification-image-repo')"
local NOTIFICATION_IMAGE="$(get_arg '--notification-image-name')"
local NOTIFICATION_TAG="$(get_arg '--notification-image-tag')"
local CUSTOM_GIT_REPO="$(get_arg '--custom-git-repo')"
local FORCE_INIT="$(get_arg '--force')"
local is_pull_all_components=true
local components=("devsvc" "notification" "content" "central_storage" "authentication")
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
echo "==> Checking parameters..."
for component in "${components[@]}"; do
if [[ -n "$(get_arg "--${component}-image-repo")" ]]; then
is_pull_all_components=false
start_components+=("${component}")
fi
# Check ARCH match default component tag value and justify the default Image tag value
if [[ "$ARCH" == "amd64" && "$component" == "devsvc" ]]; then
DEVSVC_TAG="latest-linux-amd64"
elif [[ "$ARCH" == "arm64" && "$component" == "devsvc" ]]; then
DEVSVC_TAG="latest-linux-arm64"
fi
if [[ "$ARCH" == "amd64" && "$component" == "content" ]]; then
CONTENT_TAG="latest-linux-amd64"
elif [[ "$ARCH" == "arm64" && "$component" == "content" ]]; then
CONTENT_TAG="latest-linux-arm64"
fi
if [[ "$ARCH" == "amd64" && "$component" == "central_storage" ]]; then
CENTRAL_STORAGE_TAG="latest-linux-amd64"
elif [[ "$ARCH" == "arm64" && "$component" == "central_storage" ]]; then
CENTRAL_STORAGE_TAG="latest-linux-arm64"
fi
if [[ "$ARCH" == "amd64" && "$component" == "authentication" ]]; then
AUTHENTICATION_TAG="latest-linux-amd64"
elif [[ "$ARCH" == "arm64" && "$component" == "authentication" ]]; then
AUTHENTICATION_TAG="latest-linux-arm64"
fi
if [[ "$ARCH" == "amd64" && "$component" == "notification" ]]; then
NOTIFICATION_TAG="latest-linux-amd64"
elif [[ "$ARCH" == "arm64" && "$component" == "notification" ]]; then
NOTIFICATION_TAG="latest-linux-arm64"
fi
done
# If is_pull_all_components is true, then pull all components
if [[ "$is_pull_all_components" == true ]]; then
start_components=("${components[@]}")
fi
echo " ===================================================== "
echo "Parameters:"
echo " OS = $OS"
echo " ARCH = $ARCH"
echo " DEVBOX_NAME = $DEVBOX_NAME"
echo " DEVBOX_PORT = $DEVBOX_PORT"
echo " DEVBOX_FRONTEND_PORT = $DEVBOX_FRONTEND_PORT"
echo " DEVBOX_BACKEND_PORT = $DEVBOX_BACKEND_PORT"
echo " DEVBOX_REPO = $DEVBOX_REPO"
echo " DEVBOX_IMAGE = $DEVBOX_IMAGE"
echo " DEVBOX_TAG = $DEVBOX_TAG"
echo " WORKING_HOME = $WORKING_HOME"
echo " FREELEAPS_USERNAME= $FREELEAPS_USERNAME"
echo " (FREELEAPS_PASSWORD is hidden for security)"
echo " USE_LOCAL_COMPONENT= $USE_LOCAL_COMPONENT"
echo " DEVSVC_REPO = $DEVSVC_REPO"
echo " DEVSVC_IMAGE = $DEVSVC_IMAGE"
echo " DEVSVC_TAG = $DEVSVC_TAG"
echo " CONTENT_REPO = $CONTENT_REPO"
echo " CONTENT_IMAGE = $CONTENT_IMAGE"
echo " CONTENT_TAG = $CONTENT_TAG"
echo " CENTRAL_STORAGE_REPO = $CENTRAL_STORAGE_REPO"
echo " CENTRAL_STORAGE_IMAGE= $CENTRAL_STORAGE_IMAGE"
echo " CENTRAL_STORAGE_TAG = $CENTRAL_STORAGE_TAG"
echo " AUTHENTICATION_REPO = $AUTHENTICATION_REPO"
echo " AUTHENTICATION_IMAGE= $AUTHENTICATION_IMAGE"
echo " AUTHENTICATION_TAG = $AUTHENTICATION_TAG"
echo " NOTIFICATION_REPO = $NOTIFICATION_REPO"
echo " NOTIFICATION_IMAGE= $NOTIFICATION_IMAGE"
echo " NOTIFICATION_TAG = $NOTIFICATION_TAG"
echo " FORCE_INIT = $FORCE_INIT"
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
echo "ERROR: Unsupported OS: $OS"
exit 1
fi
if [[ "$ARCH" != "auto" && "$ARCH" != "amd64" && "$ARCH" != "arm64" ]]; then
echo "ERROR: Unsupported architecture: $ARCH"
exit 1
fi
# Check ARCH match current device
if [[ "$ARCH" == "auto" ]]; then
ARCH="$(uname -m)"
if [[ "$ARCH" == "x86_64" ]]; then
ARCH="amd64"
elif [[ "$ARCH" == "aarch64" ]]; then
ARCH="arm64"
else
echo "ERROR: Unsupported architecture: $ARCH"
exit 1
fi
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
echo "ERROR: docker is not installed or not in PATH."
exit 1
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
echo "ERROR: Insufficient disk space (need >10GB)."
exit 1
fi
# 3.3 WORKING_HOME Check
if ! mkdir -p "$WORKING_HOME" 2>/dev/null; then
echo "ERROR: Can't create or write to WORKING_HOME: $WORKING_HOME"
exit 1
fi
# 3.4 Network to docker.com(sampleping docker.com)
if ! ping -c 1 docker.com &>/dev/null; then
echo "ERROR: Network unreachable."
exit 1
fi
if [[ -f "$WORKING_HOME/.devbox-instance" && -z "$FORCE_INIT" ]]; then
# Echo all start_components
if [[ "${#start_components[@]}" -gt 0 ]]; then
for component in "${start_components[@]}"; do
if [[ -f "$WORKING_HOME/.${component}-instance" ]]; then
echo "ERROR: Container named $component already exists. Use --force to remove it."
exit 1
fi
done
else
echo "ERROR: Container named $DEVBOX_NAME already exists. Use --force to remove it."
exit 1
fi
fi
# -------------------------------------------------------------------
# 5.install docker and check docker running
# -------------------------------------------------------------------
if ! install_docker; then
echo "ERROR: Failed to install Docker or Docker service is not running."
exit 1
fi
if ! check_docker_running; then
echo "ERROR: Docker service is not running."
exit 1
fi
# -------------------------------------------------------------------
# 5.1 pull and start DevBox container
# -------------------------------------------------------------------
local devbox_full_image="${DEVBOX_REPO}/${DEVBOX_IMAGE}:${DEVBOX_TAG}"
echo "==> Pulling DevBox image: $devbox_full_image"
if ! docker pull "$devbox_full_image"; then
echo "ERROR: Failed to pull DevBox image: $devbox_full_image"
exit 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
echo "==> 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
echo "ERROR: Container named $DEVBOX_NAME already exists. Use --force to remove it."
exit 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"
echo '==> [INIT] Starting DevBox environment initialization...'
# Check if docker network create devbox_freeleaps2-network
if ! docker network ls | grep -q "$DEVBOX_FREELEAPS2_NETWORK"; then
echo "==> Creating Docker network: $DEVBOX_FREELEAPS2_NETWORK"
docker network create "$DEVBOX_FREELEAPS2_NETWORK"
else
echo "==> Docker network devbox_freeleaps2-network already exists."
fi
echo '==> [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 "${DEVBOX_FRONTEND_PORT}:5173" \
-p "${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
echo "ERROR: Failed to create DevBox container."
exit 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"
# -------------------------------------------------------------------
# 6. linbwang: pull and start other components
# -------------------------------------------------------------------
echo "==> [INIT] Starting Freeleaps services... Use Local component $USE_LOCAL_COMPONENT_VAL"
export ARCH="$ARCH"
export WORKING_HOME="$WORKING_HOME"
# Check
if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then
echo ' ===> Using local components for Freeleaps services.'
export DEVSVC_IMAGE_TAG="$DEVSVC_TAG"
export CONTENT_IMAGE_TAG="$CONTENT_TAG"
export CENTRAL_STORAGE_IMAGE_TAG="$CENTRAL_STORAGE_TAG"
export AUTHENTICATION_IMAGE_TAG="$AUTHENTICATION_TAG"
export NOTIFICATION_IMAGE_TAG="$NOTIFICATION_TAG"
# Check if gitea_data_backup.tar.gz exists at current script directory, if not exit
if [[ ! -f "gitea_data_backup.tar.gz" ]]; then
echo "ERROR: gitea_data_backup.tar.gz not found. Please make sure it exists in the current directory."
exit 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
echo "ERROR: Failed to extract gitea data backup."
exit 1
fi
echo "==> Starting Gitea, MongoDB, RabbitMQ containers..."
# Start local components by docker compose file and start up specified services. docker compose file is in the same directory as the script (docker-compose.dev.arm64.new.yaml)
docker-compose -f docker-compose.dev.arm64.new.yaml up -d mongodb rabbitmq gitea
sleep 5
# start component service from start_components array
docker-compose -f docker-compose.dev.arm64.new.yaml up -d "${start_components[@]}"
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"
# 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
echo "${component} container created: $component_container_id"
# Get the owner group of the WORKING_HOME
if [[ "$(uname)" == "Darwin" ]]; then
OWNER_GROUP=$(stat -f "%Su:%Sg" "${WORKING_HOME}")
else
OWNER_GROUP=$(stat -c "%U:%G" "${WORKING_HOME}")
fi
# Echo OWNER_GROUP
echo "OWNER_GROUP: $OWNER_GROUP"
# 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 owner group of the gitea data directories
sudo chown -R "${OWNER_GROUP}" ${GITEA_HOST_DIR}
echo "Gitea data copying is done"
# Check if gitea data directories exist in the gitea container
if [[ ! -d "${GITEA_HOST_DIR}/gitea" ]]; then
echo "ERROR: Failed to copy gitea data."
exit 1
fi
# restart gitea container
docker-compose -f docker-compose.dev.arm64.new.yaml restart rabbitmq
sleep 10
docker-compose -f docker-compose.dev.arm64.new.yaml restart gitea
sleep 5
# restart notification if it is in the start_components
if [[ " ${start_components[@]} " =~ "notification" ]]; then
docker-compose -f docker-compose.dev.arm64.new.yaml restart notification
fi
else
echo '============================================'
echo ' ===> Using online components for Freeleaps services.'
echo '============================================'
# Start Gitea, MongoDB, RabbitMQ containers
docker-compose -f docker-compose.dev.arm64.new.yaml up -d mongodb rabbitmq
echo "===> start components is $start_components"
# 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"
fi
# Check all components are started
for component in "${start_components[@]}"; do
if [[ -z "$(docker ps -a --format '{{.Names}}' | grep "^$component\$")" ]]; then
echo "ERROR: Failed to start $component container."
exit 1
fi
done
# 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
read -p "Do you want to continue to pull freeleaps.com code and start the services? (Y/N): " user_input
if [[ "$user_input" == "N" || "$user_input" == "n" ]]; then
# Echo as init job completed and exit
echo
echo "==========================================================="
echo "DevBox init completed successfully!"
echo "DevBox Environment Details:"
echo " DevBox container name: $DEVBOX_NAME"
echo " DevBox container ID: $WORKING_HOME/.devbox-instance"
echo "==========================================================="
echo
exit 0
fi
IS_START_FRONTEND=true
# Check if username and password are set
if [[ -z "$FREELEAPS_USERNAME" || -z "$FREELEAPS_PASSWORD" ]]; then
echo "Warining: Username and password are required to pull freeleaps.com code."
echo "==> [INIT] DevBox environment initialization completed."
exit 1
fi
# Test if the user can access the freeleaps.com repository
echo "==> Testing access to freeleaps.com repository..."
if ! git ls-remote "https://$FREELEAPS_USERNAME:$FREELEAPS_PASSWORD@freeleaps.com:3443/products/freeleaps.git" &>/dev/null; then
echo "ERROR: Failed to access freeleaps.com repository. Please check your username and password."
echo "==> [INIT] DevBox environment initialization completed successfully, but access to the freeleaps.com repository failed."
exit 1
fi
FRONTEND_GIT_URL="https://$FREELEAPS_USERNAME:$FREELEAPS_PASSWORD@freeleaps.com:3443/products/freeleaps.git"
# Check if freeleaps2-frontend exists, if not git clone it
if [ ! -d $WORKING_HOME/freeleaps ]; then
echo "Git cloning freeleaps.com:3443/products/freeleaps.git"
git clone --depth 5 $FRONTEND_GIT_URL
else
pushd $WORKING_HOME/freeleaps
# 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
echo "Git cloning freeleaps.com:3443/products/freeleaps.git"
git clone --depth 5 $FRONTEND_GIT_URL
else
echo "Git pulling freeleaps.com:3443/products/freeleaps.git"
git pull
fi
fi
# Run banckend service and frontend service in the container
docker exec -i "$DEVBOX_NAME" bash <<EOF
# Set environment variables
export FREELEAPS_USERNAME="${FREELEAPS_USERNAME}"
export FREELEAPS_PASSWORD="${FREELEAPS_PASSWORD}"
export USE_LOCAL_COMPONENT_VAL="${USE_LOCAL_COMPONENT_VAL}"
export DEVBOX_BACKEND_PORT="${DEVBOX_BACKEND_PORT}"
export DEVBOX_FRONTEND_PORT="${DEVBOX_FRONTEND_PORT}"
# Check if useing local component and update /home/.devbox/freeleaps/.dev.env
echo "step 2: Update /home/.devbox/freeleaps/apps/.env"
# Get default IP address
DEFAULT_IP=\$(ip route | grep default | sed -n 's/.*default via \([^ ]*\).*/\1/p')
if [[ \$USE_LOCAL_COMPONENT_VAL == true ]]; then
echo "==> Using local components"
# Local components for Freeleaps services (devsvc, notification, content, central_storage, authentication)
cat << 'EOFinner' > /home/.devbox/freeleaps/apps/.env
# Online endpoint info
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 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://freeleaps-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
EOFinner
else
echo "==> Using online components"
cat << 'EOFinner' > /home/.devbox/freeleaps/apps/.env
# Online endpoint info
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=http://52.149.3.85:8007/api/devsvc/
export FREELEAPS_CHAT_ENDPOINT=https://freeleaps-alpha.com/api/chat/
export FREELEAPS_CONTENT_ENDPOINT=http://52.149.35.244:8013/api/content/
export FREELEAPS_NOTIFICATION_ENDPOINT=http://52.149.35.244:8003/api/notification/
export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://52.149.35.244:8005/api/central_storage/
export FREELEAPS_AUTHENTICATION_ENDPOINT=http://52.149.35.244:8004/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=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0
EOFinner
fi
# Effect the environment variables in the current shell
source /home/.devbox/freeleaps/apps/.env
# Ensure /home/.devbox/logs exists
mkdir -p /home/.devbox/logs
# Start WebAPI service
echo "Starting WebAPI service..."
pushd /home/.devbox/freeleaps/apps
cp /home/.devbox/freeleaps/backend_env.sh /home/.devbox/freeleaps/apps/backend_env.sh
# 5. Istall python3.10 and venv module
echo "5. Istall python3.10 and venv module"
sudo apt update
sudo apt install python3.10 python3.10-venv -y
# make sore python3.10 is installed
if ! command -v python3.10 &>/dev/null; then
echo "ERROR: Python3.10 is not installed."
exit 1
fi
# Upgrade pip and install virtualenv
echo "7. Upgrade pip and install virtualenv"
python3.10 -m ensurepip --upgrade
python3.10 -m pip install --upgrade pip
# 8. Create and activate a virtual environment
echo "8. Create and activate a virtual environment"
python3.10 -m venv venv_t
sleep 5
# CHeck if the virtual environment is created
if [ ! -f "venv_t/bin/activate" ]; then
echo "ERROR: The virtual environment cannot be created"
exit 1
fi
echo '============================================'
echo ' Start to activate virtual environment'
echo '============================================'
source venv_t/bin/activate
source /home/.devbox/freeleaps/apps/.env
# Verify the virtual environment is activated
if [[ "\$VIRTUAL_ENV" != "" ]]; then
echo "Virtual environment activate: \$VIRTUAL_ENV"
else
echo "ERROR: The virtual environment cannot be startup \$VIRTUAL_ENV"
exit 1
fi
echo '============================================'
echo ' Install requirements'
echo '============================================'
pip install -r /home/.devbox/freeleaps/apps/requirements.txt
echo '============================================'
echo 'Start to run start_webapi.sh'
echo '============================================'
./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 &
BACKEND_PID=\$!
# Save BACKEND_PID to a file \${WORKING_HOME}/.backend.pid: Stores the process id of backend process.
echo "\$BACKEND_PID" > /home/.devbox/.backend.pid
echo '============================================'
echo 'Check if the WebAPI service started successfully'
echo '============================================'
sleep 30
# 30 attempts, 5 seconds each, total wait time 2.5 minutes
MAX_ATTEMPTS=30
ATTEMPT=0
echo "Waiting for WebAPI service to become healthy..."
while [ \$ATTEMPT -lt \$MAX_ATTEMPTS ]; do
HTTP_CODE=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$DEVBOX_BACKEND_PORT/docs")
# Check HTTP Code 200
if [ "\$HTTP_CODE" -eq 200 ]; then
echo "Backend Swagger UI is available at \$URL (HTTP \$HTTP_CODE)"
break
else
echo "Waiting for Swagger UI to become available... Attempt \$((ATTEMPT+1))"
ATTEMPT=\$((ATTEMPT+1))
sleep 5 # Wait 5 seconds
fi
done
if [ \$ATTEMPT -eq \$MAX_ATTEMPTS ]; then
echo "ERROR: WebAPI failed to start after \$MAX_ATTEMPTS attempts"
exit 1
fi
echo '============================================'
echo ' Start frontend service locally'
echo '============================================'
pushd /home/.devbox/freeleaps/frontend
# start the frontend service
export VITE_API_URL='http://127.0.0.1:8002'
export VITE_WEBSOCKET_URL='http://127.0.0.1:8002'
if [[ \$USE_LOCAL_COMPONENT_VAL == true ]]; then
export VITE_PROXY_WEBSOCKET_CHAT_URL='ws://localhost:8012'
export VITE_PROXY_API_CHAT_URL='http://localhost:8012'
else
export VITE_PROXY_WEBSOCKET_CHAT_URL='wss://freeleaps-alpha.com'
export VITE_PROXY_API_CHAT_URL='https://freeleaps-alpha.com'
fi
npm update
# 1⃣ Install pnpm globally
npm install -g pnpm
# 2⃣ Verify installation
pnpm --version
# 3⃣ Clean up old dependencies
rm -rf node_modules pnpm-lock.yaml
# 4⃣ Install dependencies (ensuring lockfile updates)
pnpm install --no-frozen-lockfile
# 5⃣ Build the project
npm run build
# 6⃣ Format the code (Optional)
npm run format
# Start the frontend service with nohup in order to keep it running after the SSH session is closed. Save the process ID of the frontend service
nohup npm run dev > /home/.devbox/logs/frontend.logs 2>&1 &
FRONTEND_PID=\$!
echo "npm run dev has been started with PID: \$FRONTEND_PID"
echo "\$FRONTEND_PID" > /home/.devbox/.frontend.pid
echo '============================================'
# Wait for the frontend service to start
sleep 30
# 30 attempts, 10 seconds each, total wait time 5 minutes
MAX_ATTEMPTS=30
ATTEMPT=0
echo "Waiting for Frontend service to start..."
while [ \$ATTEMPT -lt \$MAX_ATTEMPTS ]; do
HTTP_CODE=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$DEVBOX_FRONTEND_PORT/")
# Check HTTP Code 200
if [ "\$HTTP_CODE" -eq 200 ]; then
echo "Frontend is available (HTTP \$HTTP_CODE)"
break
else
echo "Waiting for Frontend to become available... (http://localhost:\$DEVBOX_FRONTEND_PORT), (HTTP \$HTTP_CODE) Attempt \$((ATTEMPT+1))"
ATTEMPT=\$((ATTEMPT+1))
sleep 10
fi
done
if [ \$ATTEMPT -eq \$MAX_ATTEMPTS ]; then
echo "ERROR: Frontend failed to start after \$MAX_ATTEMPTS attempts"
exit 1
fi
echo "Freeleaps services started successfully"
EOF
# -------------------------------------------------------------------
# 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. Open up the frontend by visiting: http://localhost:${DEVBOX_FRONTEND_PORT}"
echo "3. Log files can be viewed at:"
echo " - Backend logs: ${WORKING_HOME}/logs/backend.logs"
echo " - Frontend logs: ${WORKING_HOME}/logs/frontend.logs"
echo " DevBox container ID: $WORKING_HOME/.devbox-instance"
echo " Backend PID: $WORKING_HOME/.backend.pid"
echo " Frontend PID: $WORKING_HOME/.frontend.pid"
echo "==========================================================="
echo
}
# :command.function
devbox_deinit_command() {
# src/deinit_command.sh
echo "# 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 FORCE="$(get_arg '--force')"
# print the parameters
echo "==> Deinitialization parameters:"
echo " WORKING_HOME = $WORKING_HOME"
echo " CLEAR_LOGS = $CLEAR_LOGS"
echo " CLEAR_REPO = $CLEAR_REPO"
echo " FORCE = $FORCE"
echo "==> 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")
echo "==> 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"
fi
if [[ -f "$WORKING_HOME/.gitea-instance" ]]; then
local gitea_container_id
gitea_container_id=$(cat "$WORKING_HOME/.gitea-instance")
echo "==> Stopping and removing Gitea container: $gitea_container_id"
docker stop "$gitea_container_id" &>/dev/null || true
docker rm "$gitea_container_id" &>/dev/null || true
rm -f "$WORKING_HOME/.gitea-instance"
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")
echo "==> Stopping and removing MongoDB container: $mongodb_container_id"
docker stop "$mongodb_container_id" &>/dev/null || true
docker rm "$mongodb_container_id" &>/dev/null || true
rm -f "$WORKING_HOME/.mongodb-instance"
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")
echo "==> Stopping and removing RabbitMQ container: $rabbitmq_container_id"
docker stop "$rabbitmq_container_id" &>/dev/null || true
docker rm "$rabbitmq_container_id" &>/dev/null || true
rm -f "$WORKING_HOME/.rabbitmq-instance"
fi
# Stop and remove other components
local components=("devsvc" "notification" "content" "central_storage" "authentication")
for component in "${components[@]}"; do
if [[ -f "$WORKING_HOME/.${component}-instance" ]]; then
local component_container_id
component_container_id=$(cat "$WORKING_HOME/.${component}-instance")
echo "==> Stopping and removing ${component} container: $component_container_id"
docker stop "$component_container_id" &>/dev/null || true
docker rm "$component_container_id" &>/dev/null || true
rm -f "$WORKING_HOME/.${component}-instance"
fi
done
# Clear the DevBox container logs
if [[ "$CLEAR_LOGS" == "true" ]]; then
echo "==> Clearing logs in $WORKING_HOME/logs..."
if [[ -d "$WORKING_HOME/logs" ]]; then
sudo chown -R $(whoami):$(whoami) "$WORKING_HOME/logs"
rm -rf "$WORKING_HOME/logs" 2>/dev/null || true
mkdir -p "$WORKING_HOME/logs" 2>/dev/null || true
fi
else
echo "==> Skipping log clearing."
fi
# Clear the source repository
if [[ "$CLEAR_REPO" == "true" && -d "$WORKING_HOME/freeleaps" ]]; then
echo "==> Deleting source repository at $WORKING_HOME/freeleaps"
sudo chown -R $(whoami):$(whoami) "$WORKING_HOME/freeleaps"
rm -rf "$WORKING_HOME/freeleaps" 2>/dev/null || true
rmdir "$WORKING_HOME/freeleaps" 2>/dev/null || true
else
echo "==> Skipping repository deletion."
fi
if [[ "$CLEAR_LOGS" == "true" ]]; then
# Check if logs directory is removed
if [[ -d "$WORKING_HOME/logs" ]]; then
echo "[WARNING] $WORKING_HOME/logs still exists after removal."
rm -rf "$WORKING_HOME/logs"
else
echo "==> Logs directory removed successfully."
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 -
for i in {1..5}; do
echo -n "="
sleep 1
done
# Remove the use-local-component file
rm -f "$WORKING_HOME/.use-local-component"
echo "> DevBox deinitialization completed."
}
# :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')"
# 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
echo "ERROR: DevBox container is not running. Please run 'devbox init' first."
exit 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
echo "==> Using local components for Freeleaps services."
USE_LOCAL_COMPONENT="true"
else
echo "==> 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=( "gitea" "mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication")
else
COMPONENTS=("$COMPONENT")
fi
else
# If no component is specified, start all components
if [[ -z "$COMPONENT" ]]; then
COMPONENTS=("mongodb" "rabbitmq" "devbox")
else
if [[ "$COMPONENT" == "devsvc" || "$COMPONENT" == "notification" || "$COMPONENT" == "content" || "$COMPONENT" == "central_storage" || "$COMPONENT" == "authentication" ]]; then
echo "ERROR: Remote component $COMPONENT cannot be restarted."
exit 1
fi
COMPONENTS=("$COMPONENT")
fi
fi
# Start the specified components
for comp in "${COMPONENTS[@]}"; do
case "$comp" in
"gitea" | "mongodb" | "rabbitmq" | "devbox" | "devsvc" | "notification" | "content" | "central_storage" | "authentication")
echo "==> Starting $comp service..."
# Check if the component container file exists
local component_container_id_file_path="${WORKING_HOME}/.${comp}-instance"
if [[ ! -f "$component_container_id_file_path" ]]; then
echo "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
echo "==> $comp container is not running, starting container..."
# Start the container
if ! docker start "${component_container_id}"; then
echo "ERROR: Failed to start $comp container."
else
echo "==> $comp container started successfully."
fi
else
echo "==> $comp container is already running."
fi
fi
;;
*)
echo "ERROR: Unknown component: $comp"
exit 1
;;
esac
done
if [[ "$FREELEAPS_ENDPOINT" == "true" ]]; 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
echo "==> Starting Freeleaps frontend and backend services..."
# Start the backend and frontend services
docker exec -i "$devbox_container_id" bash <<EOF
# Start the backend and frontend services
echo "Starting backend and frontend services..."
# Start the backend service
echo '============================================'
echo ' Start to run webapi.main:app '
echo '============================================'
pushd /home/.devbox/freeleaps/apps
# CHeck if the virtual environment is created
if [ ! -f "venv_t/bin/activate" ]; then
echo "ERROR: The virtual environment cannot be created"
exit 1
fi
echo '============================================'
echo ' Start to activate virtual environment'
echo '============================================'
source venv_t/bin/activate
source /home/.devbox/freeleaps/apps/.env
# Verify the virtual environment is activated
if [[ "\$VIRTUAL_ENV" != "" ]]; then
echo "Virtual environment activate: \$VIRTUAL_ENV"
else
echo "ERROR: The virtual environment cannot be startup \$VIRTUAL_ENV"
exit 1
fi
# 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 &
BACKEND_PID=\$!
# Save BACKEND_PID to a file \${WORKING_HOME}/.backend.pid: Stores the process id of backend process.
echo "\$BACKEND_PID" > /home/.devbox/.backend.pid
# Check if the backend service started successfully
sleep 10
if ! ps -p "\$BACKEND_PID" &>/dev/null; then
echo "ERROR: Backend service failed to start."
exit 1
fi
# Test backend and frontend services
echo "Testing backend and frontend services..."
# Test the backend service
echo "Testing backend service..."
attempt=0
max_attempts=10
while [ \$attempt -lt \$max_attempts ]; do
http_code=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$SERVICE_API_ACCESS_PORT/docs")
if [ "\$http_code" -eq 200 ]; then
break
fi
attempt=\$((attempt+1))
sleep 5
done
if [ \$attempt -eq \$max_attempts ]; then
echo "ERROR: Backend service is not available after \$max_attempts attempts."
exit 1
fi
# Start the frontend service
echo '============================================'
echo ' Start frontend service locally'
echo '============================================'
pushd /home/.devbox/freeleaps/frontend
npm run dev > /home/.devbox/logs/frontend.logs 2>&1 &
FRONTEND_PID=\$!
echo "\$FRONTEND_PID" > /home/.devbox/.frontend.pid
# Check if the frontend service started successfully
sleep 10
if ! ps -p "\$FRONTEND_PID" &>/dev/null; then
echo "ERROR: Frontend service failed to start."
exit 1
fi
# Test the frontend service
WEB_APP_ACCESS_PORT=\$(cat /home/.devbox/.devbox-frontend-port)
echo "Testing frontend service... PORT: \$WEB_APP_ACCESS_PORT"
attempt=0
max_attempts=10
while [ \$attempt -lt \$max_attempts ]; do
HTTP_CODE=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$WEB_APP_ACCESS_PORT/")
# Check HTTP Code 200
if [ "\$HTTP_CODE" -eq 200 ]; then
echo "Frontend is available (HTTP \$HTTP_CODE)"
break
else
echo "Attempt \$((attempt+1)): Frontend not available (HTTP \$HTTP_CODE). Waiting..."
attempt=\$((attempt+1))
sleep 10
fi
done
if [ \$attempt -eq \$max_attempts ]; then
echo "ERROR: Frontend service is not available after \$max_attempts attempts."
exit 1
fi
echo "Backend and frontend services started successfully."
EOF
else
echo "ERROR: DevBox container is not running."
exit 1
fi
fi
echo "==> DevBox services started successfully."
}
# :command.function
devbox_stop_command() {
echo "==> Stopping DevBox services..."
local COMPONENT="$(get_arg '--component')"
local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")"
# If the DevBox container is not running, exit
if [[ -z "$COMPONENT" ]]; then
COMPONENTS=("gitea" "mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication")
else
COMPONENTS=("$COMPONENT")
fi
# Stop the specified components
for comp in "${COMPONENTS[@]}"; do
case "$comp" in
"devbox")
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
echo "==> Stopping devbox..."
docker stop "$container_id" &>/dev/null || true
# Remove the frontend and backend pid files
rm -f "${WORKING_HOME}/.backend.pid"
rm -f "${WORKING_HOME}/.frontend.pid"
else
echo "==> DevBox container is not running."
fi
else
echo "==> Backend service is not running."
fi
;;
"gitea"| "mongodb"| "rabbitmq" | "devsvc" | "notification" | "content" | "central_storage" | "authentication")
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
echo "==> Stopping $comp service..."
docker stop "$container_id" &>/dev/null || true
else
echo "==> $comp service is not running."
fi
else
echo "==> $comp service is not running."
fi
;;
*)
echo "ERROR: Unknown component: $comp"
exit 1
;;
esac
done
echo "==> All conponent services stopped successfully."
}
# :command.function
devbox_status_command() {
echo "==> 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
echo "==> DevBox container is not running."
exit 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
echo "==> DevBox container is not running."
exit 1
fi
# If no component is specified, check all components
if [[ -z "$COMPONENT" ]]; then
COMPONENTS=("mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication")
else
COMPONENTS=("$COMPONENT")
fi
# Check the status of the specified components
for comp in "${COMPONENTS[@]}"; do
case "$comp" in
"mongodb")
echo "==> Checking MongoDB status..."
if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then
local container_id
container_id=$(cat "${WORKING_HOME}/.mongodb-instance")
if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then
echo "[RESULT]: MongoDB container is running."
else
echo "[RESULT]: MongoDB container is not running."
fi
else
echo "[RESULT]: MongoDB container is not running."
fi
;;
"rabbitmq")
echo "==> Checking RabbitMQ status..."
if [[ -f "${WORKING_HOME}/.rabbitmq-instance" ]]; then
local container_id
container_id=$(cat "${WORKING_HOME}/.rabbitmq-instance")
if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then
echo "[RESULT]: RabbitMQ container is running."
else
echo "[RESULT]: RabbitMQ container is not running."
fi
else
echo "[RESULT]: RabbitMQ container is not running."
fi
;;
"devbox")
echo "==> Checking devbox service status..."
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
echo "[RESULT]: devbox container is running."
else
echo "[RESULT]: devbox container is not running."
fi
else
echo "[RESULT]: devbox container is not running."
fi
;;
"devsvc" | "notification" | "content" | "central_storage" | "authentication")
echo "==> 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
echo "[RESULT]: $comp service is running."
else
echo "[RESULT]: $comp service is not running."
fi
else
echo "[RESULT]: $comp service is not running."
fi
;;
*)
echo "ERROR: Unknown component: $comp"
exit 1
;;
esac
done
echo "==> DevBox services status checked successfully."
}
# :command.function
devbox_restart_command() {
echo "==> 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 devbox container file path
local devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance"
if [[ ! -f "$devbox_container_id_file_path" ]]; then
echo "ERROR: DevBox container is not running. Please run 'devbox init' first."
exit 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
echo "==> Using local components..."
if [[ -z "$COMPONENT" ]]; then
COMPONENTS=("gitea" "mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication")
else
COMPONENTS=("$COMPONENT")
fi
else
echo "==> Using remote components..."
if [[ -z "$COMPONENT" ]]; then
COMPONENTS=("mongodb" "rabbitmq" "devbox")
else
if [[ "$COMPONENT" == "devsvc" || "$COMPONENT" == "notification" || "$COMPONENT" == "content" || "$COMPONENT" == "central_storage" || "$COMPONENT" == "authentication" ]]; then
echo "ERROR: Remote component $COMPONENT cannot be restarted."
exit 1
fi
COMPONENTS=("$COMPONENT")
fi
fi
# Stop the specified components
for comp in "${COMPONENTS[@]}"; do
case "$comp" in
"devbox")
if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then
local container_id
container_id=$(cat "${WORKING_HOME}/.devbox-instance")
echo "==> Stopping devbox service..."
docker stop "$container_id" &>/dev/null || true
# Remove the frontend and backend pid files
rm -f "${WORKING_HOME}/.backend.pid"
rm -f "${WORKING_HOME}/.frontend.pid"
else
echo "==> Devbox is not running."
fi
;;
"gitea" | "mongodb" | "rabbitmq" | "devsvc" | "notification" | "content" | "central_storage" | "authentication")
if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then
local container_id
container_id=$(cat "${WORKING_HOME}/.${comp}-instance")
echo "==> Stopping $comp service..."
docker stop "$container_id" &>/dev/null || true
else
echo "==> $comp service is not running."
fi
;;
*)
echo "ERROR: Unknown component: $comp"
exit 1
;;
esac
done
# Start the specified components
for comp in "${COMPONENTS[@]}"; do
case "$comp" in
"gitea" | "mongodb" | "rabbitmq" | "devbox" | "devsvc" | "notification" | "content" | "central_storage" | "authentication")
echo "==> 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
echo "==> $comp service is not running."
fi
;;
*)
echo "ERROR: Unknown component: $comp"
exit 1
;;
esac
done
if [[ "$FREELEAPS_ENDPOINT" == "true" ]]; 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
# Start the backend and frontend services
docker exec -i "$devbox_container_id" bash <<EOF
# Start the backend and frontend services
echo "Starting backend and frontend services..."
# Start the backend service
echo '============================================'
echo ' Start to run webapi.main:app'
echo '============================================'
pushd /home/.devbox/freeleaps/apps
# CHeck if the virtual environment is created
if [ ! -f "venv_t/bin/activate" ]; then
echo "ERROR: The virtual environment cannot be created"
exit 1
fi
echo '============================================'
echo ' Start to activate virtual environment'
echo '============================================'
source venv_t/bin/activate
source /home/.devbox/freeleaps/apps/.env
# Verify the virtual environment is activated
if [[ "\$VIRTUAL_ENV" != "" ]]; then
echo "Virtual environment activate: \$VIRTUAL_ENV"
else
echo "ERROR: The virtual environment cannot be startup \$VIRTUAL_ENV"
exit 1
fi
# 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 &
BACKEND_PID=\$!
# Save BACKEND_PID to a file \${WORKING_HOME}/.backend.pid: Stores the process id of backend process.
echo "\$BACKEND_PID" > /home/.devbox/.backend.pid
# Check if the backend service started successfully
sleep 10
if ! ps -p "\$BACKEND_PID" &>/dev/null; then
echo "ERROR: Backend service failed to start."
exit 1
fi
# Test backend and frontend services
echo "Testing backend and frontend services..."
# Test the backend service
echo "Testing backend service..."
attempt=0
max_attempts=10
while [ \$attempt -lt \$max_attempts ]; do
http_code=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$SERVICE_API_ACCESS_PORT/docs")
if [ "\$http_code" -eq 200 ]; then
break
fi
attempt=\$((attempt+1))
sleep 5
done
if [ \$attempt -eq \$max_attempts ]; then
echo "ERROR: Backend service is not available after \$max_attempts attempts."
exit 1
fi
# Start the frontend service
echo '============================================'
echo ' Start frontend service locally'
echo '============================================'
pushd /home/.devbox/freeleaps/frontend
npm run dev > /home/.devbox/logs/frontend.logs 2>&1 &
FRONTEND_PID=\$!
echo "\$FRONTEND_PID" > /home/.devbox/.frontend.pid
# Check if the frontend service started successfully
sleep 10
if ! ps -p "\$FRONTEND_PID" &>/dev/null; then
echo "ERROR: Frontend service failed to start."
exit 1
fi
# Test the frontend service
WEB_APP_ACCESS_PORT=\$(cat /home/.devbox/.devbox-frontend-port)
echo "Testing frontend service..."
attempt=0
max_attempts=10
while [ \$attempt -lt \$max_attempts ]; do
http_code=\$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$WEB_APP_ACCESS_PORT/")
if [ "\$http_code" -eq 200 ]; then
echo "Frontend service is available (HTTP \$http_code)"
break
else
echo "Attempt \$((attempt+1)): Frontend not available (HTTP \$http_code). Waiting..."
attempt=\$((attempt+1))
sleep 10
fi
done
if [ \$attempt -eq \$max_attempts ]; then
echo "ERROR: Frontend service is not available after \$max_attempts attempts."
exit 1
fi
echo "Backend and frontend services started successfully."
EOF
fi
echo "==> DevBox services restarted successfully."
}
# :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 $#
;;
# :command.command_fallback
"")
devbox_usage >&2
exit 1
;;
*)
printf "invalid command: %s\n" "$action" >&2
exit 1
;;
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 | -F)
# :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" >&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
;;
# :flag.case
--use-local-component | -u)
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
;;
# :flag.case
--devsvc-image-repo | -D)
# :flag.case_arg
if [[ -n ${2+x} ]]; then
add_arg '--devsvc-image-repo' "$2"
shift 2
else
printf "%s\n" "--devsvc-image-repo requires an argument: --devsvc-image-repo DEVSVC_IMAGE_REPO" >&2
exit 1
fi
;;
# :flag.case
--devsvc-image-name | -M)
# :flag.case_arg
if [[ -n ${2+x} ]]; then
add_arg '--devsvc-image-name' "$2"
shift 2
else
printf "%s\n" "--devsvc-image-name requires an argument: --devsvc-image-name DEVSVC_IMAGE_NAME" >&2
exit 1
fi
;;
# :flag.case
--devsvc-image-tag | -G)
# :flag.case_arg
if [[ -n ${2+x} ]]; then
add_arg '--devsvc-image-tag' "$2"
shift 2
else
printf "%s\n" "--devsvc-image-tag requires an argument: --devsvc-image-tag DEVSVC_IMAGE_TAG" >&2
exit 1
fi
;;
# :flag.case
--notification-image-repo | -Y)
# :flag.case_arg
if [[ -n ${2+x} ]]; then
add_arg '--notification-image-repo' "$2"
shift 2
else
printf "%s\n" "--notification-image-repo requires an argument: --notification-image-repo NOTIFICATION_IMAGE_REPO" >&2
exit 1
fi
;;
--notification-image-name | -K)
# :flag.case_arg
if [[ -n ${2+x} ]]; then
add_arg '--notification-image-name' "$2"
shift 2
else
printf "%s\n" "--notification-image-name requires an argument: --notification-image-name NOTIFICATION_IMAGE_NAME" >&2
exit 1
fi
;;
--notification-image-tag | -Z)
# :flag.case_arg
if [[ -n ${2+x} ]]; then
add_arg '--notification-image-tag' "$2"
shift 2
else
printf "%s\n" "--notification-image-tag requires an argument: --notification-image-tag NOTIFICATION_IMAGE_TAG" >&2
exit 1
fi
;;
# :flag.case
--content-image-repo | -C)
if [[ -n ${2+x} ]]; then
add_arg '--content-image-repo' "$2"
shift 2
else
printf "%s\n" "--content-image-repo requires an argument: --content-image-repo FREELEAPS_CONTENT_IMAGE_REPO" >&2
exit 1
fi
;;
# :flag.case
--content-image-name | -E)
if [[ -n ${2+x} ]]; then
add_arg '--content-image-name' "$2"
shift 2
else
printf "%s\n" "--content-image-name requires an argument: --content-image-name FREELEAPS_CONTENT_IMAGE_NAME" >&2
exit 1
fi
;;
# :flag.case
--content-image-tag | -H)
if [[ -n ${2+x} ]]; then
add_arg '--content-image-tag' "$2"
shift 2
else
printf "%s\n" "--content-image-tag requires an argument: --content-image-tag FREELEAPS_CONTENT_IMAGE_TAG" >&2
exit 1
fi
;;
# :flag.case
--central_storage-image-repo | -S)
if [[ -n ${2+x} ]]; then
add_arg '--central_storage-image-repo' "$2"
shift 2
else
printf "%s\n" "--central_storage-image-repo requires an argument: --central_storage-image-repo FREELEAPS_CENTRAL_STORAGE_IMAGE_REPO" >&2
exit 1
fi
;;
# :flag.case
--central_storage-image-name | -J)
if [[ -n ${2+x} ]]; then
add_arg '--central_storage-image-name' "$2"
shift 2
else
printf "%s\n" "--central_storage-image-name requires an argument: --central_storage-image-name FREELEAPS_CENTRAL_STORAGE_IMAGE_NAME" >&2
exit 1
fi
;;
# :flag.case
--central_storage-image-tag | -Q)
if [[ -n ${2+x} ]]; then
add_arg '--central_storage-image-tag' "$2"
shift 2
else
printf "%s\n" "--central_storage-image-tag requires an argument: --central_storage-image-tag FREELEAPS_CENTRAL_STORAGE_IMAGE_TAG" >&2
exit 1
fi
;;
# :flag.case
--authentication-image-repo | -V)
if [[ -n ${2+x} ]]; then
add_arg '--authentication-image-repo' "$2"
shift 2
else
printf "%s\n" "--authentication-image-repo requires an argument: --authentication-image-repo FREELEAPS_AUTHENTICATION_IMAGE_REPO" >&2
exit 1
fi
;;
# :flag.case
--authentication-image-name | -L)
if [[ -n ${2+x} ]]; then
add_arg '--authentication-image-name' "$2"
shift 2
else
printf "%s\n" "--authentication-image-name requires an argument: --authentication-image-name FREELEAPS_AUTHENTICATION_IMAGE_NAME" >&2
exit 1
fi
;;
# :flag.case
--authentication-image-tag | -W)
if [[ -n ${2+x} ]]; then
add_arg '--authentication-image-tag' "$2"
shift 2
else
printf "%s\n" "--authentication-image-tag requires an argument: --authentication-image-tag FREELEAPS_AUTHENTICATION_IMAGE_TAG" >&2
exit 1
fi
;;
# :flag.case
--force | -f)
# :flag.case_no_arg
add_arg '--force' '1'
shift
;;
-?*)
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
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 '--devsvc-image-tag')" ]; then
add_arg '--devsvc-image-tag' "latest-linux-arm64"
fi
if [ -z "$(get_arg '--content-image-tag')" ]; then
add_arg '--content-image-tag' "latest-linux-arm64"
fi
if [ -z "$(get_arg '--central_storage-image-tag')" ]; then
add_arg '--central_storage-image-tag' "latest-linux-arm64"
fi
if [ -z "$(get_arg '--authentication-image-tag')" ]; then
add_arg '--authentication-image-tag' "latest-linux-arm64"
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
}
# :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
;;
-?*)
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
}
# :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' "false"
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
;;
-?*)
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 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' "false"
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 "$@"