From 1bdbe1d55f7108d0d5ceee22118b164852e1f633 Mon Sep 17 00:00:00 2001 From: qiutianyong Date: Thu, 6 Feb 2025 10:08:57 +0800 Subject: [PATCH 01/32] Update for the first version of devbox script to start up freeleaps local environment on WSL --- devbox/devbox.local/devbox | 1653 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1653 insertions(+) create mode 100644 devbox/devbox.local/devbox diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox new file mode 100644 index 0000000..ad4d899 --- /dev/null +++ b/devbox/devbox.local/devbox @@ -0,0 +1,1653 @@ +#!/usr/bin/env bash +# This script was generated by bashly 1.2.7 (https://bashly.dannyb.co) +# Modifying it manually is not recommended + +# :wrapper.bash3_bouncer +if [[ "${BASH_VERSINFO:-0}" -lt 4 ]]; then + printf "bash version 4 or higher is required\n" >&2 + exit 1 +fi + +# :command.master_script + +# :command.version_command +version_command() { + echo "$version" +} + +# :command.usage +devbox_usage() { + printf "devbox - DevBox Command Line Tool\n\n" + + printf "%s\n" "Usage:" + printf " devbox COMMAND\n" + printf " devbox [COMMAND] --help | -h\n" + printf " devbox --version | -v\n" + echo + # :command.usage_commands + printf "%s\n" "Commands:" + printf " %s Initialize the local development environment based on DevBox container.\n" "init" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + printf "%s\n" "Options:" + + # :command.usage_fixed_flags + printf " %s\n" "--help, -h" + printf " Show this help\n" + echo + printf " %s\n" "--version, -v" + printf " Show version number\n" + echo + + # :command.usage_environment_variables + printf "%s\n" "Environment Variables:" + + # :environment_variable.usage + printf " %s\n" "FREELEAPS_USERNAME" + printf " Set the Freeleaps username for cloning the source repository.\n" + echo + + # :environment_variable.usage + printf " %s\n" "FREELEAPS_PASSWORD" + printf " Set the Freeleaps password for cloning the source repository.\n" + echo + + # :environment_variable.usage + printf " %s\n" "WORKING_HOME" + printf " Set the working home directory for DevBox.\n" + echo + + fi +} + +# :command.usage +devbox_init_usage() { + if [[ -n $long_usage ]]; then + printf "devbox init\n\n" + printf " Initialize the local development environment based on DevBox container.\n This command will pull the DevBox container image, create containers for \n various Freeleaps components, clone the source code repository, and \n persist relevant container/process information under WORKING_HOME.\n \n Sub-command \`init\` uses Docker (or another container runtime) to set \n up a local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Validate flags and environment.\n 2. Pull DevBox base image and create container.\n 3. Pull each required component image, create containers.\n 4. Clone remote source repository using FREELEAPS_USERNAME/PASSWORD.\n 5. Start back-end and front-end services.\n 6. Persist container IDs, logs, etc. into WORKING_HOME.\n\n" + else + printf "devbox init - Initialize the local development environment based on DevBox container.\n\n" + fi + printf "Alias: i\n" + echo + + printf "%s\n" "Usage:" + printf " devbox init [OPTIONS]\n" + printf " devbox init --help | -h\n" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + printf "%s\n" "Options:" + + # :command.usage_flags + # :flag.usage + printf " %s\n" "--os OS" + printf " Specifies the operating system (auto, linux, darwin, wsl2). Default is auto.\n" + printf " %s\n" "Default: auto" + echo + + # :flag.usage + printf " %s\n" "--arch ARCH" + printf " Specifies the architecture (auto, amd64, arm64). Default is auto.\n" + printf " %s\n" "Default: auto" + echo + + # :flag.usage + printf " %s\n" "--devbox-container-name DEVBOX_CONTAINER_NAME" + printf " Specifies the DevBox container name. Default is devbox.\n" + printf " %s\n" "Default: devbox" + echo + + # :flag.usage + printf " %s\n" "--devbox-container-port DEVBOX_CONTAINER_PORT" + printf " Specifies the container port for DevBox SSH access. Default is 22222.\n" + printf " %s\n" "Default: 22222" + echo + + # :flag.usage + printf " %s\n" "--devbox-image-repo DEVBOX_IMAGE_REPO" + printf " Specifies the DevBox container image repository. Default is\n docker.io/freeleaps.\n" + printf " %s\n" "Default: docker.io/freeleaps" + echo + + # :flag.usage + printf " %s\n" "--devbox-image-name DEVBOX_IMAGE_NAME" + printf " Specifies the DevBox container image name. Default is devbox.\n" + printf " %s\n" "Default: devbox" + echo + + # :flag.usage + printf " %s\n" "--devbox-image-tag DEVBOX_IMAGE_TAG" + printf " Specifies the DevBox container image tag. Default is latest.\n" + printf " %s\n" "Default: latest" + echo + + # :flag.usage + printf " %s\n" "--working-home WORKING_HOME" + printf " Specifies the working home of DevBox CLI. Default is ${HOME}/.devbox.\n" + echo + + # :flag.usage + printf " %s\n" "--freeleaps-username FREELEAPS_USERNAME (required)" + printf " Specifies the Freeleaps.com username (Required).\n" + echo + + # :flag.usage + printf " %s\n" "--freeleaps-password FREELEAPS_PASSWORD (required)" + printf " Specifies the Freeleaps.com password (Required).\n" + echo + + # :flag.usage + printf " %s\n" "--use-local-component IS_USE_LOCAL_COMPONENT" + printf " Check if use local component or use online dev environment. (Default: false, use online service) (Optional)\n" + echo + + # :flag.usage + printf " %s\n" "--devsvc-image-repo DEVSVC_IMAGE_REPO" + printf " Specifies the repository for devsvc component. (Optional)\n" + echo + + # :flag.usage + printf " %s\n" "--devsvc-image-name DEVSVC_IMAGE_NAME" + printf " Specifies the image name for devsvc component. (Optional)\n" + echo + + # :flag.usage + printf " %s\n" "--devsvc-image-tag DEVSVC_IMAGE_TAG" + printf " Specifies the image tag for devsvc component. (Optional, default=latest)\n" + printf " %s\n" "Default: latest" + echo + + # 用于 notification 组件的 usage 说明 + printf " %s\n" "--notification-image-repo NOTIFICATION_IMAGE_REPO" + printf " Specifies the repository for notification component. (Optional)\n" + echo + + printf " %s\n" "--notification-image-name NOTIFICATION_IMAGE_NAME" + printf " Specifies the image name for notification component. (Optional)\n" + echo + + printf " %s\n" "--notification-image-tag NOTIFICATION_IMAGE_TAG" + printf " Specifies the image tag for notification component. (Optional, default=latest)\n" + printf " %s\n" "Default: latest" + echo + + # 用于 content 组件的 usage 说明 + printf " %s\n" "--content-image-repo CONTENT_IMAGE_REPO" + printf " Specifies the repository for content component. (Optional)\n" + echo + + printf " %s\n" "--content-image-name CONTENT_IMAGE_NAME" + printf " Specifies the image name for content component. (Optional)\n" + echo + + printf " %s\n" "--content-image-tag CONTENT_IMAGE_TAG" + printf " Specifies the image tag for content component. (Optional, default=latest)\n" + printf " %s\n" "Default: latest" + echo + + # 用于 central_storage 组件的 usage 说明 + printf " %s\n" "--central_storage-image-repo CENTRAL_STORAGE_IMAGE_REPO" + printf " Specifies the repository for central_storage component. (Optional)\n" + echo + + printf " %s\n" "--central_storage-image-name CENTRAL_STORAGE_IMAGE_NAME" + printf " Specifies the image name for central_storage component. (Optional)\n" + echo + + printf " %s\n" "--central_storage-image-tag CENTRAL_STORAGE_IMAGE_TAG" + printf " Specifies the image tag for central_storage component. (Optional, default=latest)\n" + printf " %s\n" "Default: latest" + echo + + # 用于 authentication 组件的 usage 说明 + printf " %s\n" "--authentication-image-repo AUTHENTICATION_IMAGE_REPO" + printf " Specifies the repository for authentication component. (Optional)\n" + echo + + printf " %s\n" "--authentication-image-name AUTHENTICATION_IMAGE_NAME" + printf " Specifies the image name for authentication component. (Optional)\n" + echo + + printf " %s\n" "--authentication-image-tag AUTHENTICATION_IMAGE_TAG" + printf " Specifies the image tag for authentication component. (Optional, default=latest)\n" + printf " %s\n" "Default: latest" + echo + + # :flag.usage + printf " %s\n" "--force, -f" + printf " Force initialization even if resources already exist.\n" + echo + + # :command.usage_fixed_flags + printf " %s\n" "--help, -h" + printf " Show this help\n" + echo + + # :command.usage_examples + printf "%s\n" "Examples:" + printf " devbox init --os=linux --arch=amd64 --freeleaps-username alice\n --freeleaps-password secret\n" + printf " devbox init \ --devbox-container-name custom-devbox \ --devbox-container-port\n 22222 \ --freeleaps-username alice \ --freeleaps-password secret\n" + echo + + fi +} + +# :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() { + if ((${#args[@]})); then + readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort) + echo args: + for k in "${sorted_keys[@]}"; do + echo "- \${args[$k]} = ${args[$k]}" + done + else + echo args: none + fi + + if ((${#deps[@]})); then + readarray -t sorted_keys < <(printf '%s\n' "${!deps[@]}" | sort) + echo + echo deps: + for k in "${sorted_keys[@]}"; do + echo "- \${deps[$k]} = ${deps[$k]}" + done + fi + + if ((${#env_var_names[@]})); then + readarray -t sorted_names < <(printf '%s\n' "${env_var_names[@]}" | sort) + echo + echo "environment variables:" + for k in "${sorted_names[@]}"; do + echo "- \$$k = ${!k:-}" + 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 +} + +# :command.command_functions +# :command.function +devbox_init_command() { + + # src/init_command.sh + #!/usr/bin/env bash + # + # File: src/init_command.sh + # This file implements the `devbox init` command logic. + + # Make sure the function name is `devbox_init_command`. + + 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 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 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 + + # --force flag to overwrite existing resources + local FORCE_INIT="${args_force}" + local OS="${args['--os']}" + local ARCH="${args['--arch']}" + local DEVBOX_NAME="${args['--devbox-container-name']}" + local DEVBOX_PORT="${args['--devbox-container-port']}" + local DEVBOX_FRONTEND_PORT="${args['--devbox-frontend-port']}" + local DEVBOX_BACKEND_PORT="${args['--devbox-backend-port']}" + local DEVBOX_REPO="${args['--devbox-image-repo']}" + local DEVBOX_IMAGE="${args['--devbox-image-name']}" + local DEVBOX_TAG="${args['--devbox-image-tag']}" + local WORKING_HOME="${args['--working-home']:-${WORKING_HOME:-${HOME}/.devbox}}" + local FREELEAPS_USERNAME="${args['--freeleaps-username']}" + local FREELEAPS_PASSWORD="${args['--freeleaps-password']}" + local USE_LOCAL_COMPONENT="${args['--use-local-component']:-false}" + local DEVSVC_REPO="${args['--devsvc-image-repo']}" + local DEVSVC_IMAGE="${args['--devsvc-image-name']}" + local DEVSVC_TAG="${args['--devsvc-image-tag']}" + local NOTIFICATION_REPO="${args['--notification-image-repo']}" + local NOTIFICATION_IMAGE="${args['--notification-image-name']}" + local NOTIFICATION_TAG="${args['--notification-image-tag']}" + local CONTENT_REPO="${args['--content-image-repo']}" + local CONTENT_IMAGE="${args['--content-image-name']}" + local CONTENT_TAG="${args['--content-image-tag']}" + local CENTRAL_STORAGE_REPO="${args['--central_storage-image-repo']}" + local CENTRAL_STORAGE_IMAGE="${args['--central_storage-image-name']}" + local CENTRAL_STORAGE_TAG="${args['--central_storage-image-tag']}" + local AUTHENTICATION_REPO="${args['--authentication-image-repo']}" + local AUTHENTICATION_IMAGE="${args['--authentication-image-name']}" + local AUTHENTICATION_TAG="${args['--authentication-image-tag']}" + + local FORCE_INIT="${args['--force']}" + + local is_pull_all_components=true + + for component in "${components[@]}"; do + if [[ -z "${args["${component}_image_repo"]}" ]]; then + is_pull_all_components=false + break + fi + done + + + 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 " NOTIFICATION_REPO = $NOTIFICATION_REPO" + echo " NOTIFICATION_IMAGE= $NOTIFICATION_IMAGE" + echo " NOTIFICATION_TAG = $NOTIFICATION_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 " 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 + + # ------------------------------------------------------------------- + # 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}')" + # 若无法获取或小于 1GB (1048576 KB),报错 + if [[ -z "$free_space_kb" || $free_space_kb -lt 1048576 ]]; then + echo "ERROR: Insufficient disk space (need >1GB)." + 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(sample:ping docker.com) + if ! ping -c 1 docker.com &>/dev/null; then + echo "ERROR: Network unreachable." + exit 1 + fi + + # ------------------------------------------------------------------- + # 4. If .devbox-instance exists, --force,use it + # ------------------------------------------------------------------- + if [[ -f "$WORKING_HOME/.devbox-instance" && -z "$FORCE_INIT" ]]; then + echo "ERROR: DevBox already initialized. Use --force to overwrite." + exit 1 + 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 + + + # 如有 --force 且存在旧容器,则可在此删除旧容器/文件(也可在下面先检查容器再删) + # ... + + # ------------------------------------------------------------------- + # 5. 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 + else + echo "ERROR: Container named $DEVBOX_NAME already exists. Use --force to remove it." + exit 1 + fi + fi + + + # 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 /var/run/docker.sock:/var/run/docker.sock \ + "$devbox_full_image" 2>/dev/null + )" + + if [[ -z "$container_id" ]]; then + echo "ERROR: Failed to create DevBox container." + exit 1 + fi + echo "DevBox container created: $container_id" + + # record container id + echo "$container_id" > "${WORKING_HOME}/.devbox-instance" + + # ------------------------------------------------------------------- + # 6. linbwang: pull and start other components + # ------------------------------------------------------------------- + +echo "==> [INIT] Starting Freeleaps services... $USE_LOCAL_COMPONENT" + +if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then + echo ' ===> Using local components for Freeleaps services.' + # Local components for Freeleaps services (devsvc, notification, content, central_storage, authentication) + + for component in "${components[@]}"; do + local COMPONENT_REPO="${component^^}_REPO" + COMPONENT_REPO="${COMPONENT_REPO//-/_}" + local COMPONENT_IMAGE="${component^^}_IMAGE" + COMPONENT_IMAGE="${COMPONENT_IMAGE//-/_}" + local COMPONENT_TAG="${component^^}_TAG" + COMPONENT_TAG="${COMPONENT_TAG//-/_}" + + # check if is_pull_all_components is false and component repo and component image parameter not empty + if [[ "$is_pull_all_components" == false && -n "${!COMPONENT_REPO}" && -n "${!COMPONENT_IMAGE}" ]]; then + continue + fi + + # Pull the component image + local component_full_image="${!COMPONENT_REPO}/${!COMPONENT_IMAGE}:${!COMPONENT_TAG}" + echo "==> Pulling ${component} image: $component_full_image" + if ! docker pull "$component_full_image"; then + echo "WARNING: Failed to pull ${component} image: $component_full_image, please Check the image and specify it to initialize the component." + fi + + # if container with same name exists, remove it + if docker ps -a --format '{{.Names}}' | grep -q "^${component}\$"; then + if [[ -n "$FORCE_INIT" ]]; then + echo "==> Removing existing container named $component ..." + docker stop "$component" &>/dev/null || true + docker rm "$component" &>/dev/null || true + else + echo "WARNING: Container named $component already exists. Use --force to remove it." + fi + fi + + echo "==> Creating and starting ${component} container..." + local component_container_id + component_container_id="$( + docker run -d \ + --name "$component" \ + --link "$DEVBOX_NAME" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + "$component_full_image" 2>/dev/null + )" + if [[ -z "$component_container_id" ]]; then + echo "WARNING: Failed to create ${component} container. please Check the image and specify it to initialize the component." + fi + echo "$component_container_id" > "${WORKING_HOME}/.${component}-instance" + echo "${component} container created: $component_container_id" + done +else + echo ' ===> Using online components for Freeleaps services.' +fi + +docker exec -i "$DEVBOX_NAME" bash < Using local components" + + cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.env + # Online endpoint info + export MONGODB_NAME=freeleaps2 + export MONGODB_URI=mongodb://172.18.0.1:27017/ + export SITE_ACCESS_PORT=80 + export FREELEAPS_ENV=dev + export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd + export STRIPE_WEBHOOK_SECRET= + export SITE_URL_ROOT=http://localhost/ + export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" + export RABBITMQ_HOST=172.18.0.1 + export RABBITMQ_PORT=5672 + export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ + export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ + export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://localhost:8005/api/central_storage/ + export JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b + export FREELEAPS_AUTHENTICATION_ENDPOINT=http://localhost:8004/api/auth/ + export EMAIL_FROM=freeleaps@freeleaps.com +EOFinner +else + cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.env + # Online endpoint info + export MONGODB_NAME=freeleaps2-mongo + export MONGODB_URI=mongodb://172.18.0.1:27017/ + export SITE_ACCESS_PORT=80 + export FREELEAPS_ENV=dev + export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd + export STRIPE_WEBHOOK_SECRET= + export SITE_URL_ROOT=http://localhost/ + export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" + export RABBITMQ_HOST=172.18.0.1 + export RABBITMQ_PORT=5672 + export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ + export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ + export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://localhost:8005/api/central_storage/ + export JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b + export FREELEAPS_AUTHENTICATION_ENDPOINT=http://localhost:8004/api/auth/ + export EMAIL_FROM=freeleaps@freeleaps.com +EOFinner +fi + +source ~/freeleaps_home/freeleaps/.dev.env +cp ~/freeleaps_home/freeleaps/.dev.env ~/freeleaps_home/freeleaps/apps/.env + +cp ~/freeleaps_home/freeleaps/.dev.env ~/freeleaps_home/freeleaps/.env +source ~/freeleaps_home/freeleaps/.dev.env +source ~/freeleaps_home/freeleaps/apps/.env + +echo "Step 3. [INFO] Checking Docker installation..." + + # 1. Check docker CLI installted + if ! command -v docker >/dev/null 2>&1; then + echo "[ERROR] Docker CLI is not installed." + exit 1 + fi + + echo "[INFO] Docker CLI is installed. Checking daemon..." + + # 2. Check if Docker Daemon running + if docker info >/dev/null 2>&1; then + echo '============================================' + echo "[OK] Docker daemon is running." + echo '============================================' + else + + if grep -qi microsoft /proc/version; then + echo "[INFO] Detected WSL environment. Verifying Docker socket..." + if [ -S /var/run/docker.sock ]; then + echo "[OK] Docker socket found." + else + # WSL 2 with Docker Desktop + 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." + else + echo "[ERROR] Failed to start Docker using systemctl." + exit 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." + else + echo "[ERROR] Failed to start Docker using service command." + exit 1 + fi + else + echo "[ERROR] Neither systemctl nor service command found. Please start Docker manually." + exit 1 + fi + fi + + fi + fi + fi + + + +# 3.Create and start MongoDB container +echo "Step 4=2. [INFO] Starting MongoDB container..." + +MONGO_CONTAINER_NAME="freeleaps2-mongo" +MONGO_IMAGE="mongo:latest" + +# if a container with the same name exists, remove it +if docker ps -a --format '{{.Names}}' | grep -q "^\${MONGO_CONTAINER_NAME}\$"; then + echo "==> Removing existing MongoDB container..." + docker stop "\$MONGO_CONTAINER_NAME" &>/dev/null || true + docker rm "\$MONGO_CONTAINER_NAME" &>/dev/null || true +fi + +echo "==> Pulling MongoDB image: \$MONGO_IMAGE" +if ! docker pull "\$MONGO_IMAGE"; then + echo "ERROR: Failed to pull MongoDB image: \$MONGO_IMAGE" + exit 1 +fi + +echo "==> Starting MongoDB container..." +mongo_container_id=\$(docker run -d --name "\$MONGO_CONTAINER_NAME" -p 27017:27017 "\$MONGO_IMAGE") +if [[ -z "\$mongo_container_id" ]]; then + echo "ERROR: Failed to start MongoDB container." + exit 1 +fi +echo "MongoDB container started successfully: \$mongo_container_id" + +sleep 10 + + +echo '============================================' + +MAX_ATTEMPTS=10 +ATTEMPT=0 +while [ \$ATTEMPT -lt \$MAX_ATTEMPTS ]; do + if docker exec "\$MONGO_CONTAINER_NAME" mongosh --eval "db.adminCommand('ping')" 2>/dev/null | grep -q '{ ok: 1 }'; then + echo "MongoDB health check passed." + break + fi + echo "Waiting for MongoDB to be ready... (Attempt \$((ATTEMPT+1)))" + sleep 10 + ATTEMPT=\$((ATTEMPT+1)) +done + +if [ \$ATTEMPT -eq \$MAX_ATTEMPTS ]; then + echo "ERROR: MongoDB health check failed." + exit 1 +fi + +echo "Step 4. [INFO] Starting MongoDB container..." + + +pushd ~/freeleaps_home/freeleaps + +# 4. 安装RABBITMQ_HOST=localhost, RABBITMQ_PORT=5672 docker 容器镜像,并检查是否成功启动 +echo "Step 4. [INFO] Starting RabbitMQ container..." + +RABBITMQ_CONTAINER_NAME="freeleaps2-rabbitmq" +RABBITMQ_IMAGE="rabbitmq:latest" + +# If a container with the same name exists, remove it +if docker ps -a --format '{{.Names}}' | grep -q "^\${RABBITMQ_CONTAINER_NAME}\$"; then + echo "==> Removing existing RabbitMQ container..." + docker stop "\${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true + docker rm "\${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true +fi + +# Pull the RabbitMQ image +echo "==> Pulling RabbitMQ image: \${RABBITMQ_IMAGE}" +if ! docker pull "\${RABBITMQ_IMAGE}"; then + echo "ERROR: Failed to pull RabbitMQ image: \${RABBITMQ_IMAGE}" + exit 1 +fi + +# Run the RabbitMQ container mapping port 5672 +echo "==> Starting RabbitMQ container..." +rabbitmq_container_id=\$(docker run -d --name "\${RABBITMQ_CONTAINER_NAME}" -p 5672:5672 "\${RABBITMQ_IMAGE}") + +if [[ -z "\${rabbitmq_container_id}" ]]; then + echo "ERROR: Failed to start RabbitMQ container." + exit 1 +fi +echo "RabbitMQ container started successfully: \${rabbitmq_container_id}" + +# Allow RabbitMQ some time to initialize +sleep 20 + +# Check RabbitMQ health via rabbitmqctl +if docker exec "\${RABBITMQ_CONTAINER_NAME}" rabbitmqctl status &>/dev/null; then + echo "RabbitMQ health check passed." +else + echo "ERROR: RabbitMQ health check failed." + exit 1 +fi +echo "Step 5. [INFO] Starting RabbitMQ container..." + +echo '============================================' + +# Run start_webapi.sh and check if success started +echo "Step 4: Run start_webapi.sh and check if success started" + +# Start WebAPI service +echo "Starting WebAPI service..." +# Make sure the logs directory exists +mkdir -p ~/freeleaps_home/logs +pushd ~/freeleaps_home/freeleaps/apps +cp ~/freeleaps_home/freeleaps/backend_env.sh ~/freeleaps_home/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 + +echo '============================================1' +dpkg -l | grep python3.10-venv + +echo '============================================1' +# make sore python3.10 is installed +if ! command -v python3.10 &>/dev/null; then + echo "ERROR: Python3.10 is not installed." + exit 1 +fi + +# Set python3.10 as default python3 +echo "6.1. Set Python3.10 as default Python3" +sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1 +sudo update-alternatives --config python3 + +# Check if the ladvei +echo "7. Upgrade pip and install virtualenv" +python3.10 -m ensurepip --upgrade +python3.10 -m pip install --upgrade pip + + +echo '============================================' + +echo 'Current path: ' +pwd + +echo '============================================' + +# 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: 虚拟环境没有创建成功" + exit 1 +fi + +echo '============================================' + +echo ' Start to activate virtual environment' +echo '============================================' + +source venv_t/bin/activate + + +# 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 ~/freeleaps_home/freeleaps/apps/requirements.txt + +echo '============================================' +echo 'Start to run start_webapi.sh' +echo '============================================' +./start_webapi.sh > /tmp/webapi.logs 2>&1 & +WEBAPI_PID=\$! + + +echo '============================================' +echo 'Check if the WebAPI service started successfully' +echo '============================================' + +sleep 30 + +MAX_ATTEMPTS=30 # 30次尝试,每次10秒,总等待5分钟 +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") + # 判断返回的状态码是否为 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 # 等待 5 秒后重试 + 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 ~/freeleaps_home/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' +npm install + +npm update + +npm install -g pnpm +pnpm install +npm run build +npm run format +npm run dev + +# Wait for the frontend service to start +sleep 120 + +# 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 + # ------------------------------------------------------------------- + echo + echo "===========================================================" + echo "DevBox init completed successfully!" + echo " DevBox container ID: $container_id" + [[ -f "${WORKING_HOME}/.devsvc-instance" ]] && echo " devsvc container ID: $(cat "${WORKING_HOME}/.devsvc-instance")" + echo " Repository cloned to: $repo_dir" + echo " Back-end logs: $WORKING_HOME/logs/back-end.logs" + echo " Front-end logs: $WORKING_HOME/logs/front-end.logs" + echo "===========================================================" + echo + +} + +# :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 $# + ;; + + # :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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--os']="$2" + shift + shift + else + printf "%s\n" "--os requires an argument: --os OS" >&2 + exit 1 + fi + ;; + + # :flag.case + --arch) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--arch']="$2" + shift + shift + else + printf "%s\n" "--arch requires an argument: --arch ARCH" >&2 + exit 1 + fi + ;; + + # :flag.case + --devbox-container-name) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devbox-container-name']="$2" + shift + shift + 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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devbox-container-port']="$2" + shift + shift + else + printf "%s\n" "--devbox-container-port requires an argument: --devbox-container-port DEVBOX_CONTAINER_PORT" >&2 + exit 1 + fi + ;; + + # :flag.case + --devbox-image-repo) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devbox-image-repo']="$2" + shift + shift + 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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devbox-image-name']="$2" + shift + shift + 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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devbox-image-tag']="$2" + shift + shift + else + printf "%s\n" "--devbox-image-tag requires an argument: --devbox-image-tag DEVBOX_IMAGE_TAG" >&2 + exit 1 + fi + ;; + + # :flag.case + --working-home) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--working-home']="$2" + shift + shift + else + printf "%s\n" "--working-home requires an argument: --working-home WORKING_HOME" >&2 + exit 1 + fi + ;; + + # :flag.case + --freeleaps-username) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--freeleaps-username']="$2" + shift + shift + else + printf "%s\n" "--freeleaps-username requires an argument: --freeleaps-username FREELEAPS_USERNAME" >&2 + exit 1 + fi + ;; + + # :flag.case + --freeleaps-password) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--freeleaps-password']="$2" + shift + shift + else + printf "%s\n" "--freeleaps-password requires an argument: --freeleaps-password FREELEAPS_PASSWORD" >&2 + exit 1 + fi + ;; + + # :flag.case + --use-local-component) + if [[ -n ${2+x} ]]; then + args['--devsvc-image-repo']="$2" + shift 2 + else + printf "%s\n" "--use-local-component requires an argument: --use-local-component IS_USING_LOCAL_COMPONENT" >&2 + exit 1 + fi + ;; + + # :flag.case + --devsvc-image-repo) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devsvc-image-repo']="$2" + shift + shift + 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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devsvc-image-name']="$2" + shift + shift + 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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--devsvc-image-tag']="$2" + shift + shift + 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) + if [[ -n ${2+x} ]]; then + args['--notification-image-repo']="$2" + shift 2 + else + printf "%s\n" "--notification-image-repo requires an argument: --notification-image-repo FREELEAPS_NOTIFICATION_IMAGE_REPO" >&2 + exit 1 + fi + ;; + + # :flag.case + --notification-image-name) + if [[ -n ${2+x} ]]; then + args['--notification-image-name']="$2" + shift 2 + else + printf "%s\n" "--notification-image-name requires an argument: --notification-image-name FREELEAPS_NOTIFICATION_IMAGE_NAME" >&2 + exit 1 + fi + ;; + + # :flag.case + --notification-image-tag) + if [[ -n ${2+x} ]]; then + args['--notification-image-tag']="$2" + shift 2 + else + printf "%s\n" "--notification-image-tag requires an argument: --notification-image-tag FREELEAPS_NOTIFICATION_IMAGE_TAG" >&2 + exit 1 + fi + ;; + + # :flag.case + --content-image-repo) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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) + if [[ -n ${2+x} ]]; then + args['--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 + args['--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.required_flags_filter + if [[ -z ${args['--freeleaps-username']+x} ]]; then + printf "missing required flag: --freeleaps-username FREELEAPS_USERNAME\n" >&2 + exit 1 + fi + if [[ -z ${args['--freeleaps-password']+x} ]]; then + printf "missing required flag: --freeleaps-password FREELEAPS_PASSWORD\n" >&2 + exit 1 + fi + + # :command.default_assignments + [[ -n ${args['--os']:-} ]] || args['--os']="auto" + [[ -n ${args['--arch']:-} ]] || args['--arch']="auto" + [[ -n ${args['--devbox-container-name']:-} ]] || args['--devbox-container-name']="devbox" + [[ -n ${args['--devbox-container-port']:-} ]] || args['--devbox-container-port']="22222" + [[ -n ${args['--devbox-frontend-port']:-} ]] || args['--devbox-frontend-port']="5173" + [[ -n ${args['--devbox-backend-port']:-} ]] || args['--devbox-backend-port']="8002" + + [[ -n ${args['--devbox-image-repo']:-} ]] || args['--devbox-image-repo']="docker.io/freeleaps" + [[ -n ${args['--devbox-image-name']:-} ]] || args['--devbox-image-name']="devbox_v1" + [[ -n ${args['--devbox-image-tag']:-} ]] || args['--devbox-image-tag']="devbox_local" + [[ -n ${args['--devsvc-image-tag']:-} ]] || args['--devsvc-image-tag']="latest-linux-amd64" + [[ -n ${args['--notification-image-tag']:-} ]] || args['--notification-image-tag']="latest-linux-amd64" + [[ -n ${args['--content-image-tag']:-} ]] || args['--content-image-tag']="latest-linux-amd64" + [[ -n ${args['--central_storage-image-tag']:-} ]] || args['--central_storage-image-tag']="latest-linux-amd64" + [[ -n ${args['--authentication-image-tag']:-} ]] || args['--authentication-image-tag']="latest-linux-amd64" + [[ -n ${args['--working-home']:-} ]] || args['--working-home']="${HOME}/.devbox" + [[ -n ${args['--use-local-component']:-} ]] || args['--use-local-component']="false" + + +} + +# :command.initialize +initialize() { + version="1.0.0" + long_usage='' + set -e + + # :command.globals + declare -g -A args=() + declare -g -A deps=() + declare -g -a env_var_names=() + declare -g -a input=() + +} + +# :command.run +run() { + normalize_input "$@" + parse_requirements "${input[@]}" + + case "$action" in + "init") devbox_init_command ;; + esac +} + +initialize +run "$@" From 278c44a25f953482a264310b71887aaad386fbfa Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Thu, 6 Feb 2025 11:28:57 +0800 Subject: [PATCH 02/32] Update for frontend start with nohup --- devbox/devbox.local/devbox | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index ad4d899..b2bde4b 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1090,7 +1090,16 @@ npm install -g pnpm pnpm install npm run build npm run format -npm run dev +# 静默后台启动 npm run dev,将输出重定向到 /dev/null +nohup npm run dev > /dev/null 2>&1 & + +# 获取后台启动进程的 PID(可选) +DEV_PID=$! + +echo "npm run dev 已启动,进程号: \$DEV_PID" + + +echo '============================================' # Wait for the frontend service to start sleep 120 From 0aef2aaae6345e83f768f7a1c384b0de900cda1b Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Thu, 6 Feb 2025 11:41:58 +0800 Subject: [PATCH 03/32] Update host ip in docker environment for local mongodb and rabbitMQ --- devbox/devbox.local/devbox | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index b2bde4b..f250a00 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -723,6 +723,7 @@ export WORKING_HOME="${WORKING_HOME}" export USE_LOCAL_COMPONENT="${USE_LOCAL_COMPONENT}" export DEVBOX_BACKEND_PORT="${DEVBOX_BACKEND_PORT}" export DEVBOX_FRONTEND_PORT="${DEVBOX_FRONTEND_PORT}" +DEFAULT_IP=\$(ip route | grep default | sed -n 's/.*default via \([^ ]*\).*/\1/p') export OSTYPE="${OSTYPE}" @@ -758,14 +759,14 @@ if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.env # Online endpoint info export MONGODB_NAME=freeleaps2 - export MONGODB_URI=mongodb://172.18.0.1:27017/ + export MONGODB_URI=mongodb://\$DEFAULT_IP:27017/ export SITE_ACCESS_PORT=80 export FREELEAPS_ENV=dev export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd export STRIPE_WEBHOOK_SECRET= export SITE_URL_ROOT=http://localhost/ export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" - export RABBITMQ_HOST=172.18.0.1 + export RABBITMQ_HOST=\$DEFAULT_IP export RABBITMQ_PORT=5672 export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ @@ -778,14 +779,14 @@ else cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.env # Online endpoint info export MONGODB_NAME=freeleaps2-mongo - export MONGODB_URI=mongodb://172.18.0.1:27017/ + export MONGODB_URI=mongodb://\$DEFAULT_IP:27017/ export SITE_ACCESS_PORT=80 export FREELEAPS_ENV=dev export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd export STRIPE_WEBHOOK_SECRET= export SITE_URL_ROOT=http://localhost/ export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" - export RABBITMQ_HOST=172.18.0.1 + export RABBITMQ_HOST=\$DEFAULT_IP export RABBITMQ_PORT=5672 export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ From 4c3c0274ccbd4771f9a1bbc7bb0e8a324e3baffa Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Sat, 8 Feb 2025 16:50:43 +0800 Subject: [PATCH 04/32] Update for new change --- devbox/devbox.local/devbox | 413 +++++++++++++++++-------------------- 1 file changed, 186 insertions(+), 227 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index f250a00..e8db385 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -83,141 +83,161 @@ devbox_init_usage() { printf "%s\n" "Options:" # :command.usage_flags - # :flag.usage + # :flag.usage os (auto, linux, darwin, wsl2) printf " %s\n" "--os OS" printf " Specifies the operating system (auto, linux, darwin, wsl2). Default is auto.\n" printf " %s\n" "Default: auto" echo - # :flag.usage + # :flag.usage arch printf " %s\n" "--arch ARCH" printf " Specifies the architecture (auto, amd64, arm64). Default is auto.\n" printf " %s\n" "Default: auto" echo - # :flag.usage + # :flag.usage devbox container name printf " %s\n" "--devbox-container-name DEVBOX_CONTAINER_NAME" printf " Specifies the DevBox container name. Default is devbox.\n" printf " %s\n" "Default: devbox" echo - # :flag.usage + # :flag.usage devbox container port printf " %s\n" "--devbox-container-port DEVBOX_CONTAINER_PORT" printf " Specifies the container port for DevBox SSH access. Default is 22222.\n" printf " %s\n" "Default: 22222" echo - # :flag.usage + # :flag.usage devbox image repo printf " %s\n" "--devbox-image-repo DEVBOX_IMAGE_REPO" printf " Specifies the DevBox container image repository. Default is\n docker.io/freeleaps.\n" printf " %s\n" "Default: docker.io/freeleaps" echo - # :flag.usage + # :flag.usage devbox frontend port + printf " %s\n" "--devbox-frontend-port DEVBOX_FRONTEND_PORT" + printf " Specifies the container port for DevBox frontend access. Default is 5173.\n" + printf " %s\n" "Default: 5173" + echo + + # :flag.usage devbox backend port + printf " %s\n" "--devbox-backend-port DEVBOX_BACKEND_PORT" + printf " Specifies the container port for DevBox backend access. Default is 8002.\n" + printf " %s\n" "Default: 8002" + echo + + # :flag.usage devbox image name printf " %s\n" "--devbox-image-name DEVBOX_IMAGE_NAME" printf " Specifies the DevBox container image name. Default is devbox.\n" printf " %s\n" "Default: devbox" echo - # :flag.usage + # :flag.usage devbox image tag printf " %s\n" "--devbox-image-tag DEVBOX_IMAGE_TAG" printf " Specifies the DevBox container image tag. Default is latest.\n" printf " %s\n" "Default: latest" echo - # :flag.usage + # :flag.usage working home printf " %s\n" "--working-home WORKING_HOME" printf " Specifies the working home of DevBox CLI. Default is ${HOME}/.devbox.\n" echo - # :flag.usage + # :flag.usage freeleaps username printf " %s\n" "--freeleaps-username FREELEAPS_USERNAME (required)" - printf " Specifies the Freeleaps.com username (Required).\n" + printf " Specifies the Freeleaps.com repository username (Required).\n" echo - # :flag.usage + # :flag.usage freeleaps password printf " %s\n" "--freeleaps-password FREELEAPS_PASSWORD (required)" - printf " Specifies the Freeleaps.com password (Required).\n" + printf " Specifies the Freeleaps.com password repository (Required).\n" echo - # :flag.usage + # :flag.usage use local component printf " %s\n" "--use-local-component IS_USE_LOCAL_COMPONENT" printf " Check if use local component or use online dev environment. (Default: false, use online service) (Optional)\n" echo - # :flag.usage + # :flag.usage devsvc image repo printf " %s\n" "--devsvc-image-repo DEVSVC_IMAGE_REPO" printf " Specifies the repository for devsvc component. (Optional)\n" echo - # :flag.usage + # :flag.usage devsvc image name printf " %s\n" "--devsvc-image-name DEVSVC_IMAGE_NAME" printf " Specifies the image name for devsvc component. (Optional)\n" echo - # :flag.usage + # :flag.usage devsvc image tag printf " %s\n" "--devsvc-image-tag DEVSVC_IMAGE_TAG" printf " Specifies the image tag for devsvc component. (Optional, default=latest)\n" printf " %s\n" "Default: latest" echo - # 用于 notification 组件的 usage 说明 + # :flag.usage notification image repo printf " %s\n" "--notification-image-repo NOTIFICATION_IMAGE_REPO" printf " Specifies the repository for notification component. (Optional)\n" echo + # :flag.usage notification image name printf " %s\n" "--notification-image-name NOTIFICATION_IMAGE_NAME" printf " Specifies the image name for notification component. (Optional)\n" echo + # :flag.usage notification image tag printf " %s\n" "--notification-image-tag NOTIFICATION_IMAGE_TAG" printf " Specifies the image tag for notification component. (Optional, default=latest)\n" printf " %s\n" "Default: latest" echo - # 用于 content 组件的 usage 说明 + # :flag.usage content image repo printf " %s\n" "--content-image-repo CONTENT_IMAGE_REPO" printf " Specifies the repository for content component. (Optional)\n" echo + # :flag.usage content image name printf " %s\n" "--content-image-name CONTENT_IMAGE_NAME" printf " Specifies the image name for content component. (Optional)\n" echo + # :flag.usage content image tag printf " %s\n" "--content-image-tag CONTENT_IMAGE_TAG" printf " Specifies the image tag for content component. (Optional, default=latest)\n" printf " %s\n" "Default: latest" echo - # 用于 central_storage 组件的 usage 说明 + # :flag.usage central storage image repo printf " %s\n" "--central_storage-image-repo CENTRAL_STORAGE_IMAGE_REPO" printf " Specifies the repository for central_storage component. (Optional)\n" echo + # :flag.usage central storage image name printf " %s\n" "--central_storage-image-name CENTRAL_STORAGE_IMAGE_NAME" printf " Specifies the image name for central_storage component. (Optional)\n" echo + # :flag.usage central storage image tag printf " %s\n" "--central_storage-image-tag CENTRAL_STORAGE_IMAGE_TAG" printf " Specifies the image tag for central_storage component. (Optional, default=latest)\n" printf " %s\n" "Default: latest" echo - # 用于 authentication 组件的 usage 说明 + # :flag.usage authentication image repo printf " %s\n" "--authentication-image-repo AUTHENTICATION_IMAGE_REPO" printf " Specifies the repository for authentication component. (Optional)\n" echo + # :flag.usage authentication image name printf " %s\n" "--authentication-image-name AUTHENTICATION_IMAGE_NAME" printf " Specifies the image name for authentication component. (Optional)\n" echo + # :flag.usage authentication image tag printf " %s\n" "--authentication-image-tag AUTHENTICATION_IMAGE_TAG" printf " Specifies the image tag for authentication component. (Optional, default=latest)\n" printf " %s\n" "Default: latest" echo - # :flag.usage + # :flag.usage force printf " %s\n" "--force, -f" printf " Force initialization even if resources already exist.\n" echo @@ -631,7 +651,7 @@ devbox_init_command() { fi fi - + # Create and start DevBox container local container_id container_id="$( @@ -640,6 +660,7 @@ devbox_init_command() { -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 \ "$devbox_full_image" 2>/dev/null )" @@ -648,10 +669,10 @@ devbox_init_command() { echo "ERROR: Failed to create DevBox container." exit 1 fi - echo "DevBox container created: $container_id" # record container id - echo "$container_id" > "${WORKING_HOME}/.devbox-instance" + echo "$container_id" > "$WORKING_HOME/.devbox-instance" + # ------------------------------------------------------------------- # 6. linbwang: pull and start other components @@ -706,26 +727,130 @@ if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then if [[ -z "$component_container_id" ]]; then echo "WARNING: Failed to create ${component} container. please Check the image and specify it to initialize the component." fi - echo "$component_container_id" > "${WORKING_HOME}/.${component}-instance" + echo "$component_container_id" > "/home/.devbox/.${component}-instance" echo "${component} container created: $component_container_id" done -else - echo ' ===> Using online components for Freeleaps services.' -fi + + # 3.Create and start MongoDB container + echo "Step 3. [INFO] Starting MongoDB container..." + + MONGO_CONTAINER_NAME="freeleaps2-mongo" + MONGO_IMAGE="mongo:latest" + + # if a container with the same name exists, remove it + if docker ps -a --format '{{.Names}}' | grep -q "^${MONGO_CONTAINER_NAME}\$"; then + echo "==> Removing existing MongoDB container..." + docker stop "$MONGO_CONTAINER_NAME" &>/dev/null || true + docker rm "$MONGO_CONTAINER_NAME" &>/dev/null || true + fi + + echo "==> Pulling MongoDB image: $MONGO_IMAGE" + if ! docker pull "$MONGO_IMAGE"; then + echo "ERROR: Failed to pull MongoDB image: $MONGO_IMAGE" + exit 1 + fi + + echo "==> Starting MongoDB container..." + mongo_container_id=$(docker run -d --name "$MONGO_CONTAINER_NAME" -p 27017:27017 "$MONGO_IMAGE") + if [[ -z "$mongo_container_id" ]]; then + echo "ERROR: Failed to start MongoDB container." + exit 1 + fi + echo "MongoDB container started successfully: $mongo_container_id" + + sleep 10 + + + echo '============================================' + + MAX_ATTEMPTS=10 + ATTEMPT=0 + while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do + if docker exec "$MONGO_CONTAINER_NAME" mongosh --eval "db.adminCommand('ping')" 2>/dev/null | grep -q '{ ok: 1 }'; then + echo "MongoDB health check passed." + break + fi + echo "Waiting for MongoDB to be ready... (Attempt $((ATTEMPT+1)))" + sleep 10 + ATTEMPT=$((ATTEMPT+1)) + done + + if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then + echo "ERROR: MongoDB health check failed." + exit 1 + fi + + echo "Step 4. [INFO] Starting MongoDB container..." + +else + echo '============================================' + echo ' ===> Using online components for Freeleaps services.' + echo '============================================' +fi + # 4. Pull and start RabbitMQ container + echo "Step 4. [INFO] Starting RabbitMQ container..." + + RABBITMQ_CONTAINER_NAME="freeleaps2-rabbitmq" + RABBITMQ_IMAGE="rabbitmq:latest" + + # If a container with the same name exists, remove it + if docker ps -a --format '{{.Names}}' | grep -q "^${RABBITMQ_CONTAINER_NAME}\$"; then + echo "==> Removing existing RabbitMQ container..." + docker stop "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true + docker rm "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true + fi + + # Pull the RabbitMQ image + echo "==> Pulling RabbitMQ image: ${RABBITMQ_IMAGE}" + if ! docker pull "${RABBITMQ_IMAGE}"; then + echo "ERROR: Failed to pull RabbitMQ image: ${RABBITMQ_IMAGE}" + exit 1 + fi + + # Run the RabbitMQ container mapping port 5672 + echo "==> Starting RabbitMQ container..." + rabbitmq_container_id=$(docker run -d --name "${RABBITMQ_CONTAINER_NAME}" -p 5672:5672 "${RABBITMQ_IMAGE}") + + if [[ -z "${rabbitmq_container_id}" ]]; then + echo "ERROR: Failed to start RabbitMQ container." + exit 1 + fi + echo "RabbitMQ container started successfully: ${rabbitmq_container_id}" + + # Allow RabbitMQ some time to initialize + sleep 20 + + # Check RabbitMQ health via rabbitmqctl + if docker exec "${RABBITMQ_CONTAINER_NAME}" rabbitmqctl status &>/dev/null; then + echo "RabbitMQ health check passed." + else + echo "ERROR: RabbitMQ health check failed." + exit 1 + fi + echo "Step 5. [INFO] Starting RabbitMQ container..." + + + + docker exec -i "$DEVBOX_NAME" bash < Using local components" + # Local components for Freeleaps services (devsvc, notification, content, central_storage, authentication) cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.env # Online endpoint info export MONGODB_NAME=freeleaps2 @@ -778,21 +904,24 @@ EOFinner else cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.env # Online endpoint info - export MONGODB_NAME=freeleaps2-mongo - export MONGODB_URI=mongodb://\$DEFAULT_IP:27017/ - export SITE_ACCESS_PORT=80 - export FREELEAPS_ENV=dev - export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd - export STRIPE_WEBHOOK_SECRET= - export SITE_URL_ROOT=http://localhost/ - export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" + export MONGODB_NAME=freeleaps2 + export MONGODB_PORT=27017 + export MONGODB_URI='mongodb+srv://jetli:8IHKx6dZK8BfugGp@freeleaps2.hanbj.mongodb.net/' + export RABBITMQ_HOSTNAME=\$DEFAULT_IP export RABBITMQ_HOST=\$DEFAULT_IP export RABBITMQ_PORT=5672 - export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ - export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ - export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://localhost:8005/api/central_storage/ - export JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b - export FREELEAPS_AUTHENTICATION_ENDPOINT=http://localhost:8004/api/auth/ + 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://ip: 52.149.3.85:8007/api/devsvc/ + export FREELEAPS_CONTENT_ENDPOINT=http://52.149.35.244:8013/api/content/ + export FREELEAPS_PAYMENT_ENDPOINT=http://52.149.35.244:8006/api/payment/ + 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 EOFinner fi @@ -804,161 +933,6 @@ cp ~/freeleaps_home/freeleaps/.dev.env ~/freeleaps_home/freeleaps/.env source ~/freeleaps_home/freeleaps/.dev.env source ~/freeleaps_home/freeleaps/apps/.env -echo "Step 3. [INFO] Checking Docker installation..." - - # 1. Check docker CLI installted - if ! command -v docker >/dev/null 2>&1; then - echo "[ERROR] Docker CLI is not installed." - exit 1 - fi - - echo "[INFO] Docker CLI is installed. Checking daemon..." - - # 2. Check if Docker Daemon running - if docker info >/dev/null 2>&1; then - echo '============================================' - echo "[OK] Docker daemon is running." - echo '============================================' - else - - if grep -qi microsoft /proc/version; then - echo "[INFO] Detected WSL environment. Verifying Docker socket..." - if [ -S /var/run/docker.sock ]; then - echo "[OK] Docker socket found." - else - # WSL 2 with Docker Desktop - 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." - else - echo "[ERROR] Failed to start Docker using systemctl." - exit 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." - else - echo "[ERROR] Failed to start Docker using service command." - exit 1 - fi - else - echo "[ERROR] Neither systemctl nor service command found. Please start Docker manually." - exit 1 - fi - fi - - fi - fi - fi - - - -# 3.Create and start MongoDB container -echo "Step 4=2. [INFO] Starting MongoDB container..." - -MONGO_CONTAINER_NAME="freeleaps2-mongo" -MONGO_IMAGE="mongo:latest" - -# if a container with the same name exists, remove it -if docker ps -a --format '{{.Names}}' | grep -q "^\${MONGO_CONTAINER_NAME}\$"; then - echo "==> Removing existing MongoDB container..." - docker stop "\$MONGO_CONTAINER_NAME" &>/dev/null || true - docker rm "\$MONGO_CONTAINER_NAME" &>/dev/null || true -fi - -echo "==> Pulling MongoDB image: \$MONGO_IMAGE" -if ! docker pull "\$MONGO_IMAGE"; then - echo "ERROR: Failed to pull MongoDB image: \$MONGO_IMAGE" - exit 1 -fi - -echo "==> Starting MongoDB container..." -mongo_container_id=\$(docker run -d --name "\$MONGO_CONTAINER_NAME" -p 27017:27017 "\$MONGO_IMAGE") -if [[ -z "\$mongo_container_id" ]]; then - echo "ERROR: Failed to start MongoDB container." - exit 1 -fi -echo "MongoDB container started successfully: \$mongo_container_id" - -sleep 10 - - -echo '============================================' - -MAX_ATTEMPTS=10 -ATTEMPT=0 -while [ \$ATTEMPT -lt \$MAX_ATTEMPTS ]; do - if docker exec "\$MONGO_CONTAINER_NAME" mongosh --eval "db.adminCommand('ping')" 2>/dev/null | grep -q '{ ok: 1 }'; then - echo "MongoDB health check passed." - break - fi - echo "Waiting for MongoDB to be ready... (Attempt \$((ATTEMPT+1)))" - sleep 10 - ATTEMPT=\$((ATTEMPT+1)) -done - -if [ \$ATTEMPT -eq \$MAX_ATTEMPTS ]; then - echo "ERROR: MongoDB health check failed." - exit 1 -fi - -echo "Step 4. [INFO] Starting MongoDB container..." - - -pushd ~/freeleaps_home/freeleaps - -# 4. 安装RABBITMQ_HOST=localhost, RABBITMQ_PORT=5672 docker 容器镜像,并检查是否成功启动 -echo "Step 4. [INFO] Starting RabbitMQ container..." - -RABBITMQ_CONTAINER_NAME="freeleaps2-rabbitmq" -RABBITMQ_IMAGE="rabbitmq:latest" - -# If a container with the same name exists, remove it -if docker ps -a --format '{{.Names}}' | grep -q "^\${RABBITMQ_CONTAINER_NAME}\$"; then - echo "==> Removing existing RabbitMQ container..." - docker stop "\${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true - docker rm "\${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true -fi - -# Pull the RabbitMQ image -echo "==> Pulling RabbitMQ image: \${RABBITMQ_IMAGE}" -if ! docker pull "\${RABBITMQ_IMAGE}"; then - echo "ERROR: Failed to pull RabbitMQ image: \${RABBITMQ_IMAGE}" - exit 1 -fi - -# Run the RabbitMQ container mapping port 5672 -echo "==> Starting RabbitMQ container..." -rabbitmq_container_id=\$(docker run -d --name "\${RABBITMQ_CONTAINER_NAME}" -p 5672:5672 "\${RABBITMQ_IMAGE}") - -if [[ -z "\${rabbitmq_container_id}" ]]; then - echo "ERROR: Failed to start RabbitMQ container." - exit 1 -fi -echo "RabbitMQ container started successfully: \${rabbitmq_container_id}" - -# Allow RabbitMQ some time to initialize -sleep 20 - -# Check RabbitMQ health via rabbitmqctl -if docker exec "\${RABBITMQ_CONTAINER_NAME}" rabbitmqctl status &>/dev/null; then - echo "RabbitMQ health check passed." -else - echo "ERROR: RabbitMQ health check failed." - exit 1 -fi -echo "Step 5. [INFO] Starting RabbitMQ container..." - -echo '============================================' - # Run start_webapi.sh and check if success started echo "Step 4: Run start_webapi.sh and check if success started" @@ -976,34 +950,18 @@ sudo apt update sudo apt install python3.10 python3.10-venv -y -echo '============================================1' -dpkg -l | grep python3.10-venv - -echo '============================================1' # make sore python3.10 is installed if ! command -v python3.10 &>/dev/null; then echo "ERROR: Python3.10 is not installed." exit 1 fi -# Set python3.10 as default python3 -echo "6.1. Set Python3.10 as default Python3" -sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.10 1 -sudo update-alternatives --config python3 -# Check if the ladvei +# Upgrade pip and install virtualenv echo "7. Upgrade pip and install virtualenv" python3.10 -m ensurepip --upgrade python3.10 -m pip install --upgrade pip - -echo '============================================' - -echo 'Current path: ' -pwd - -echo '============================================' - # 8. Create and activate a virtual environment echo "8. Create and activate a virtual environment" python3.10 -m venv venv_t @@ -1012,7 +970,7 @@ sleep 5 # CHeck if the virtual environment is created if [ ! -f "venv_t/bin/activate" ]; then - echo "ERROR: 虚拟环境没有创建成功" + echo "ERROR: The virtual environment cannot be created" exit 1 fi @@ -1040,7 +998,7 @@ pip install -r ~/freeleaps_home/freeleaps/apps/requirements.txt echo '============================================' echo 'Start to run start_webapi.sh' echo '============================================' -./start_webapi.sh > /tmp/webapi.logs 2>&1 & +./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 & WEBAPI_PID=\$! @@ -1050,21 +1008,22 @@ echo '============================================' sleep 30 -MAX_ATTEMPTS=30 # 30次尝试,每次10秒,总等待5分钟 +# 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") - # 判断返回的状态码是否为 200 + # 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 # 等待 5 秒后重试 + sleep 5 # Wait 5 seconds fi done @@ -1091,13 +1050,13 @@ npm install -g pnpm pnpm install npm run build npm run format -# 静默后台启动 npm run dev,将输出重定向到 /dev/null -nohup npm run dev > /dev/null 2>&1 & +# Start the frontend service with nohup in order to keep it running after the SSH session is closed +nohup npm run dev > /home/.devbox/logs/frontend.logs > /dev/null 2>&1 & -# 获取后台启动进程的 PID(可选) +# Save the process ID of the frontend service DEV_PID=$! -echo "npm run dev 已启动,进程号: \$DEV_PID" +echo "npm run dev has been started with PID: \$DEV_PID" echo '============================================' @@ -1135,7 +1094,7 @@ EOF # ------------------------------------------------------------------- - # 10. Final + # 10. Final notification # ------------------------------------------------------------------- echo echo "===========================================================" @@ -1143,8 +1102,8 @@ EOF echo " DevBox container ID: $container_id" [[ -f "${WORKING_HOME}/.devsvc-instance" ]] && echo " devsvc container ID: $(cat "${WORKING_HOME}/.devsvc-instance")" echo " Repository cloned to: $repo_dir" - echo " Back-end logs: $WORKING_HOME/logs/back-end.logs" - echo " Front-end logs: $WORKING_HOME/logs/front-end.logs" + echo " Backend logs: $WORKING_HOME/logs/backend.logs" + echo " Frontend logs: $WORKING_HOME/logs/frontend.logs" echo "===========================================================" echo From b59c43e5fb5e1222a01f54d070bf449b30e7367d Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Sun, 9 Feb 2025 13:29:18 +0800 Subject: [PATCH 05/32] Update for fixing local component startup command and configuration --- devbox/devbox.local/devbox | 314 ++++++++++++++++++++----------------- 1 file changed, 173 insertions(+), 141 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index e8db385..f8b6cbe 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -153,7 +153,7 @@ devbox_init_usage() { echo # :flag.usage use local component - printf " %s\n" "--use-local-component IS_USE_LOCAL_COMPONENT" + printf " %s\n" "--use-local-component USE_LOCAL_COMPONENT" printf " Check if use local component or use online dev environment. (Default: false, use online service) (Optional)\n" echo @@ -173,19 +173,19 @@ devbox_init_usage() { printf " %s\n" "Default: latest" echo - # :flag.usage notification image repo - printf " %s\n" "--notification-image-repo NOTIFICATION_IMAGE_REPO" - printf " Specifies the repository for notification component. (Optional)\n" + # :flag.usage payment image repo + printf " %s\n" "--payment-image-repo PAYMENT_IMAGE_REPO" + printf " Specifies the repository for payment component. (Optional)\n" echo - # :flag.usage notification image name - printf " %s\n" "--notification-image-name NOTIFICATION_IMAGE_NAME" - printf " Specifies the image name for notification component. (Optional)\n" + # :flag.usage payment image name + printf " %s\n" "--payment-image-name PAYMENT_IMAGE_NAME" + printf " Specifies the image name for payment component. (Optional)\n" echo - # :flag.usage notification image tag - printf " %s\n" "--notification-image-tag NOTIFICATION_IMAGE_TAG" - printf " Specifies the image tag for notification component. (Optional, default=latest)\n" + # :flag.usage payment image tag + printf " %s\n" "--payment-image-tag PAYMENT_IMAGE_TAG" + printf " Specifies the image tag for payment component. (Optional, default=latest)\n" printf " %s\n" "Default: latest" echo @@ -436,12 +436,6 @@ devbox_init_command() { # src/init_command.sh #!/usr/bin/env bash - # - # File: src/init_command.sh - # This file implements the `devbox init` command logic. - - # Make sure the function name is `devbox_init_command`. - echo "==> [INIT] Starting DevBox environment initialization..." echo @@ -468,9 +462,9 @@ devbox_init_command() { local DEVSVC_IMAGE="$args_devsvc_image_image" # --devsvc-image-image local DEVSVC_TAG="$args_devsvc_image_tag" # --devsvc-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 PAYMENT_REPO="$args_payment_image_repo" # --payment-image-repo + local PAYMENT_IMAGE="$args_payment_image_image" # --payment-image-image + local PAYMENT_TAG="$args_payment_image_tag" # --payment-image-tag local CONTENT_REPO="$args_content_image_repo" # --content-image-repo local CONTENT_IMAGE="$args_content_image_image" # --content-image-image @@ -498,13 +492,13 @@ devbox_init_command() { local WORKING_HOME="${args['--working-home']:-${WORKING_HOME:-${HOME}/.devbox}}" local FREELEAPS_USERNAME="${args['--freeleaps-username']}" local FREELEAPS_PASSWORD="${args['--freeleaps-password']}" - local USE_LOCAL_COMPONENT="${args['--use-local-component']:-false}" + local USE_LOCAL_COMPONENT="${args['--use-local-component']}" local DEVSVC_REPO="${args['--devsvc-image-repo']}" local DEVSVC_IMAGE="${args['--devsvc-image-name']}" local DEVSVC_TAG="${args['--devsvc-image-tag']}" - local NOTIFICATION_REPO="${args['--notification-image-repo']}" - local NOTIFICATION_IMAGE="${args['--notification-image-name']}" - local NOTIFICATION_TAG="${args['--notification-image-tag']}" + local PAYMENT_REPO="${args['--payment-image-repo']}" + local PAYMENT_IMAGE="${args['--payment-image-name']}" + local PAYMENT_TAG="${args['--payment-image-tag']}" local CONTENT_REPO="${args['--content-image-repo']}" local CONTENT_IMAGE="${args['--content-image-name']}" local CONTENT_TAG="${args['--content-image-tag']}" @@ -518,14 +512,20 @@ devbox_init_command() { local FORCE_INIT="${args['--force']}" local is_pull_all_components=true + local components=("devsvc" "payment" "content" "central_storage" "authentication") + echo "==> Checking parameters..." for component in "${components[@]}"; do - if [[ -z "${args["${component}_image_repo"]}" ]]; then + echo "==> Checking ${component} image repo...value: ${args["${component}_image_repo"]}" + # if ${args["${component}_image_repo"]} is not empty, then set is_pull_all_components to false + if [[ -n "${args["${component}_image_repo"]}" ]]; then is_pull_all_components=false break fi done + echo "==> is_pull_all_components: $is_pull_all_components" + echo " ===================================================== " echo "Parameters:" echo " OS = $OS" @@ -544,9 +544,9 @@ devbox_init_command() { echo " DEVSVC_REPO = $DEVSVC_REPO" echo " DEVSVC_IMAGE = $DEVSVC_IMAGE" echo " DEVSVC_TAG = $DEVSVC_TAG" - echo " NOTIFICATION_REPO = $NOTIFICATION_REPO" - echo " NOTIFICATION_IMAGE= $NOTIFICATION_IMAGE" - echo " NOTIFICATION_TAG = $NOTIFICATION_TAG" + echo " PAYMENT_REPO = $PAYMENT_REPO" + echo " PAYMENT_IMAGE= $PAYMENT_IMAGE" + echo " PAYMENT_TAG = $PAYMENT_TAG" echo " CONTENT_REPO = $CONTENT_REPO" echo " CONTENT_IMAGE = $CONTENT_IMAGE" echo " CONTENT_TAG = $CONTENT_TAG" @@ -573,6 +573,24 @@ devbox_init_command() { 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 + + echo "==> Detected OS: $OS, ARCH: $ARCH" + + # 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 # ------------------------------------------------------------------- @@ -630,7 +648,7 @@ devbox_init_command() { # ... # ------------------------------------------------------------------- - # 5. pull and start DevBox container + # 5.1 pull and start DevBox container # ------------------------------------------------------------------- local devbox_full_image="${DEVBOX_REPO}/${DEVBOX_IMAGE}:${DEVBOX_TAG}" echo "==> Pulling DevBox image: $devbox_full_image" @@ -682,23 +700,47 @@ echo "==> [INIT] Starting Freeleaps services... $USE_LOCAL_COMPONENT" if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then echo ' ===> Using local components for Freeleaps services.' - # Local components for Freeleaps services (devsvc, notification, content, central_storage, authentication) + + # Define local components ports dictionary + declare -A local_components_ports + local_components_ports["devsvc"]="8007" + local_components_ports["payment"]="8006" + local_components_ports["content"]="8013" + local_components_ports["central_storage"]="8005" + local_components_ports["authentication"]="8004" + # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) for component in "${components[@]}"; do - local COMPONENT_REPO="${component^^}_REPO" - COMPONENT_REPO="${COMPONENT_REPO//-/_}" - local COMPONENT_IMAGE="${component^^}_IMAGE" - COMPONENT_IMAGE="${COMPONENT_IMAGE//-/_}" - local COMPONENT_TAG="${component^^}_TAG" - COMPONENT_TAG="${COMPONENT_TAG//-/_}" + repo_var="${component^^}_REPO" + image_var="${component^^}_IMAGE" + tag_var="${component^^}_TAG" + + # 使用间接展开获取变量的值 + COMPONENT_REPO="${!repo_var}" + COMPONENT_IMAGE="${!image_var}" + COMPONENT_TAG="${!tag_var}" + + # 调试输出,检查变量是否正确 + echo "Component: $component" + echo " Repo: $COMPONENT_REPO" + echo " Image: $COMPONENT_IMAGE" + echo " Tag: $COMPONENT_TAG" # check if is_pull_all_components is false and component repo and component image parameter not empty if [[ "$is_pull_all_components" == false && -n "${!COMPONENT_REPO}" && -n "${!COMPONENT_IMAGE}" ]]; then + echo "==> Using local components for Freeleaps services. For $component, COMPONENT_REPO - '$COMPONENT_REPO', COMPONENT_IMAGE - '$COMPONENT_IMAGE'" continue fi - # Pull the component image - local component_full_image="${!COMPONENT_REPO}/${!COMPONENT_IMAGE}:${!COMPONENT_TAG}" + component_full_image="" + + if [[ "$is_pull_all_components" == true ]]; then + component_full_image="docker.io/freeleaps/$component:$arch_tag" + else + component_full_image="${!COMPONENT_REPO}/${!COMPONENT_IMAGE}:${!COMPONENT_TAG}" + fi + + echo "==> Pulling ${component} image: $component_full_image" if ! docker pull "$component_full_image"; then echo "WARNING: Failed to pull ${component} image: $component_full_image, please Check the image and specify it to initialize the component." @@ -715,19 +757,27 @@ if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then fi fi - echo "==> Creating and starting ${component} container..." + + + echo "==> Creating and starting ${component} container... ${local_components_ports[$component]}" local component_container_id component_container_id="$( docker run -d \ --name "$component" \ --link "$DEVBOX_NAME" \ + -p "${local_components_ports[$component]}:${local_components_ports[$component]}" \ -v /var/run/docker.sock:/var/run/docker.sock \ - "$component_full_image" 2>/dev/null + -e SERVICE_API_ACCESS_PORT=${local_components_ports[$component]} \ + -e SERVICE_API_ACCESS_HOST=0.0.0.0 \ + "$component_full_image" \ + uvicorn webapi.main:app --reload --port ${local_components_ports[$component]} --host 0.0.0.0 2>/dev/null )" if [[ -z "$component_container_id" ]]; then echo "WARNING: Failed to create ${component} container. please Check the image and specify it to initialize the component." fi - echo "$component_container_id" > "/home/.devbox/.${component}-instance" + + echo "$component_container_id" > "$WORKING_HOME/.${component}-instance" + echo "${component} container created: $component_container_id" done @@ -761,7 +811,6 @@ if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then sleep 10 - echo '============================================' MAX_ATTEMPTS=10 @@ -783,15 +832,10 @@ if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then echo "Step 4. [INFO] Starting MongoDB container..." -else - echo '============================================' - echo ' ===> Using online components for Freeleaps services.' - echo '============================================' -fi # 4. Pull and start RabbitMQ container echo "Step 4. [INFO] Starting RabbitMQ container..." - RABBITMQ_CONTAINER_NAME="freeleaps2-rabbitmq" + RABBITMQ_CONTAINER_NAME="freeleaps2" RABBITMQ_IMAGE="rabbitmq:latest" # If a container with the same name exists, remove it @@ -829,93 +873,82 @@ fi exit 1 fi echo "Step 5. [INFO] Starting RabbitMQ container..." +else + echo '============================================' + echo ' ===> Using online components for Freeleaps services.' + echo '============================================' +fi +pushd $WORKING_HOME +# 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" + FRONTEND_GIT_URL="https://$FREELEAPS_USERNAME:$FREELEAPS_PASSWORD@freeleaps.com:3443/products/freeleaps.git" + git clone --depth 5 $FRONTEND_GIT_URL +else + pushd $WORKING_HOME/freeleaps + echo "Git pulling freeleaps.com:3443/products/freeleaps.git" + git pull +fi - +# Run banckend service and frontend service in the container docker exec -i "$DEVBOX_NAME" bash < Using local components" - - # Local components for Freeleaps services (devsvc, notification, content, central_storage, authentication) - cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.env + # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) + cat << 'EOFinner' > /home/.devbox/freeleaps/apps/.env # Online endpoint info export MONGODB_NAME=freeleaps2 export MONGODB_URI=mongodb://\$DEFAULT_IP:27017/ - export SITE_ACCESS_PORT=80 - export FREELEAPS_ENV=dev - export STRIPE_API_KEY=sk_test_51Ogsw5B0IyqaSJBrwczlr820jnmvA1qQQGoLZ2XxOsIzikpmXo4pRLjw4XVMTEBR8DdVTYySiAv1XX53Zv5xqynF00GfMqttFd - export STRIPE_WEBHOOK_SECRET= - export SITE_URL_ROOT=http://localhost/ - export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" - export RABBITMQ_HOST=\$DEFAULT_IP - export RABBITMQ_PORT=5672 - export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ - export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ - export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://localhost:8005/api/central_storage/ - export JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b - export FREELEAPS_AUTHENTICATION_ENDPOINT=http://localhost:8004/api/auth/ - export EMAIL_FROM=freeleaps@freeleaps.com -EOFinner -else - cat << 'EOFinner' > ~/freeleaps_home/freeleaps/.dev.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=\$DEFAULT_IP + export BLOB_STORE_CONNECTION_STR="DefaultEndpointsProtocol=https;AccountName=freeleaps1static;AccountKey=SIk7S3RviJxl1XhGiDZKA3cvzfxNrSbsBMfJ3EbKTsKPeMwhy8FTLpJliRLzQVE6uaSX8giDYw2h+ASt5MmHxQ==;EndpointSuffix=core.windows.net" + export RABBITMQ_HOSTNAME=freeleaps2 export RABBITMQ_HOST=\$DEFAULT_IP 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://\$DEFAULT_IP/ + export FREELEAPS_DEVSVC_ENDPOINT=http://\$DEFAULT_IP:8007/api/devsvc/ + export FREELEAPS_CONTENT_ENDPOINT=http://\$DEFAULT_IP:8013/api/content/ + export FREELEAPS_PAYMENT_ENDPOINT=http://\$DEFAULT_IP:8006/api/payment/ + export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://\$DEFAULT_IP:8005/api/central_storage/ + export FREELEAPS_AUTHENTICATION_ENDPOINT=http://\$DEFAULT_IP: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 +EOFinner +else + 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://ip: 52.149.3.85:8007/api/devsvc/ + export FREELEAPS_DEVSVC_ENDPOINT=http://52.149.3.85:8007/api/devsvc/ export FREELEAPS_CONTENT_ENDPOINT=http://52.149.35.244:8013/api/content/ export FREELEAPS_PAYMENT_ENDPOINT=http://52.149.35.244:8006/api/payment/ export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://52.149.35.244:8005/api/central_storage/ @@ -926,28 +959,31 @@ else EOFinner fi -source ~/freeleaps_home/freeleaps/.dev.env -cp ~/freeleaps_home/freeleaps/.dev.env ~/freeleaps_home/freeleaps/apps/.env +# Effect the environment variables in the current shell -cp ~/freeleaps_home/freeleaps/.dev.env ~/freeleaps_home/freeleaps/.env -source ~/freeleaps_home/freeleaps/.dev.env -source ~/freeleaps_home/freeleaps/apps/.env +source /home/.devbox/freeleaps/apps/.env -# Run start_webapi.sh and check if success started -echo "Step 4: Run start_webapi.sh and check if success started" +# Echo the environment variables +echo "===================================================" +echo "Environment variables:" +echo " MONGODB_NAME=\$MONGODB_NAME" +echo " MONGODB_URI=\$MONGODB_URI" +echo " RABBITMQ_HOST=\$RABBITMQ_HOST" +echo " RABBITMQ_PORT=\$RABBITMQ_PORT" +echo " FREELEAPS_DEVSVC_ENDPOINT=\$FREELEAPS_DEVSVC_ENDPOINT" +echo "===================================================" + +# Ensure /home/.devbox/logs exists +mkdir -p /home/.devbox/logs # Start WebAPI service echo "Starting WebAPI service..." -# Make sure the logs directory exists -mkdir -p ~/freeleaps_home/logs -pushd ~/freeleaps_home/freeleaps/apps -cp ~/freeleaps_home/freeleaps/backend_env.sh ~/freeleaps_home/freeleaps/apps/backend_env.sh - +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 @@ -956,7 +992,6 @@ if ! command -v python3.10 &>/dev/null; then exit 1 fi - # Upgrade pip and install virtualenv echo "7. Upgrade pip and install virtualenv" python3.10 -m ensurepip --upgrade @@ -975,12 +1010,10 @@ if [ ! -f "venv_t/bin/activate" ]; then 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 @@ -993,7 +1026,7 @@ fi echo '============================================' echo ' Install requirements' echo '============================================' -pip install -r ~/freeleaps_home/freeleaps/apps/requirements.txt +pip install -r /home/.devbox/freeleaps/apps/requirements.txt echo '============================================' echo 'Start to run start_webapi.sh' @@ -1036,8 +1069,7 @@ fi echo '============================================' echo ' Start frontend service locally' echo '============================================' -pushd ~/freeleaps_home/freeleaps/frontend - +pushd /home/.devbox/freeleaps/frontend # start the frontend service export VITE_API_URL='http://127.0.0.1:8002' @@ -1357,10 +1389,10 @@ devbox_init_parse_requirements() { # :flag.case --use-local-component) if [[ -n ${2+x} ]]; then - args['--devsvc-image-repo']="$2" + args['--use-local-component']="$2" shift 2 else - printf "%s\n" "--use-local-component requires an argument: --use-local-component IS_USING_LOCAL_COMPONENT" >&2 + printf "%s\n" "--use-local-component requires an argument: --use-local-component USING_LOCAL_COMPONENT" >&2 exit 1 fi ;; @@ -1408,34 +1440,34 @@ devbox_init_parse_requirements() { ;; # :flag.case - --notification-image-repo) + --payment-image-repo) if [[ -n ${2+x} ]]; then - args['--notification-image-repo']="$2" + args['--payment-image-repo']="$2" shift 2 else - printf "%s\n" "--notification-image-repo requires an argument: --notification-image-repo FREELEAPS_NOTIFICATION_IMAGE_REPO" >&2 + printf "%s\n" "--payment-image-repo requires an argument: --payment-image-repo FREELEAPS_PAYMENT_IMAGE_REPO" >&2 exit 1 fi ;; # :flag.case - --notification-image-name) + --payment-image-name) if [[ -n ${2+x} ]]; then - args['--notification-image-name']="$2" + args['--payment-image-name']="$2" shift 2 else - printf "%s\n" "--notification-image-name requires an argument: --notification-image-name FREELEAPS_NOTIFICATION_IMAGE_NAME" >&2 + printf "%s\n" "--payment-image-name requires an argument: --payment-image-name FREELEAPS_PAYMENT_IMAGE_NAME" >&2 exit 1 fi ;; # :flag.case - --notification-image-tag) + --payment-image-tag) if [[ -n ${2+x} ]]; then - args['--notification-image-tag']="$2" + args['--payment-image-tag']="$2" shift 2 else - printf "%s\n" "--notification-image-tag requires an argument: --notification-image-tag FREELEAPS_NOTIFICATION_IMAGE_TAG" >&2 + printf "%s\n" "--payment-image-tag requires an argument: --payment-image-tag FREELEAPS_PAYMENT_IMAGE_TAG" >&2 exit 1 fi ;; @@ -1584,12 +1616,12 @@ devbox_init_parse_requirements() { [[ -n ${args['--devbox-image-name']:-} ]] || args['--devbox-image-name']="devbox_v1" [[ -n ${args['--devbox-image-tag']:-} ]] || args['--devbox-image-tag']="devbox_local" [[ -n ${args['--devsvc-image-tag']:-} ]] || args['--devsvc-image-tag']="latest-linux-amd64" - [[ -n ${args['--notification-image-tag']:-} ]] || args['--notification-image-tag']="latest-linux-amd64" + [[ -n ${args['--payment-image-tag']:-} ]] || args['--payment-image-tag']="latest-linux-amd64" [[ -n ${args['--content-image-tag']:-} ]] || args['--content-image-tag']="latest-linux-amd64" [[ -n ${args['--central_storage-image-tag']:-} ]] || args['--central_storage-image-tag']="latest-linux-amd64" [[ -n ${args['--authentication-image-tag']:-} ]] || args['--authentication-image-tag']="latest-linux-amd64" [[ -n ${args['--working-home']:-} ]] || args['--working-home']="${HOME}/.devbox" - [[ -n ${args['--use-local-component']:-} ]] || args['--use-local-component']="false" + [[ -n ${args['--use-local-component']:-} ]] || args['--use-local-component']="true" } From dd7049128d49a46515ab995c4b4cf43de1052557 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Mon, 10 Feb 2025 02:03:17 +0800 Subject: [PATCH 06/32] Update devbox for full command --- devbox/devbox.local/devbox | 1508 +++++++++++++++++++++++++++++++++--- 1 file changed, 1404 insertions(+), 104 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index f8b6cbe..19ae0dd 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -62,6 +62,7 @@ devbox_usage() { fi } + # :command.usage devbox_init_usage() { if [[ -n $long_usage ]]; then @@ -249,13 +250,214 @@ devbox_init_usage() { # :command.usage_examples printf "%s\n" "Examples:" - printf " devbox init --os=linux --arch=amd64 --freeleaps-username alice\n --freeleaps-password secret\n" + printf " devbox init --os=linux --arch=arm64 --freeleaps-username alice\n --freeleaps-password secret\n" printf " devbox init \ --devbox-container-name custom-devbox \ --devbox-container-port\n 22222 \ --freeleaps-username alice \ --freeleaps-password secret\n" echo fi } +# :command.usage +devbox_deinit_usage() { + if [[ -n $long_usage ]]; then + printf "devbox deinit\n\n" + printf " De-initialize the local development environment based on DevBox container.\n This command will stop and remove all containers, clean up the working \n directory, and reset the environment to the initial state.\n \n Sub-command \`deinit\` uses Docker (or another container runtime) to clean \n up the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Stop and remove all containers.\n 2. Clean up the working directory.\n 3. Reset the environment to the initial state.\n\n" + else + printf "devbox deinit - De-initialize the local development environment based on DevBox container.\n\n" + fi + printf "Alias: d\n" + echo + + printf "%s\n" "Usage:" + printf " devbox deinit [OPTIONS]\n" + printf " devbox deinit --help | -h\n" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + printf "%s\n" "Options:" + + # :command.usage_flags + # :flag.usage + printf " %s\n" "--working-home WORKING_HOME" + printf " Specifies the working home of DevBox CLI. Default is ${HOME}/.devbox.\n" + printf " %s\n" "Default: ${HOME}/.devbox" + echo + + # :flag.usage + printf " %s\n" "--clear-logs CLEAR_LOGS" + printf " Specifies whether clear log files or not. Default is true.\n" + printf " %s\n" "Default: true" + echo + + # :flag.usage + printf " %s\n" "--clear-repo CLEAR_REPO" + printf " Specifies whether delete source repository or not. Default is false.\n" + printf " %s\n" "Default: false" + echo + + # :command.usage_fixed_flags + printf " %s\n" "--help, -h" + printf " Show this help\n" + echo + + # :command.usage_examples + printf "%s\n" "Examples:" + printf " devbox deinit\n" + printf " devbox deinit --working-home=/tmp/devbox --clear-logs=false --clear-repo=true\n" + echo + + fi +} + +# :command.usage +devbox_start_usage() { + if [[ -n $long_usage ]]; then + printf "devbox start\n\n" + printf " Start the local development environment based on DevBox container.\n This command will start all containers, services, and processes that \n are required for Freeleaps development.\n \n Sub-command \`start\` uses Docker (or another container runtime) to start \n the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Start all containers.\n 2. Start all services and processes.\n\n" + else + printf "devbox start - Start the local development environment based on DevBox container.\n\n" + fi + printf "Alias: s\n" + echo + + printf "%s\n" "Usage:" + printf " devbox start [OPTIONS]\n" + printf " devbox start --help | -h\n" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + printf "%s\n" "Options:" + + # :command.usage_flags + # :flag.usage + printf " %s\n" "--component COMPONENT" + printf " Specifies the name of the component to start (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, payment, content, central_storage,\n authentication).\n" + echo + + # :command.usage_fixed_flags + printf " %s\n" "--help, -h" + printf " Show this help\n" + echo + + # :command.usage_examples + printf "%s\n" "Examples:" + printf " devbox start\n" + printf " devbox start --component=backend\n" + echo + + fi +} + +# :command.usage +devbox_stop_usage() { + if [[ -n $long_usage ]]; then + printf "devbox stop\n\n" + printf " Stop the local development environment based on DevBox container.\n This command will stop all containers, services, and processes that \n are required for Freeleaps development.\n \n Sub-command \`stop\` uses Docker (or another container runtime) to stop \n the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Stop all containers.\n 2. Stop all services and processes.\n\n" + else + printf "devbox stop - Stop the local development environment based on DevBox container.\n\n" + fi + printf "Alias: p\n" + echo + + printf "%s\n" "Usage:" + printf " devbox stop [OPTIONS]\n" + printf " devbox stop --help | -h\n" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + printf "%s\n" "Options:" + + # :command.usage_flags + # :flag.usage + printf " %s\n" "--component COMPONENT" + printf " Specifies the name of the component to stop (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, payment, content, central_storage,\n authentication).\n" + echo + + # :command.usage_fixed_flags + printf " %s\n" "--help, -h" + printf " Show this help\n" + echo + + # :command.usage_examples + printf "%s\n" "Examples:" + printf " devbox stop\n" + printf " devbox stop --component=backend\n" + echo + + fi +} + +# :command.usage +devbox_status_usage() { + if [[ -n $long_usage ]]; then + printf "devbox status\n\n" + printf " Display the status of the local development environment based on DevBox\n container.\n This command will show the status of all containers, services, and processes \n that are required for Freeleaps development.\n \n Sub-command \`status\` uses Docker (or another container runtime) to show \n the status of the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Show the status of all containers.\n 2. Show the status of all services and processes.\n\n" + else + printf "devbox status - Display the status of the local development environment based on DevBox container.\n\n" + fi + printf "Alias: t\n" + echo + + printf "%s\n" "Usage:" + printf " devbox status\n" + printf " devbox status --help | -h\n" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + printf "%s\n" "Options:" + + # :command.usage_fixed_flags + printf " %s\n" "--help, -h" + printf " Show this help\n" + echo + + # :command.usage_examples + printf "%s\n" "Examples:" + printf " devbox status\n" + printf " devbox status --component=backend\n" + echo + + fi +} + +# :command.usage +devbox_restart_usage() { + if [[ -n $long_usage ]]; then + printf "devbox restart\n\n" + printf " Restart the local development environment based on DevBox container.\n This command will restart all containers, services, and processes that \n are required for Freeleaps development.\n \n Sub-command \`restart\` uses Docker (or another container runtime) to restart \n the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Stop all containers.\n 2. Start all containers.\n 3. Stop all services and processes.\n 4. Start all services and processes.\n\n" + else + printf "devbox restart - Restart the local development environment based on DevBox container.\n\n" + fi + printf "Alias: r\n" + echo + + printf "%s\n" "Usage:" + printf " devbox restart\n" + printf " devbox restart --help | -h\n" + echo + + # :command.long_usage + if [[ -n "$long_usage" ]]; then + printf "%s\n" "Options:" + + # :command.usage_fixed_flags + printf " %s\n" "--help, -h" + printf " Show this help\n" + echo + + # :command.usage_examples + printf "%s\n" "Examples:" + printf " devbox restart\n" + printf " devbox restart --component=backend\n" + echo + + fi +} + # :command.normalize_input # :command.normalize_input_function normalize_input() { @@ -430,11 +632,108 @@ check_docker_running() { return 1 } +start_local_mongodb() { + echo "==> Starting MongoDB service..." + echo "Step 3. [INFO] Starting MongoDB container..." + + MONGO_CONTAINER_NAME="freeleaps2-mongo" + MONGO_IMAGE="mongo:latest" + + # if a container with the same name exists, remove it + if docker ps -a --format '{{.Names}}' | grep -q "^${MONGO_CONTAINER_NAME}\$"; then + echo "==> Removing existing MongoDB container..." + docker stop "$MONGO_CONTAINER_NAME" &>/dev/null || true + docker rm "$MONGO_CONTAINER_NAME" &>/dev/null || true + fi + + echo "==> Pulling MongoDB image: $MONGO_IMAGE" + if ! docker pull "$MONGO_IMAGE"; then + echo "ERROR: Failed to pull MongoDB image: $MONGO_IMAGE" + exit 1 + fi + + echo "==> Starting MongoDB container..." + mongo_container_id=$(docker run -d --name "$MONGO_CONTAINER_NAME" -p 27017:27017 "$MONGO_IMAGE") + if [[ -z "$mongo_container_id" ]]; then + echo "ERROR: Failed to start MongoDB container." + exit 1 + fi + echo "MongoDB container started successfully: $mongo_container_id" + + sleep 10 + + MAX_ATTEMPTS=10 + ATTEMPT=0 + while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do + if docker exec "$MONGO_CONTAINER_NAME" mongosh --eval "db.adminCommand('ping')" 2>/dev/null | grep -q '{ ok: 1 }'; then + echo "MongoDB health check passed." + break + fi + echo "Waiting for MongoDB to be ready... (Attempt $((ATTEMPT+1)))" + sleep 10 + ATTEMPT=$((ATTEMPT+1)) + done + + + if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then + echo "ERROR: MongoDB health check failed." + exit 1 + fi + + echo "$mongo_container_id" > "$WORKING_HOME/.mongodb-instance" + echo "==> Completed MongoDB container..." +} + +start_local_rabbitMQ(){ + echo "[INFO] Starting RabbitMQ container..." + + RABBITMQ_CONTAINER_NAME="freeleaps2" + RABBITMQ_IMAGE="rabbitmq:latest" + + # If a container with the same name exists, remove it + if docker ps -a --format '{{.Names}}' | grep -q "^${RABBITMQ_CONTAINER_NAME}\$"; then + echo "==> Removing existing RabbitMQ container..." + docker stop "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true + docker rm "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true + fi + + # Pull the RabbitMQ image + echo "==> Pulling RabbitMQ image: ${RABBITMQ_IMAGE}" + if ! docker pull "${RABBITMQ_IMAGE}"; then + echo "ERROR: Failed to pull RabbitMQ image: ${RABBITMQ_IMAGE}" + exit 1 + fi + + # Run the RabbitMQ container mapping port 5672 + rabbitmq_container_id=$(docker run -d --name "${RABBITMQ_CONTAINER_NAME}" -p 5672:5672 "${RABBITMQ_IMAGE}") + + if [[ -z "${rabbitmq_container_id}" ]]; then + echo "ERROR: Failed to start RabbitMQ container." + exit 1 + fi + + echo "RabbitMQ container started successfully: ${rabbitmq_container_id}" + + # Allow RabbitMQ some time to initialize + sleep 20 + + # Check RabbitMQ health via rabbitmqctl + if docker exec "${RABBITMQ_CONTAINER_NAME}" rabbitmqctl status &>/dev/null; then + echo "RabbitMQ health check passed." + else + echo "ERROR: RabbitMQ health check failed." + exit 1 + fi + + echo "$rabbitmq_container_id" > "$WORKING_HOME/.rabbitmq-instance" + + echo "[INFO] Completed RabbitMQ container..." +} + # :command.command_functions # :command.function devbox_init_command() { - # src/init_command.sh #!/usr/bin/env bash echo "==> [INIT] Starting DevBox environment initialization..." echo @@ -517,7 +816,7 @@ devbox_init_command() { echo "==> Checking parameters..." for component in "${components[@]}"; do echo "==> Checking ${component} image repo...value: ${args["${component}_image_repo"]}" - # if ${args["${component}_image_repo"]} is not empty, then set is_pull_all_components to false + # if any component image repo is provided, then don't pull all components if [[ -n "${args["${component}_image_repo"]}" ]]; then is_pull_all_components=false break @@ -603,9 +902,9 @@ devbox_init_command() { # 3.2 Check disk space local free_space_kb free_space_kb="$(df -Pk "$HOME" | awk 'END{print $4}')" - # 若无法获取或小于 1GB (1048576 KB),报错 - if [[ -z "$free_space_kb" || $free_space_kb -lt 1048576 ]]; then - echo "ERROR: Insufficient disk space (need >1GB)." + # 若无法获取或小于 10GB (10485760 KB),报错 + if [[ -z "$free_space_kb" || $free_space_kb -lt 10485760 ]]; then + echo "ERROR: Insufficient disk space (need >10GB)." exit 1 fi @@ -785,94 +1084,10 @@ if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then # 3.Create and start MongoDB container echo "Step 3. [INFO] Starting MongoDB container..." - MONGO_CONTAINER_NAME="freeleaps2-mongo" - MONGO_IMAGE="mongo:latest" - - # if a container with the same name exists, remove it - if docker ps -a --format '{{.Names}}' | grep -q "^${MONGO_CONTAINER_NAME}\$"; then - echo "==> Removing existing MongoDB container..." - docker stop "$MONGO_CONTAINER_NAME" &>/dev/null || true - docker rm "$MONGO_CONTAINER_NAME" &>/dev/null || true - fi - - echo "==> Pulling MongoDB image: $MONGO_IMAGE" - if ! docker pull "$MONGO_IMAGE"; then - echo "ERROR: Failed to pull MongoDB image: $MONGO_IMAGE" - exit 1 - fi - - echo "==> Starting MongoDB container..." - mongo_container_id=$(docker run -d --name "$MONGO_CONTAINER_NAME" -p 27017:27017 "$MONGO_IMAGE") - if [[ -z "$mongo_container_id" ]]; then - echo "ERROR: Failed to start MongoDB container." - exit 1 - fi - echo "MongoDB container started successfully: $mongo_container_id" - - sleep 10 - - echo '============================================' - - MAX_ATTEMPTS=10 - ATTEMPT=0 - while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do - if docker exec "$MONGO_CONTAINER_NAME" mongosh --eval "db.adminCommand('ping')" 2>/dev/null | grep -q '{ ok: 1 }'; then - echo "MongoDB health check passed." - break - fi - echo "Waiting for MongoDB to be ready... (Attempt $((ATTEMPT+1)))" - sleep 10 - ATTEMPT=$((ATTEMPT+1)) - done - - if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then - echo "ERROR: MongoDB health check failed." - exit 1 - fi - - echo "Step 4. [INFO] Starting MongoDB container..." + start_local_mongodb # 4. Pull and start RabbitMQ container - echo "Step 4. [INFO] Starting RabbitMQ container..." - - RABBITMQ_CONTAINER_NAME="freeleaps2" - RABBITMQ_IMAGE="rabbitmq:latest" - - # If a container with the same name exists, remove it - if docker ps -a --format '{{.Names}}' | grep -q "^${RABBITMQ_CONTAINER_NAME}\$"; then - echo "==> Removing existing RabbitMQ container..." - docker stop "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true - docker rm "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true - fi - - # Pull the RabbitMQ image - echo "==> Pulling RabbitMQ image: ${RABBITMQ_IMAGE}" - if ! docker pull "${RABBITMQ_IMAGE}"; then - echo "ERROR: Failed to pull RabbitMQ image: ${RABBITMQ_IMAGE}" - exit 1 - fi - - # Run the RabbitMQ container mapping port 5672 - echo "==> Starting RabbitMQ container..." - rabbitmq_container_id=$(docker run -d --name "${RABBITMQ_CONTAINER_NAME}" -p 5672:5672 "${RABBITMQ_IMAGE}") - - if [[ -z "${rabbitmq_container_id}" ]]; then - echo "ERROR: Failed to start RabbitMQ container." - exit 1 - fi - echo "RabbitMQ container started successfully: ${rabbitmq_container_id}" - - # Allow RabbitMQ some time to initialize - sleep 20 - - # Check RabbitMQ health via rabbitmqctl - if docker exec "${RABBITMQ_CONTAINER_NAME}" rabbitmqctl status &>/dev/null; then - echo "RabbitMQ health check passed." - else - echo "ERROR: RabbitMQ health check failed." - exit 1 - fi - echo "Step 5. [INFO] Starting RabbitMQ container..." + start_local_rabbitMQ else echo '============================================' echo ' ===> Using online components for Freeleaps services.' @@ -1032,8 +1247,10 @@ echo '============================================' echo 'Start to run start_webapi.sh' echo '============================================' ./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 & -WEBAPI_PID=\$! +BACKEND_PID=\$! +# Save BACKEND_PID to a file \${WORKING_HOME}/.frontend.pid: Stores the process id of frontend process. +echo "\$BACKEND_PID" > /home/.devbox/.backend.pid echo '============================================' echo 'Check if the WebAPI service started successfully' @@ -1084,17 +1301,17 @@ npm run build npm run format # Start the frontend service with nohup in order to keep it running after the SSH session is closed nohup npm run dev > /home/.devbox/logs/frontend.logs > /dev/null 2>&1 & - # Save the process ID of the frontend service -DEV_PID=$! +FRONTEND_PID=$! -echo "npm run dev has been started with PID: \$DEV_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 120 +sleep 30 # 30 attempts, 10 seconds each, total wait time 5 minutes MAX_ATTEMPTS=30 @@ -1132,15 +1349,760 @@ EOF echo "===========================================================" echo "DevBox init completed successfully!" echo " DevBox container ID: $container_id" - [[ -f "${WORKING_HOME}/.devsvc-instance" ]] && echo " devsvc container ID: $(cat "${WORKING_HOME}/.devsvc-instance")" + [[ -f "${WORKING_HOME}/.devbox-instance" ]] && echo " devbox container ID: $(cat "${WORKING_HOME}/.devbox-instance")" echo " Repository cloned to: $repo_dir" echo " Backend logs: $WORKING_HOME/logs/backend.logs" echo " Frontend logs: $WORKING_HOME/logs/frontend.logs" + 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="${args['--working-home']:-${HOME}/.devbox}" + local CLEAR_LOGS="${args['--clear-logs']:-false}" + local CLEAR_REPO="${args['--clear-repo']:-false}" + local FORCE="${args['--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..." + + # Clear the DevBox container logs + if [[ "$CLEAR_LOGS" == "true" ]]; then + echo "==> Clearing logs in $WORKING_HOME/logs..." + rm -rf "$WORKING_HOME/logs"/* 2>/dev/null || true + 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" + rm -rf "$WORKING_HOME/freeleaps" + else + echo "==> Skipping repository deletion." + fi + + # 停止并删除容器等 + 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" + fi + + echo "==> DevBox deinitialization completed." +} + +# :command.function +devbox_start_command() { + + local COMPONENT="${args['--component']}" + local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" + + # 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") + + echo "==> Starting DevBox services..." + + # 检查 DevBox 容器的状态 + if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then + echo "==> DevBox container is not running, starting container..." + # 启动容器 + if ! docker start "${devbox_container_id}"; then + echo "ERROR: Failed to start DevBox container." + exit 1 + fi + echo "==> DevBox container started successfully." + else + echo "==> DevBox container is already running." + fi + + # 如果未指定组件,启动所有组件 + if [[ -z "$COMPONENT" ]]; then + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") + else + COMPONENTS=("$COMPONENT") + fi + + # 启动指定的组件 + for comp in "${COMPONENTS[@]}"; do + case "$comp" in + "mongodb") + echo "==> Starting MongoDB..." + # Check if MongoDB container file path + local mongodb_container_id_file_path="${WORKING_HOME}/.mongodb-instance" + if [[ ! -f "$mongodb_container_id_file_path" ]]; then + echo "ERROR: MongoDB container is not running. Please run 'devbox init' first." + else + local mongodb_container_id=$(cat "$mongodb_container_id_file_path") + if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${mongodb_container_id}\$"; then + echo "==> MongoDB container is not running, starting container..." + # 启动容器 + if ! docker start "${mongodb_container_id}"; then + echo "ERROR: Failed to start MongoDB container." + exit 1 + fi + echo "==> MongoDB container started successfully." + else + echo "==> MongoDB container is already running." + fi + fi + ;; + "rabbitmq") + echo "==> Starting RabbitMQ..." + # Check if RabbitMQ container file path + local rabbitmq_container_id_file_path="${WORKING_HOME}/.rabbitmq-instance" + if [[ ! -f "$rabbitmq_container_id_file_path" ]]; then + echo "ERROR: RabbitMQ container is not running. Please run 'devbox init' first." + else + local rabbitmq_container_id=$(cat "$rabbitmq_container_id_file_path") + if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${rabbitmq_container_id}\$"; then + echo "==> RabbitMQ container is not running, starting container..." + # 启动容器 + if ! docker start "${rabbitmq_container_id}"; then + echo "ERROR: Failed to start RabbitMQ container." + exit 1 + fi + echo "==> RabbitMQ container started successfully." + else + echo "==> RabbitMQ container is already running." + fi + fi + ;; + "backend") + echo "==> Starting backend service..." + # start the backend service + docker exec -i "$devbox_container_id" bash <<'EOF' +# Start the backend service +echo "Starting backend service..." + +# Check if /home/.devbox/.backend.pid exists, if not exists, ask for executing devbox init first +if [ ! -f /home/.devbox/.backend.pid ]; then + echo "ERROR: Backend service is not running. Please run 'devbox init' first." + exit 1 +fi + +backend_pid=\$(cat /home/.devbox/.backend.pid) + +if ps -p "\$backend_pid" > /dev/null; then + echo "Backend service is already running." +else + # Remove the .backend.pid file + rm -f /home/.devbox/.backend.pid + + echo '============================================' + echo 'Start to run start_webapi.sh' + echo '============================================' + pushd /home/.devbox/freeleaps/apps + ./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 & + BACKEND_PID=\$! + + # Save BACKEND_PID to a file \${WORKING_HOME}/.frontend.pid: Stores the process id of frontend 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 + + # Check if \$DEVBOX_BACKEND_PORT exists + if [ -z "\$DEVBOX_BACKEND_PORT" ]; then + echo "ERROR: DEVBOX_BACKEND_PORT is not set." + export DEVBOX_BACKEND_PORT=8002 + fi + + 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 +fi +EOF + + ;; + "frontend") + echo "==> Starting frontend service..." + # Start the frontend service + docker exec -i "$devbox_container_id" bash <<'EOF' + # Start the frontend service + echo "Starting frontend service..." + # Check if /home/.devbox/.frontend.pid exists + if [ -f /home/.devbox/.frontend.pid ]; then + frontend_pid=\$(cat /home/.devbox/.frontend.pid) + + # Check if the frontend service is already running + if ps -p "\$frontend_pid" > /dev/null; then + echo "Frontend service is already running." + exit 0 + else + # Remove the frontend.pid file + rm -f /home/.devbox/.frontend.pid + fi + fi + + echo '============================================' + echo ' Start frontend service locally' + echo '============================================' + pushd /home/.devbox/freeleaps/frontend + + # Check if npm is installed and pnpm is installed and is npm run dev result is generated + if ! command -v npm &>/dev/null; then + echo "ERROR: npm is not installed." + exit 1 + fi + + if ! command -v pnpm &>/dev/null; then + echo "ERROR: pnpm is not installed." + exit 1 + fi + + if [ ! -f "package.json" ]; then + echo "ERROR: package.json not found." + exit 1 + fi + + # 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 > /dev/null 2>&1 & + FRONTEND_PID=$! + + echo "npm run dev has been started with PID: \$FRONTEND_PID" + echo "\$FRONTEND_PID" > /home/.devbox/.frontend.pid + + # Wait for the frontend service to start + sleep 30 + + # 30 attempts, 10 seconds each, total wait time 5 minutes + MAX_ATTEMPTS=30 + ATTEMPT=0 + + # Check if \$DEVBOX_FRONTEND_PORT exists + if [ -z "\$DEVBOX_FRONTEND_PORT" ]; then + echo "ERROR: DEVBOX_FRONTEND_PORT is not set." + export DEVBOX_FRONTEND_PORT=5173 + fi + + 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 +EOF + ;; + *) + echo "ERROR: Unknown component: $comp" + exit 1 + ;; + esac + done + + # If the frontend component is started, print the URL + if [[ " ${COMPONENTS[@]} " =~ " frontend " ]]; then + echo "==> Frontend started. You can access the Freeleaps web application at: http://localhost:5173" + fi + + echo "==> DevBox services started successfully." +} + +# :command.function +devbox_stop_command() { + echo "==> Stopping DevBox services..." + + local COMPONENT="${args['--component']}" + local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" + + echo "==> Stopping DevBox services..." + + # If the DevBox container is not running, exit + if [[ -z "$COMPONENT" ]]; then + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") + else + COMPONENTS=("$COMPONENT") + fi + + # Stop the specified components + for comp in "${COMPONENTS[@]}"; do + case "$comp" in + "mongodb") + echo "==> Stopping MongoDB..." + if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.mongodb-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> MongoDB container is not running." + fi + ;; + "rabbitmq") + echo "==> Stopping RabbitMQ..." + if [[ -f "${WORKING_HOME}/.rabbitmq-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.rabbitmq-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> RabbitMQ container is not running." + fi + ;; + "backend") + echo "==> Stopping backend service..." + if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.devbox-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> Backend service is not running." + fi + ;; + "frontend") + echo "==> Stopping frontend service..." + if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.devbox-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> Frontend service is not running." + fi + ;; + *) + echo "ERROR: Unknown component: $comp" + exit 1 + ;; + esac + echo "==> $comp service stopped successfully." + done + echo "==> DevBox services stopped successfully." +} + +# :command.function +devbox_status_command() { + + echo "==> Checking DevBox services status..." + local COMPONENT="${args['--component']}" + local WORKING_HOME="${args['--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") + + echo "==> Checking DevBox services status..." + + # 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" "backend" "frontend") + 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 "==> MongoDB container is running." + else + echo "==> MongoDB container is not running." + fi + else + echo "==> 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 "==> RabbitMQ container is running." + else + echo "==> RabbitMQ container is not running." + fi + else + echo "==> RabbitMQ container is not running." + fi + ;; + "backend") + echo "==> Checking backend 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 "==> Backend service is running." + else + echo "==> Backend service is not running." + fi + else + echo "==> Backend service is not running." + fi + ;; + "frontend") + echo "==> Checking frontend 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 "==> Frontend service is running." + else + echo "==> Frontend service is not running." + fi + else + echo "==> Frontend 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="${args['--component']}" + local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" + + # 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") + if ! docker ps -a --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then + echo "ERROR: DevBox container is not running. Please run 'devbox init' first." + rm -f "$devbox_container_id_file_path" + exit 1 + fi + + if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then + echo "==> DevBox container is running." + else + echo "==> DevBox container is not running." + docker start "$devbox_container_id" &>/dev/null || true + sleep 20 + fi + + if [[ -z "$COMPONENT" ]]; then + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") + else + COMPONENTS=("$COMPONENT") + fi + + echo "==> Restarting DevBox services..." + + # Stop the specified components + for comp in "${COMPONENTS[@]}"; do + case "$comp" in + "mongodb") + echo "==> Stopping MongoDB..." + if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.mongodb-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> MongoDB container is not running." + fi + ;; + "rabbitmq") + echo "==> Stopping RabbitMQ..." + if [[ -f "${WORKING_HOME}/.rabbitmq-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.rabbitmq-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> RabbitMQ container is not running." + fi + ;; + "backend") + echo "==> Stopping backend service..." + if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.devbox-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> Backend service is not running." + fi + ;; + "frontend") + echo "==> Stopping frontend service..." + if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.devbox-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> Frontend 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 + "mongodb") + echo "==> Restarting MongoDB..." + if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.mongodb-instance") + docker start "$container_id" &>/dev/null || true + else + echo "==> MongoDB container is not running." + fi + ;; + "rabbitmq") + echo "==> Restarting RabbitMQ..." + if [[ -f "${WORKING_HOME}/.rabbitmq-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.rabbitmq-instance") + docker start "$container_id" &>/dev/null || true + else + echo "==> RabbitMQ container is not running." + fi + ;; + "backend") + echo "==> Restarting backend service..." + if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.devbox-instance") + docker start "$container_id" &>/dev/null || true + + # Start backend service in the container + docker exec -i "$devbox_container_id" bash < /dev/null; then + echo "Backend service is already running." +else + echo '============================================' + echo 'Start to run start_webapi.sh' + echo '============================================' + pushd /home/.devbox/freeleaps/apps + ./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 & + BACKEND_PID=\$! + + # Save BACKEND_PID to a file \${WORKING_HOME}/.frontend.pid: Stores the process id of frontend 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 + + # Check if \$DEVBOX_BACKEND_PORT exists + if [ -z "\$DEVBOX_BACKEND_PORT" ]; then + echo "ERROR: DEVBOX_BACKEND_PORT is not set." + export DEVBOX_BACKEND_PORT=8002 + fi + + 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 +fi +EOF + + else + echo "==> Backend service is not running." + fi + ;; + "frontend") + echo "==> Restarting frontend service..." + if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.devbox-instance") + docker start "$container_id" &>/dev/null || true + + # Start frontend service in the container + docker exec -i "$DEVBOX_NAME" bash < /dev/null; then + echo "Frontend service is already running." + exit 0 + else + # Remove the frontend.pid file + rm -f /home/.devbox/.frontend.pid + fi + fi + + echo '============================================' + echo ' Start frontend service locally' + echo '============================================' + pushd /home/.devbox/freeleaps/frontend + + # Check if npm is installed and pnpm is installed and is npm run dev result is generated + if ! command -v npm &>/dev/null; then + echo "ERROR: npm is not installed." + exit 1 + fi + + if ! command -v pnpm &>/dev/null; then + echo "ERROR: pnpm is not installed." + exit 1 + fi + + if [ ! -f "package.json" ]; then + echo "ERROR: package.json not found." + exit 1 + fi + + # 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 > /dev/null 2>&1 & + FRONTEND_PID=$! + + echo "npm run dev has been started with PID: \$FRONTEND_PID" + echo "\$FRONTEND_PID" > /home/.devbox/.frontend.pid + + # Wait for the frontend service to start + sleep 30 + + # 30 attempts, 10 seconds each, total wait time 5 minutes + MAX_ATTEMPTS=30 + ATTEMPT=0 + + # Check if \$DEVBOX_FRONTEND_PORT exists + if [ -z "\$DEVBOX_FRONTEND_PORT" ]; then + echo "ERROR: DEVBOX_FRONTEND_PORT is not set." + export DEVBOX_FRONTEND_PORT=5173 + fi + + 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 +EOF + else + echo "==> Frontend service is not running." + fi + ;; + *) + echo "ERROR: Unknown component: $comp" + exit 1 + ;; + esac + done + + echo "==> DevBox services restarted successfully." +} + # :command.parse_requirements parse_requirements() { # :command.fixed_flags_filter @@ -1184,6 +2146,41 @@ 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 @@ -1220,6 +2217,7 @@ parse_requirements() { } + # :command.parse_requirements devbox_init_parse_requirements() { # :command.fixed_flags_filter @@ -1615,17 +2613,315 @@ devbox_init_parse_requirements() { [[ -n ${args['--devbox-image-repo']:-} ]] || args['--devbox-image-repo']="docker.io/freeleaps" [[ -n ${args['--devbox-image-name']:-} ]] || args['--devbox-image-name']="devbox_v1" [[ -n ${args['--devbox-image-tag']:-} ]] || args['--devbox-image-tag']="devbox_local" - [[ -n ${args['--devsvc-image-tag']:-} ]] || args['--devsvc-image-tag']="latest-linux-amd64" - [[ -n ${args['--payment-image-tag']:-} ]] || args['--payment-image-tag']="latest-linux-amd64" - [[ -n ${args['--content-image-tag']:-} ]] || args['--content-image-tag']="latest-linux-amd64" - [[ -n ${args['--central_storage-image-tag']:-} ]] || args['--central_storage-image-tag']="latest-linux-amd64" - [[ -n ${args['--authentication-image-tag']:-} ]] || args['--authentication-image-tag']="latest-linux-amd64" + [[ -n ${args['--devsvc-image-tag']:-} ]] || args['--devsvc-image-tag']="latest-linux-arm64" + [[ -n ${args['--payment-image-tag']:-} ]] || args['--payment-image-tag']="latest-linux-arm64" + [[ -n ${args['--content-image-tag']:-} ]] || args['--content-image-tag']="latest-linux-arm64" + [[ -n ${args['--central_storage-image-tag']:-} ]] || args['--central_storage-image-tag']="latest-linux-arm64" + [[ -n ${args['--authentication-image-tag']:-} ]] || args['--authentication-image-tag']="latest-linux-arm64" [[ -n ${args['--working-home']:-} ]] || args['--working-home']="${HOME}/.devbox" [[ -n ${args['--use-local-component']:-} ]] || args['--use-local-component']="true" } + +# :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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--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 + [[ -n ${args['--working-home']:-} ]] || args['--working-home']="${HOME}/.devbox" + [[ -n ${args['--clear-logs']:-} ]] || args['--clear-logs']="true" + [[ -n ${args['--clear-repo']:-} ]] || args['--clear-repo']="false" + +} + +# :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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--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_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) + + # :flag.case_arg + if [[ -n ${2+x} ]]; then + args['--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 + + -?*) + 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 + + -?*) + 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.initialize initialize() { version="1.0.0" @@ -1647,8 +2943,12 @@ run() { 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 "$@" From 59dafb8f872b8fdc3d624f7ba4049e98268fceed Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Mon, 10 Feb 2025 02:09:51 +0800 Subject: [PATCH 07/32] Update for restart command optimize --- devbox/devbox.local/devbox | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 19ae0dd..35b42b9 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1973,10 +1973,11 @@ else # Check if \$DEVBOX_BACKEND_PORT exists if [ -z "\$DEVBOX_BACKEND_PORT" ]; then - echo "ERROR: DEVBOX_BACKEND_PORT is not set." + echo "WARNING: DEVBOX_BACKEND_PORT is not set." export DEVBOX_BACKEND_PORT=8002 fi + echo "DEVBOX_BACKEND_PORT: \$DEVBOX_BACKEND_PORT" echo "Waiting for WebAPI service to become healthy..." while [ \$ATTEMPT -lt \$MAX_ATTEMPTS ]; do From a711c424837cca536a617cb32f33d82b92a6c370 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Mon, 10 Feb 2025 15:53:06 +0800 Subject: [PATCH 08/32] Update for change script to support bash 3 --- devbox/devbox.local/devbox | 491 ++++++++++++++++++++++++------------- 1 file changed, 315 insertions(+), 176 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 35b42b9..8e83d7b 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -3,8 +3,8 @@ # Modifying it manually is not recommended # :wrapper.bash3_bouncer -if [[ "${BASH_VERSINFO:-0}" -lt 4 ]]; then - printf "bash version 4 or higher is required\n" >&2 +if [[ "${BASH_VERSINFO:-0}" -lt 3 ]]; then + printf "bash version 3 or higher is required\n" >&2 exit 1 fi @@ -15,6 +15,40 @@ 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 "devbox - DevBox Command Line Tool\n\n" @@ -492,35 +526,56 @@ normalize_input() { # :command.inspect_args inspect_args() { - if ((${#args[@]})); then - readarray -t sorted_keys < <(printf '%s\n' "${!args[@]}" | sort) - echo args: - for k in "${sorted_keys[@]}"; do - echo "- \${args[$k]} = ${args[$k]}" + # 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 + echo "args: none" fi - if ((${#deps[@]})); then - readarray -t sorted_keys < <(printf '%s\n' "${!deps[@]}" | sort) + # 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 k in "${sorted_keys[@]}"; do - echo "- \${deps[$k]} = ${deps[$k]}" + 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 - if ((${#env_var_names[@]})); then - readarray -t sorted_names < <(printf '%s\n' "${env_var_names[@]}" | sort) + # 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 k in "${sorted_names[@]}"; do - echo "- \$$k = ${!k:-}" + 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..." @@ -730,6 +785,23 @@ start_local_rabbitMQ(){ echo "[INFO] Completed RabbitMQ container..." } +# 定义键和值数组 +local_components_ports_keys=("devsvc" "payment" "content" "central_storage" "authentication") +local_components_ports_values=("8007" "8006" "8013" "8005" "8004") + +# 根据组件名查找对应的端口 +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() { @@ -779,45 +851,46 @@ devbox_init_command() { # --force flag to overwrite existing resources local FORCE_INIT="${args_force}" - local OS="${args['--os']}" - local ARCH="${args['--arch']}" - local DEVBOX_NAME="${args['--devbox-container-name']}" - local DEVBOX_PORT="${args['--devbox-container-port']}" - local DEVBOX_FRONTEND_PORT="${args['--devbox-frontend-port']}" - local DEVBOX_BACKEND_PORT="${args['--devbox-backend-port']}" - local DEVBOX_REPO="${args['--devbox-image-repo']}" - local DEVBOX_IMAGE="${args['--devbox-image-name']}" - local DEVBOX_TAG="${args['--devbox-image-tag']}" - local WORKING_HOME="${args['--working-home']:-${WORKING_HOME:-${HOME}/.devbox}}" - local FREELEAPS_USERNAME="${args['--freeleaps-username']}" - local FREELEAPS_PASSWORD="${args['--freeleaps-password']}" - local USE_LOCAL_COMPONENT="${args['--use-local-component']}" - local DEVSVC_REPO="${args['--devsvc-image-repo']}" - local DEVSVC_IMAGE="${args['--devsvc-image-name']}" - local DEVSVC_TAG="${args['--devsvc-image-tag']}" - local PAYMENT_REPO="${args['--payment-image-repo']}" - local PAYMENT_IMAGE="${args['--payment-image-name']}" - local PAYMENT_TAG="${args['--payment-image-tag']}" - local CONTENT_REPO="${args['--content-image-repo']}" - local CONTENT_IMAGE="${args['--content-image-name']}" - local CONTENT_TAG="${args['--content-image-tag']}" - local CENTRAL_STORAGE_REPO="${args['--central_storage-image-repo']}" - local CENTRAL_STORAGE_IMAGE="${args['--central_storage-image-name']}" - local CENTRAL_STORAGE_TAG="${args['--central_storage-image-tag']}" - local AUTHENTICATION_REPO="${args['--authentication-image-repo']}" - local AUTHENTICATION_IMAGE="${args['--authentication-image-name']}" - local AUTHENTICATION_TAG="${args['--authentication-image-tag']}" - 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 PAYMENT_REPO="$(get_arg '--payment-image-repo')" + local PAYMENT_IMAGE="$(get_arg '--payment-image-name')" + local PAYMENT_TAG="$(get_arg '--payment-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 FORCE_INIT="$(get_arg '--force')" local is_pull_all_components=true local components=("devsvc" "payment" "content" "central_storage" "authentication") echo "==> Checking parameters..." for component in "${components[@]}"; do - echo "==> Checking ${component} image repo...value: ${args["${component}_image_repo"]}" + echo "==> Checking ${component} image repo...value: $(get_arg "--${component}-image-repo")" # if any component image repo is provided, then don't pull all components - if [[ -n "${args["${component}_image_repo"]}" ]]; then + + if [[ -n "$(get_arg "--${component}-image-repo")" ]]; then is_pull_all_components=false break fi @@ -987,9 +1060,10 @@ devbox_init_command() { exit 1 fi - # record container id + # 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 @@ -997,29 +1071,21 @@ devbox_init_command() { echo "==> [INIT] Starting Freeleaps services... $USE_LOCAL_COMPONENT" -if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then +if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then echo ' ===> Using local components for Freeleaps services.' - # Define local components ports dictionary - declare -A local_components_ports - local_components_ports["devsvc"]="8007" - local_components_ports["payment"]="8006" - local_components_ports["content"]="8013" - local_components_ports["central_storage"]="8005" - local_components_ports["authentication"]="8004" - # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) for component in "${components[@]}"; do - repo_var="${component^^}_REPO" - image_var="${component^^}_IMAGE" - tag_var="${component^^}_TAG" + repo_var="$(upper "$component")_REPO" + image_var="$(upper "$component")_IMAGE" + tag_var="$(upper "$component")_TAG" - # 使用间接展开获取变量的值 + + # Get the component's repo, image, and tag COMPONENT_REPO="${!repo_var}" COMPONENT_IMAGE="${!image_var}" COMPONENT_TAG="${!tag_var}" - # 调试输出,检查变量是否正确 echo "Component: $component" echo " Repo: $COMPONENT_REPO" echo " Image: $COMPONENT_IMAGE" @@ -1057,19 +1123,19 @@ if [[ "${USE_LOCAL_COMPONENT,,}" == "true" ]]; then fi - - echo "==> Creating and starting ${component} container... ${local_components_ports[$component]}" + port=$(get_port $component) + echo "==> Creating and starting ${component} container... ${port}" local component_container_id component_container_id="$( docker run -d \ --name "$component" \ --link "$DEVBOX_NAME" \ - -p "${local_components_ports[$component]}:${local_components_ports[$component]}" \ + -p "${port}:${port}" \ -v /var/run/docker.sock:/var/run/docker.sock \ - -e SERVICE_API_ACCESS_PORT=${local_components_ports[$component]} \ + -e SERVICE_API_ACCESS_PORT=${port} \ -e SERVICE_API_ACCESS_HOST=0.0.0.0 \ "$component_full_image" \ - uvicorn webapi.main:app --reload --port ${local_components_ports[$component]} --host 0.0.0.0 2>/dev/null + uvicorn webapi.main:app --reload --port ${port} --host 0.0.0.0 2>/dev/null )" if [[ -z "$component_container_id" ]]; then echo "WARNING: Failed to create ${component} container. please Check the image and specify it to initialize the component." @@ -1123,7 +1189,7 @@ 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,,}" == "true" ]]; then +if [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "true" ]]; then echo "==> Using local components" # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) cat << 'EOFinner' > /home/.devbox/freeleaps/apps/.env @@ -1249,7 +1315,7 @@ echo '============================================' ./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 & BACKEND_PID=\$! -# Save BACKEND_PID to a file \${WORKING_HOME}/.frontend.pid: Stores the process id of frontend process. +# 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 '============================================' @@ -1299,10 +1365,9 @@ npm install -g pnpm pnpm install npm run build npm run format -# Start the frontend service with nohup in order to keep it running after the SSH session is closed -nohup npm run dev > /home/.devbox/logs/frontend.logs > /dev/null 2>&1 & -# Save the process ID of the frontend service -FRONTEND_PID=$! +# 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 @@ -1366,10 +1431,10 @@ devbox_deinit_command() { # src/deinit_command.sh echo "# It contains the implementation for the 'devbox deinit' command." - local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" - local CLEAR_LOGS="${args['--clear-logs']:-false}" - local CLEAR_REPO="${args['--clear-repo']:-false}" - local FORCE="${args['--force']}" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" + local CLEAR_LOGS="$(get_arg '--clear-logs' 'false')" + local CLEAR_REPO="$(get_arg '--clear-repo' 'false')" + local FORCE="$(get_arg '--force')" # print the parameters echo "==> Deinitialization parameters:" @@ -1412,8 +1477,8 @@ devbox_deinit_command() { # :command.function devbox_start_command() { - local COMPONENT="${args['--component']}" - local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" + local COMPONENT="$(get_arg '--component')" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" # Check if the DevBox container is running local devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" @@ -1426,10 +1491,10 @@ devbox_start_command() { echo "==> Starting DevBox services..." - # 检查 DevBox 容器的状态 + # Check if DevBox container is running if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then echo "==> DevBox container is not running, starting container..." - # 启动容器 + # Start the container if ! docker start "${devbox_container_id}"; then echo "ERROR: Failed to start DevBox container." exit 1 @@ -1439,14 +1504,14 @@ devbox_start_command() { echo "==> DevBox container is already running." fi - # 如果未指定组件,启动所有组件 + # If no component is specified, start all components if [[ -z "$COMPONENT" ]]; then COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") else COMPONENTS=("$COMPONENT") fi - # 启动指定的组件 + # Start the specified components for comp in "${COMPONENTS[@]}"; do case "$comp" in "mongodb") @@ -1459,7 +1524,7 @@ devbox_start_command() { local mongodb_container_id=$(cat "$mongodb_container_id_file_path") if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${mongodb_container_id}\$"; then echo "==> MongoDB container is not running, starting container..." - # 启动容器 + # Start the container if ! docker start "${mongodb_container_id}"; then echo "ERROR: Failed to start MongoDB container." exit 1 @@ -1480,7 +1545,7 @@ devbox_start_command() { local rabbitmq_container_id=$(cat "$rabbitmq_container_id_file_path") if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${rabbitmq_container_id}\$"; then echo "==> RabbitMQ container is not running, starting container..." - # 启动容器 + # Start the container if ! docker start "${rabbitmq_container_id}"; then echo "ERROR: Failed to start RabbitMQ container." exit 1 @@ -1494,7 +1559,7 @@ devbox_start_command() { "backend") echo "==> Starting backend service..." # start the backend service - docker exec -i "$devbox_container_id" bash <<'EOF' + docker exec -i "$devbox_container_id" bash < /dev/null; then echo "Backend service is already running." else @@ -1533,6 +1602,7 @@ else ATTEMPT=0 # Check if \$DEVBOX_BACKEND_PORT exists + DEVBOX_BACKEND_PORT=\$(cat /home/.devbox/.devbox-backend-port) if [ -z "\$DEVBOX_BACKEND_PORT" ]; then echo "ERROR: DEVBOX_BACKEND_PORT is not set." export DEVBOX_BACKEND_PORT=8002 @@ -1564,23 +1634,31 @@ EOF "frontend") echo "==> Starting frontend service..." # Start the frontend service - docker exec -i "$devbox_container_id" bash <<'EOF' + docker exec -i "$devbox_container_id" bash < /dev/null; then - echo "Frontend service is already running." - exit 0 - else - # Remove the frontend.pid file - rm -f /home/.devbox/.frontend.pid - fi - fi + frontend_pid=\$(cat /home/.devbox/.frontend.pid) + + # Remove empty spaces and new lines in the frontend_pid + frontend_pid=\$(echo "\$frontend_pid" | tr -d '[:space:]') + + # Check if frontend pid is empty + if [ -z "\$frontend_pid" ]; then + echo "Frontend service is not running. " + else + echo '============================================' + if ps -p "\$frontend_pid" > /dev/null; then + echo "Frontend service is already running." + exit 0 + fi + fi + + # Remove the .frontend.pid file before starting the frontend service + rm -f /home/.devbox/.frontend.pid + fi echo '============================================' echo ' Start frontend service locally' echo '============================================' @@ -1604,25 +1682,27 @@ EOF # 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 > /dev/null 2>&1 & - FRONTEND_PID=$! + + 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 # Wait for the frontend service to start - sleep 30 + sleep 10 # 30 attempts, 10 seconds each, total wait time 5 minutes MAX_ATTEMPTS=30 ATTEMPT=0 - # Check if \$DEVBOX_FRONTEND_PORT exists + DEVBOX_FRONTEND_PORT=\$(cat /home/.devbox/.devbox-frontend-port) + # get DEVBOX_FRONTEND_PORT from environment variables if [ -z "\$DEVBOX_FRONTEND_PORT" ]; then echo "ERROR: DEVBOX_FRONTEND_PORT is not set." export DEVBOX_FRONTEND_PORT=5173 fi - + 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/") @@ -1661,9 +1741,8 @@ EOF # :command.function devbox_stop_command() { echo "==> Stopping DevBox services..." - - local COMPONENT="${args['--component']}" - local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" + local COMPONENT="$(get_arg '--component')" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" echo "==> Stopping DevBox services..." @@ -1731,8 +1810,8 @@ devbox_stop_command() { devbox_status_command() { echo "==> Checking DevBox services status..." - local COMPONENT="${args['--component']}" - local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" + 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 @@ -1830,9 +1909,9 @@ devbox_status_command() { # :command.function devbox_restart_command() { echo "==> Restarting DevBox services..." - local COMPONENT="${args['--component']}" - local WORKING_HOME="${args['--working-home']:-${HOME}/.devbox}" - + local COMPONENT="$(get_arg '--component')" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" + # Check devbox container file path local devbox_container_id_file_path="${WORKING_HOME}/.devbox-instance" if [[ ! -f "$devbox_container_id_file_path" ]]; then @@ -2052,8 +2131,8 @@ EOF # 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 > /dev/null 2>&1 & - FRONTEND_PID=$! + 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 @@ -2250,7 +2329,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--os']="$2" + add_arg '--os' "$2" shift shift else @@ -2264,7 +2343,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--arch']="$2" + add_arg '--arch' "$2" shift shift else @@ -2278,7 +2357,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devbox-container-name']="$2" + add_arg '--devbox-container-name' "$2" shift shift else @@ -2292,7 +2371,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devbox-container-port']="$2" + add_arg '--devbox-container-port' "$2" shift shift else @@ -2306,7 +2385,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devbox-image-repo']="$2" + add_arg '--devbox-image-repo' "$2" shift shift else @@ -2320,7 +2399,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devbox-image-name']="$2" + add_arg '--devbox-image-name' "$2" shift shift else @@ -2334,7 +2413,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devbox-image-tag']="$2" + add_arg '--devbox-image-tag' "$2" shift shift else @@ -2348,7 +2427,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--working-home']="$2" + add_arg '--working-home' "$2" shift shift else @@ -2362,7 +2441,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--freeleaps-username']="$2" + add_arg '--freeleaps-username' "$2" shift shift else @@ -2376,7 +2455,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--freeleaps-password']="$2" + add_arg '--freeleaps-password' "$2" shift shift else @@ -2388,7 +2467,7 @@ devbox_init_parse_requirements() { # :flag.case --use-local-component) if [[ -n ${2+x} ]]; then - args['--use-local-component']="$2" + 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 @@ -2401,7 +2480,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devsvc-image-repo']="$2" + add_arg '--devsvc-image-repo' "$2" shift shift else @@ -2415,7 +2494,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devsvc-image-name']="$2" + add_arg '--devsvc-image-name' "$2" shift shift else @@ -2429,7 +2508,7 @@ devbox_init_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--devsvc-image-tag']="$2" + add_arg '--devsvc-image-tag' "$2" shift shift else @@ -2441,7 +2520,7 @@ devbox_init_parse_requirements() { # :flag.case --payment-image-repo) if [[ -n ${2+x} ]]; then - args['--payment-image-repo']="$2" + add_arg '--payment-image-repo' "$2" shift 2 else printf "%s\n" "--payment-image-repo requires an argument: --payment-image-repo FREELEAPS_PAYMENT_IMAGE_REPO" >&2 @@ -2452,7 +2531,7 @@ devbox_init_parse_requirements() { # :flag.case --payment-image-name) if [[ -n ${2+x} ]]; then - args['--payment-image-name']="$2" + add_arg '--payment-image-name' "$2" shift 2 else printf "%s\n" "--payment-image-name requires an argument: --payment-image-name FREELEAPS_PAYMENT_IMAGE_NAME" >&2 @@ -2463,7 +2542,7 @@ devbox_init_parse_requirements() { # :flag.case --payment-image-tag) if [[ -n ${2+x} ]]; then - args['--payment-image-tag']="$2" + add_arg '--payment-image-tag' "$2" shift 2 else printf "%s\n" "--payment-image-tag requires an argument: --payment-image-tag FREELEAPS_PAYMENT_IMAGE_TAG" >&2 @@ -2474,7 +2553,7 @@ devbox_init_parse_requirements() { # :flag.case --content-image-repo) if [[ -n ${2+x} ]]; then - args['--content-image-repo']="$2" + 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 @@ -2485,7 +2564,7 @@ devbox_init_parse_requirements() { # :flag.case --content-image-name) if [[ -n ${2+x} ]]; then - args['--content-image-name']="$2" + 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 @@ -2496,7 +2575,7 @@ devbox_init_parse_requirements() { # :flag.case --content-image-tag) if [[ -n ${2+x} ]]; then - args['--content-image-tag']="$2" + 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 @@ -2507,7 +2586,7 @@ devbox_init_parse_requirements() { # :flag.case --central_storage-image-repo) if [[ -n ${2+x} ]]; then - args['--central_storage-image-repo']="$2" + 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 @@ -2518,7 +2597,7 @@ devbox_init_parse_requirements() { # :flag.case --central_storage-image-name) if [[ -n ${2+x} ]]; then - args['--central_storage-image-name']="$2" + 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 @@ -2529,7 +2608,7 @@ devbox_init_parse_requirements() { # :flag.case --central_storage-image-tag) if [[ -n ${2+x} ]]; then - args['--central_storage-image-tag']="$2" + 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 @@ -2540,7 +2619,7 @@ devbox_init_parse_requirements() { # :flag.case --authentication-image-repo) if [[ -n ${2+x} ]]; then - args['--authentication-image-repo']="$2" + 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 @@ -2551,7 +2630,7 @@ devbox_init_parse_requirements() { # :flag.case --authentication-image-name) if [[ -n ${2+x} ]]; then - args['--authentication-image-name']="$2" + 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 @@ -2562,7 +2641,7 @@ devbox_init_parse_requirements() { # :flag.case --authentication-image-tag) if [[ -n ${2+x} ]]; then - args['--authentication-image-tag']="$2" + 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 @@ -2573,7 +2652,7 @@ devbox_init_parse_requirements() { --force | -f) # :flag.case_no_arg - args['--force']=1 + add_arg '--force' '1' shift ;; @@ -2594,33 +2673,82 @@ devbox_init_parse_requirements() { done # :command.required_flags_filter - if [[ -z ${args['--freeleaps-username']+x} ]]; then + + if [[ -z "$(get_arg '--freeleaps-username')" ]]; then + printf "missing required flag: --freeleaps-username FREELEAPS_USERNAME\n" >&2 exit 1 fi - if [[ -z ${args['--freeleaps-password']+x} ]]; then + if [[ -z "$(get_arg '--freeleaps-password')" ]]; then printf "missing required flag: --freeleaps-password FREELEAPS_PASSWORD\n" >&2 exit 1 fi # :command.default_assignments - [[ -n ${args['--os']:-} ]] || args['--os']="auto" - [[ -n ${args['--arch']:-} ]] || args['--arch']="auto" - [[ -n ${args['--devbox-container-name']:-} ]] || args['--devbox-container-name']="devbox" - [[ -n ${args['--devbox-container-port']:-} ]] || args['--devbox-container-port']="22222" - [[ -n ${args['--devbox-frontend-port']:-} ]] || args['--devbox-frontend-port']="5173" - [[ -n ${args['--devbox-backend-port']:-} ]] || args['--devbox-backend-port']="8002" - - [[ -n ${args['--devbox-image-repo']:-} ]] || args['--devbox-image-repo']="docker.io/freeleaps" - [[ -n ${args['--devbox-image-name']:-} ]] || args['--devbox-image-name']="devbox_v1" - [[ -n ${args['--devbox-image-tag']:-} ]] || args['--devbox-image-tag']="devbox_local" - [[ -n ${args['--devsvc-image-tag']:-} ]] || args['--devsvc-image-tag']="latest-linux-arm64" - [[ -n ${args['--payment-image-tag']:-} ]] || args['--payment-image-tag']="latest-linux-arm64" - [[ -n ${args['--content-image-tag']:-} ]] || args['--content-image-tag']="latest-linux-arm64" - [[ -n ${args['--central_storage-image-tag']:-} ]] || args['--central_storage-image-tag']="latest-linux-arm64" - [[ -n ${args['--authentication-image-tag']:-} ]] || args['--authentication-image-tag']="latest-linux-arm64" - [[ -n ${args['--working-home']:-} ]] || args['--working-home']="${HOME}/.devbox" - [[ -n ${args['--use-local-component']:-} ]] || args['--use-local-component']="true" + + 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 '--payment-image-tag')" ]; then + add_arg '--payment-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' "true" + fi } @@ -2657,7 +2785,7 @@ devbox_deinit_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--working-home']="$2" + add_arg '--working-home' "$2" shift shift else @@ -2671,7 +2799,7 @@ devbox_deinit_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--clear-logs']="$2" + add_arg '--clear-logs' "$2" shift shift else @@ -2685,7 +2813,7 @@ devbox_deinit_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--clear-repo']="$2" + add_arg '--clear-repo' "$2" shift shift else @@ -2711,9 +2839,15 @@ devbox_deinit_parse_requirements() { done # :command.default_assignments - [[ -n ${args['--working-home']:-} ]] || args['--working-home']="${HOME}/.devbox" - [[ -n ${args['--clear-logs']:-} ]] || args['--clear-logs']="true" - [[ -n ${args['--clear-repo']:-} ]] || args['--clear-repo']="false" + 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' "true" + fi + if [ -z "$(get_arg '--clear-repo')" ]; then + add_arg '--clear-repo' "false" + fi } @@ -2748,7 +2882,7 @@ devbox_start_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--component']="$2" + add_arg '--component' "$2" shift shift else @@ -2806,7 +2940,7 @@ devbox_stop_parse_requirements() { # :flag.case_arg if [[ -n ${2+x} ]]; then - args['--component']="$2" + add_arg '--component' "$2" shift shift else @@ -2923,18 +3057,23 @@ devbox_restart_parse_requirements() { } -# :command.initialize + + initialize() { version="1.0.0" - long_usage='' + long_usage="" set -e - # :command.globals - declare -g -A args=() - declare -g -A deps=() - declare -g -a env_var_names=() - declare -g -a input=() + # 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 From bf5138c3b24bd07b464fbf2086ffde206ca8740b Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Mon, 10 Feb 2025 21:12:44 +0800 Subject: [PATCH 09/32] Update for support bash 3 and make component access local mongodb with environment parameter --- devbox/devbox.local/devbox | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 8e83d7b..2a7d1fb 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1,5 +1,4 @@ #!/usr/bin/env bash -# This script was generated by bashly 1.2.7 (https://bashly.dannyb.co) # Modifying it manually is not recommended # :wrapper.bash3_bouncer @@ -691,7 +690,7 @@ start_local_mongodb() { echo "==> Starting MongoDB service..." echo "Step 3. [INFO] Starting MongoDB container..." - MONGO_CONTAINER_NAME="freeleaps2-mongo" + MONGO_CONTAINER_NAME="freeleaps2-mongodb" MONGO_IMAGE="mongo:latest" # if a container with the same name exists, remove it @@ -1072,6 +1071,13 @@ devbox_init_command() { echo "==> [INIT] Starting Freeleaps services... $USE_LOCAL_COMPONENT" if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then + # 3.Create and start MongoDB container + echo "Step 3. [INFO] Starting MongoDB container..." + + start_local_mongodb + + # 4. Pull and start RabbitMQ container + start_local_rabbitMQ echo ' ===> Using local components for Freeleaps services.' # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) @@ -1122,6 +1128,8 @@ if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then fi fi + DEFAULT_IP=$(docker network inspect bridge | grep -m1 '"Gateway":' | sed -E 's/.*"Gateway": "([^"]+)".*/\1/') + echo "Default gateway IP: $DEFAULT_IP" port=$(get_port $component) echo "==> Creating and starting ${component} container... ${port}" @@ -1134,6 +1142,7 @@ if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then -v /var/run/docker.sock:/var/run/docker.sock \ -e SERVICE_API_ACCESS_PORT=${port} \ -e SERVICE_API_ACCESS_HOST=0.0.0.0 \ + -e MONGODB_URI="mongodb://$DEFAULT_IP:27017" \ "$component_full_image" \ uvicorn webapi.main:app --reload --port ${port} --host 0.0.0.0 2>/dev/null )" @@ -1147,13 +1156,7 @@ if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then done - # 3.Create and start MongoDB container - echo "Step 3. [INFO] Starting MongoDB container..." - - start_local_mongodb - - # 4. Pull and start RabbitMQ container - start_local_rabbitMQ + else echo '============================================' echo ' ===> Using online components for Freeleaps services.' From a3ebe70dac5883101bc3dc7c0c8037a15812fd1d Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Tue, 11 Feb 2025 00:17:43 +0800 Subject: [PATCH 10/32] Update for fix use local component default value change when bash 3 issue fixing --- devbox/devbox.local/devbox | 211 +++++++++++++++++++++---------------- 1 file changed, 121 insertions(+), 90 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 2a7d1fb..42b8317 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -37,6 +37,7 @@ 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]}" @@ -203,7 +204,7 @@ devbox_init_usage() { # :flag.usage devsvc image tag printf " %s\n" "--devsvc-image-tag DEVSVC_IMAGE_TAG" - printf " Specifies the image tag for devsvc component. (Optional, default=latest)\n" + printf " Specifies the image tag for devsvc component. (Optional, default=latest-)\n" printf " %s\n" "Default: latest" echo @@ -688,7 +689,6 @@ check_docker_running() { start_local_mongodb() { echo "==> Starting MongoDB service..." - echo "Step 3. [INFO] Starting MongoDB container..." MONGO_CONTAINER_NAME="freeleaps2-mongodb" MONGO_IMAGE="mongo:latest" @@ -1068,7 +1068,7 @@ devbox_init_command() { # 6. linbwang: pull and start other components # ------------------------------------------------------------------- -echo "==> [INIT] Starting Freeleaps services... $USE_LOCAL_COMPONENT" +echo "==> [INIT] Starting Freeleaps services... Use Local component $USE_LOCAL_COMPONENT" if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then # 3.Create and start MongoDB container @@ -1078,85 +1078,82 @@ if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then # 4. Pull and start RabbitMQ container start_local_rabbitMQ - echo ' ===> Using local components for Freeleaps services.' + + echo ' ===> Using local components for Freeleaps services.' - # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) - for component in "${components[@]}"; do - repo_var="$(upper "$component")_REPO" - image_var="$(upper "$component")_IMAGE" - tag_var="$(upper "$component")_TAG" - + # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) + for component in "${components[@]}"; do + repo_var="$(upper "$component")_REPO" + image_var="$(upper "$component")_IMAGE" + tag_var="$(upper "$component")_TAG" + - # Get the component's repo, image, and tag - COMPONENT_REPO="${!repo_var}" - COMPONENT_IMAGE="${!image_var}" - COMPONENT_TAG="${!tag_var}" - - echo "Component: $component" - echo " Repo: $COMPONENT_REPO" - echo " Image: $COMPONENT_IMAGE" - echo " Tag: $COMPONENT_TAG" + # Get the component's repo, image, and tag + COMPONENT_REPO="${!repo_var}" + COMPONENT_IMAGE="${!image_var}" + COMPONENT_TAG="${!tag_var}" + + echo "Component: $component" + echo " Repo: $COMPONENT_REPO" + echo " Image: $COMPONENT_IMAGE" + echo " Tag: $COMPONENT_TAG" - # check if is_pull_all_components is false and component repo and component image parameter not empty - if [[ "$is_pull_all_components" == false && -n "${!COMPONENT_REPO}" && -n "${!COMPONENT_IMAGE}" ]]; then - echo "==> Using local components for Freeleaps services. For $component, COMPONENT_REPO - '$COMPONENT_REPO', COMPONENT_IMAGE - '$COMPONENT_IMAGE'" - continue + # check if is_pull_all_components is false and component repo and component image parameter not empty + if [[ "$is_pull_all_components" == false && -n "${!COMPONENT_REPO}" && -n "${!COMPONENT_IMAGE}" ]]; then + echo "==> Using local components for Freeleaps services. For $component, COMPONENT_REPO - '$COMPONENT_REPO', COMPONENT_IMAGE - '$COMPONENT_IMAGE'" + continue + fi + + component_full_image="" + + if [[ "$is_pull_all_components" == true ]]; then + component_full_image="docker.io/freeleaps/$component:$arch_tag" + else + component_full_image="${!COMPONENT_REPO}/${!COMPONENT_IMAGE}:${!COMPONENT_TAG}" + fi + + echo "==> Pulling ${component} image: $component_full_image" + if ! docker pull "$component_full_image"; then + echo "WARNING: Failed to pull ${component} image: $component_full_image, please Check the image and specify it to initialize the component." + fi + + # if container with same name exists, remove it + if docker ps -a --format '{{.Names}}' | grep -q "^${component}\$"; then + if [[ -n "$FORCE_INIT" ]]; then + echo "==> Removing existing container named $component ..." + docker stop "$component" &>/dev/null || true + docker rm "$component" &>/dev/null || true + else + echo "WARNING: Container named $component already exists. Use --force to remove it." fi + fi - component_full_image="" + DEFAULT_IP=$(docker network inspect bridge | grep -m1 '"Gateway":' | sed -E 's/.*"Gateway": "([^"]+)".*/\1/') + echo "Default gateway IP: $DEFAULT_IP" - if [[ "$is_pull_all_components" == true ]]; then - component_full_image="docker.io/freeleaps/$component:$arch_tag" - else - component_full_image="${!COMPONENT_REPO}/${!COMPONENT_IMAGE}:${!COMPONENT_TAG}" - fi + port=$(get_port $component) + echo "==> Creating and starting ${component} container... ${port}" + local component_container_id + component_container_id="$( + docker run -d \ + --name "$component" \ + --link "$DEVBOX_NAME" \ + -p "${port}:${port}" \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -e SERVICE_API_ACCESS_PORT=${port} \ + -e SERVICE_API_ACCESS_HOST=0.0.0.0 \ + -e MONGODB_URI="mongodb://$DEFAULT_IP:27017" \ + "$component_full_image" \ + uvicorn webapi.main:app --reload --port ${port} --host 0.0.0.0 2>/dev/null + )" + if [[ -z "$component_container_id" ]]; then + echo "WARNING: Failed to create ${component} container. please Check the image and specify it to initialize the component." + fi + echo "$component_container_id" > "$WORKING_HOME/.${component}-instance" - echo "==> Pulling ${component} image: $component_full_image" - if ! docker pull "$component_full_image"; then - echo "WARNING: Failed to pull ${component} image: $component_full_image, please Check the image and specify it to initialize the component." - fi - - # if container with same name exists, remove it - if docker ps -a --format '{{.Names}}' | grep -q "^${component}\$"; then - if [[ -n "$FORCE_INIT" ]]; then - echo "==> Removing existing container named $component ..." - docker stop "$component" &>/dev/null || true - docker rm "$component" &>/dev/null || true - else - echo "WARNING: Container named $component already exists. Use --force to remove it." - fi - fi - - DEFAULT_IP=$(docker network inspect bridge | grep -m1 '"Gateway":' | sed -E 's/.*"Gateway": "([^"]+)".*/\1/') - echo "Default gateway IP: $DEFAULT_IP" - - port=$(get_port $component) - echo "==> Creating and starting ${component} container... ${port}" - local component_container_id - component_container_id="$( - docker run -d \ - --name "$component" \ - --link "$DEVBOX_NAME" \ - -p "${port}:${port}" \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -e SERVICE_API_ACCESS_PORT=${port} \ - -e SERVICE_API_ACCESS_HOST=0.0.0.0 \ - -e MONGODB_URI="mongodb://$DEFAULT_IP:27017" \ - "$component_full_image" \ - uvicorn webapi.main:app --reload --port ${port} --host 0.0.0.0 2>/dev/null - )" - if [[ -z "$component_container_id" ]]; then - echo "WARNING: Failed to create ${component} container. please Check the image and specify it to initialize the component." - fi - - echo "$component_container_id" > "$WORKING_HOME/.${component}-instance" - - echo "${component} container created: $component_container_id" - done - - - + echo "${component} container created: $component_container_id" + done else echo '============================================' echo ' ===> Using online components for Freeleaps services.' @@ -1172,6 +1169,8 @@ if [ ! -d $WORKING_HOME/freeleaps ]; then git clone --depth 5 $FRONTEND_GIT_URL else pushd $WORKING_HOME/freeleaps + pwd + ls -la echo "Git pulling freeleaps.com:3443/products/freeleaps.git" git pull fi @@ -1247,16 +1246,6 @@ fi source /home/.devbox/freeleaps/apps/.env -# Echo the environment variables -echo "===================================================" -echo "Environment variables:" -echo " MONGODB_NAME=\$MONGODB_NAME" -echo " MONGODB_URI=\$MONGODB_URI" -echo " RABBITMQ_HOST=\$RABBITMQ_HOST" -echo " RABBITMQ_PORT=\$RABBITMQ_PORT" -echo " FREELEAPS_DEVSVC_ENDPOINT=\$FREELEAPS_DEVSVC_ENDPOINT" -echo "===================================================" - # Ensure /home/.devbox/logs exists mkdir -p /home/.devbox/logs @@ -1418,7 +1407,7 @@ EOF echo "DevBox init completed successfully!" echo " DevBox container ID: $container_id" [[ -f "${WORKING_HOME}/.devbox-instance" ]] && echo " devbox container ID: $(cat "${WORKING_HOME}/.devbox-instance")" - echo " Repository cloned to: $repo_dir" + echo " Repository cloned to: $WORKING_HOME/freeleaps" echo " Backend logs: $WORKING_HOME/logs/backend.logs" echo " Frontend logs: $WORKING_HOME/logs/frontend.logs" echo " Backend PID: $WORKING_HOME/.backend.pid" @@ -1435,8 +1424,9 @@ 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' 'false')" - local CLEAR_REPO="$(get_arg '--clear-repo' 'false')" + local CLEAR_LOGS="$(get_arg '--clear-logs')" + local CLEAR_REPO="$(get_arg '--clear-repo')" + local FORCE="$(get_arg '--force')" # print the parameters @@ -1464,7 +1454,7 @@ devbox_deinit_command() { echo "==> Skipping repository deletion." fi - # 停止并删除容器等 + # Stop and remove DevBox container if [[ -f "$WORKING_HOME/.devbox-instance" ]]; then local container_id container_id=$(cat "$WORKING_HOME/.devbox-instance") @@ -1472,8 +1462,49 @@ devbox_deinit_command() { 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 + # 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" "payment" "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 + echo "==> DevBox deinitialization completed." } @@ -2750,7 +2781,7 @@ devbox_init_parse_requirements() { fi if [ -z "$(get_arg '--use-local-component')" ]; then - add_arg '--use-local-component' "true" + add_arg '--use-local-component' "false" fi @@ -2846,7 +2877,7 @@ devbox_deinit_parse_requirements() { add_arg '--working-home' "${HOME}/.devbox" fi if [ -z "$(get_arg '--clear-logs')" ]; then - add_arg '--clear-logs' "true" + add_arg '--clear-logs' "false" fi if [ -z "$(get_arg '--clear-repo')" ]; then add_arg '--clear-repo' "false" From 96a5e7a870735cbcc6c4efd96a7a29a9d439c874 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Tue, 11 Feb 2025 09:45:51 +0800 Subject: [PATCH 11/32] Update for adding mongod --bind_ip_all --- devbox/devbox.local/devbox | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 42b8317..81b27ab 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -707,7 +707,7 @@ start_local_mongodb() { fi echo "==> Starting MongoDB container..." - mongo_container_id=$(docker run -d --name "$MONGO_CONTAINER_NAME" -p 27017:27017 "$MONGO_IMAGE") + mongo_container_id=$(docker run -d --name "$MONGO_CONTAINER_NAME" -p 27017:27017 "$MONGO_IMAGE" mongod --bind_ip_all) if [[ -z "$mongo_container_id" ]]; then echo "ERROR: Failed to start MongoDB container." exit 1 From b363f50ae7e4a524984afcb3d5ce5dad6a796b96 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Tue, 11 Feb 2025 10:15:53 +0800 Subject: [PATCH 12/32] Update for add more component for devbox status command --- devbox/devbox.local/devbox | 122 +++++++++++++++++++++++++++++++------ 1 file changed, 104 insertions(+), 18 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 81b27ab..edbd595 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1855,8 +1855,6 @@ devbox_status_command() { local devbox_container_id=$(cat "${WORKING_HOME}/.devbox-instance") - echo "==> Checking DevBox services status..." - # 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." @@ -1866,7 +1864,7 @@ devbox_status_command() { # If no component is specified, check all components if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1880,57 +1878,145 @@ devbox_status_command() { local container_id container_id=$(cat "${WORKING_HOME}/.mongodb-instance") if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then - echo "==> MongoDB container is running." + echo "[RESULT]: MongoDB container is running." else - echo "==> MongoDB container is not running." + echo "[RESULT]: MongoDB container is not running." fi else - echo "==> MongoDB container is not running." + 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 "==> RabbitMQ container is running." + echo "[RESULT]: RabbitMQ container is running." else - echo "==> RabbitMQ container is not running." + echo "[RESULT]: RabbitMQ container is not running." fi else - echo "==> RabbitMQ container is not running." + echo "[RESULT]: RabbitMQ container is not running." fi ;; + "backend") echo "==> Checking backend 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 "==> Backend service is running." + if [[ -f "${WORKING_HOME}/.backend.pid" ]]; then + local backend_pid + backend_pid=$(cat "${WORKING_HOME}/.backend.pid") + if docker exec -i "$container_id" ps -p "$backend_pid" &>/dev/null; then + echo "[RESULT]: Backend service is running." + else + echo "[RESULT]: Backend service is not running." + fi + else + echo "[RESULT]: Backend service is not running." + fi else - echo "==> Backend service is not running." + echo "[RESULT]: Backend service is not running." fi - else - echo "==> Backend service is not running." fi ;; + "frontend") echo "==> Checking frontend 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 "==> Frontend service is running." + if [[ -f "${WORKING_HOME}/.frontend.pid" ]]; then + local frontend_pid + frontend_pid=$(cat "${WORKING_HOME}/.frontend.pid") + if docker exec -i "$container_id" ps -p "$frontend_pid" &>/dev/null; then + echo "[RESULT]: Frontend service is running." + else + echo "[RESULT]: Frontend service is not running." + fi + else + echo "[RESULT]: Frontend service is not running." + fi else - echo "==> Frontend service is not running." + echo "[RESULT]: Frontend service is not running." fi - else - echo "==> Frontend service is not running." fi ;; - + "devsvc") + echo "==> Checking devsvc service status..." + if [[ -f "${WORKING_HOME}/.devsvc-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.devsvc-instance") + if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then + echo "[RESULT]: devsvc container is running." + else + echo "[RESULT]: devsvc container is not running." + fi + else + echo "[RESULT]: devsvc container is not running." + fi + ;; + "payment") + echo "==> Checking payment service status..." + if [[ -f "${WORKING_HOME}/.payment-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.payment-instance") + if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then + echo "[RESULT]: payment container is running." + else + echo "[RESULT]: payment container is not running." + fi + else + echo "[RESULT]: payment container is not running." + fi + ;; + "content") + echo "==> Checking content service status..." + if [[ -f "${WORKING_HOME}/.content-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.content-instance") + if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then + echo "[RESULT]: content container is running." + else + echo "[RESULT]: content container is not running." + fi + else + echo "[RESULT]: content container is not running." + fi + ;; + "central_storage") + echo "==> Checking central_storage service status..." + if [[ -f "${WORKING_HOME}/.central_storage-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.central_storage-instance") + if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then + echo "[RESULT]: central_storage container is running." + else + echo "[RESULT]: central_storage container is not running." + fi + else + echo "[RESULT]: central_storage container is not running." + fi + ;; + "authentication") + echo "==> Checking authentication service status..." + if [[ -f "${WORKING_HOME}/.authentication-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.authentication-instance") + if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then + echo "[RESULT]: authentication container is running." + else + echo "[RESULT]: authentication container is not running." + fi + else + echo "[RESULT]: authentication container is not running." + fi + ;; *) echo "ERROR: Unknown component: $comp" exit 1 From 30bfdf1fa292b2f60b3421fea91aefcb9af197f7 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Tue, 11 Feb 2025 11:16:03 +0800 Subject: [PATCH 13/32] Update to add more component stop and start logic --- devbox/devbox.local/devbox | 62 +++++++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index edbd595..a64202d 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1540,7 +1540,7 @@ devbox_start_command() { # If no component is specified, start all components if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1757,6 +1757,27 @@ EOF fi EOF ;; + "devsvc" | "payment" | "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 @@ -1778,11 +1799,9 @@ devbox_stop_command() { local COMPONENT="$(get_arg '--component')" local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" - echo "==> Stopping DevBox services..." - # If the DevBox container is not running, exit if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1830,6 +1849,16 @@ devbox_stop_command() { echo "==> Frontend service is not running." fi ;; + "devsvc" | "payment" | "content" | "central_storage" | "authentication") + echo "==> Stopping $comp service..." + if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.${comp}-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> $comp service is not running." + fi + ;; *) echo "ERROR: Unknown component: $comp" exit 1 @@ -2055,13 +2084,11 @@ devbox_restart_command() { fi if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi - echo "==> Restarting DevBox services..." - # Stop the specified components for comp in "${COMPONENTS[@]}"; do case "$comp" in @@ -2105,6 +2132,16 @@ devbox_restart_command() { echo "==> Frontend service is not running." fi ;; + "devsvc" | "payment" | "content" | "central_storage" | "authentication") + echo "==> Stopping $comp service..." + if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.${comp}-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> $comp service is not running." + fi + ;; *) echo "ERROR: Unknown component: $comp" exit 1 @@ -2213,7 +2250,6 @@ EOF # Start frontend service in the container docker exec -i "$DEVBOX_NAME" bash < Frontend service is not running." fi ;; + "devsvc" | "payment" | "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 From 341b2a5042418cdc07b833af7ca479a829453de7 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Tue, 11 Feb 2025 16:54:13 +0800 Subject: [PATCH 14/32] Update for change to use docker compose --- devbox/devbox.local/devbox | 375 +++++++++--------- .../docker-compose.dev.arm64.new.yaml | 26 +- 2 files changed, 211 insertions(+), 190 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index a64202d..ba530e4 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -208,19 +208,19 @@ devbox_init_usage() { printf " %s\n" "Default: latest" echo - # :flag.usage payment image repo - printf " %s\n" "--payment-image-repo PAYMENT_IMAGE_REPO" - printf " Specifies the repository for payment component. (Optional)\n" + # :flag.usage notification, image repo + printf " %s\n" "--notification-image-repo NOTIFICATION_IMAGE_REPO" + printf " Specifies the repository for notification component. (Optional)\n" echo - # :flag.usage payment image name - printf " %s\n" "--payment-image-name PAYMENT_IMAGE_NAME" - printf " Specifies the image name for payment component. (Optional)\n" + # :flag.usage notification image name + printf " %s\n" "--notification-image-name NOTIFICATION_IMAGE_NAME" + printf " Specifies the image name for notification component. (Optional)\n" echo - # :flag.usage payment image tag - printf " %s\n" "--payment-image-tag PAYMENT_IMAGE_TAG" - printf " Specifies the image tag for payment component. (Optional, default=latest)\n" + # :flag.usage notification image tag + printf " %s\n" "--notification-image-tag NOTIFICATION_IMAGE_TAG" + printf " Specifies the image tag for notification component. (Optional, default=latest)\n" printf " %s\n" "Default: latest" echo @@ -367,7 +367,7 @@ devbox_start_usage() { # :command.usage_flags # :flag.usage printf " %s\n" "--component COMPONENT" - printf " Specifies the name of the component to start (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, payment, content, central_storage,\n authentication).\n" + printf " Specifies the name of the component to start (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, content, central_storage,\n authentication).\n" echo # :command.usage_fixed_flags @@ -407,7 +407,7 @@ devbox_stop_usage() { # :command.usage_flags # :flag.usage printf " %s\n" "--component COMPONENT" - printf " Specifies the name of the component to stop (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, payment, content, central_storage,\n authentication).\n" + printf " Specifies the name of the component to stop (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, content, central_storage,\n authentication).\n" echo # :command.usage_fixed_flags @@ -687,9 +687,59 @@ check_docker_running() { return 1 } +start_local_gitea(){ + echo "[INFO] Starting Gitea container..." + + GITEA_CONTAINER_NAME="freeleaps2-gitea" + GITEA_IMAGE="gitea/gitea:latest" + + # If a container with the same name exists, remove it + if docker ps -a --format '{{.Names}}' | grep -q "^${GITEA_CONTAINER_NAME}\$"; then + echo "==> Removing existing Gitea container..." + docker stop "${GITEA_CONTAINER_NAME}" &>/dev/null || true + docker rm "${GITEA_CONTAINER_NAME}" &>/dev/null || true + fi + + # Pull the Gitea image + echo "==> Pulling Gitea image: ${GITEA_IMAGE}" + if ! docker pull "${GITEA_IMAGE}"; then + echo "ERROR: Failed to pull Gitea image: ${GITEA_IMAGE}" + exit 1 + fi + + # Run the Gitea container mapping port 3000 + gitea_container_id=$(docker run -d --name "${GITEA_CONTAINER_NAME}" \ + -v "${WORKING_HOME}/gitea:/data" \ + -e "DISABLE_REGISTRATION=true" \ + -e "REQUIRE_SIGNIN_VIEW=true" \ + -p 3000:3000 "${GITEA_IMAGE}" ) + + if [[ -z "${gitea_container_id}" ]]; then + echo "ERROR: Failed to start Gitea container." + exit 1 + fi + + echo "Gitea container started successfully: ${gitea_container_id}" + + # Allow Gitea some time to initialize + sleep 20 + + # Check Gitea health via curl + if curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 &>/dev/null; then + echo "Gitea health check passed." + else + echo "ERROR: Gitea health check failed." + exit 1 + fi + + echo "$gitea_container_id" > "$WORKING_HOME/.gitea-instance" + + echo "[INFO] Completed Gitea container..." +} + start_local_mongodb() { echo "==> Starting MongoDB service..." - + MONGO_CONTAINER_NAME="freeleaps2-mongodb" MONGO_IMAGE="mongo:latest" @@ -784,11 +834,11 @@ start_local_rabbitMQ(){ echo "[INFO] Completed RabbitMQ container..." } -# 定义键和值数组 -local_components_ports_keys=("devsvc" "payment" "content" "central_storage" "authentication") -local_components_ports_values=("8007" "8006" "8013" "8005" "8004") +# 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="" @@ -822,6 +872,7 @@ devbox_init_command() { 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 @@ -832,10 +883,7 @@ devbox_init_command() { local DEVSVC_IMAGE="$args_devsvc_image_image" # --devsvc-image-image local DEVSVC_TAG="$args_devsvc_image_tag" # --devsvc-image-tag - local PAYMENT_REPO="$args_payment_image_repo" # --payment-image-repo - local PAYMENT_IMAGE="$args_payment_image_image" # --payment-image-image - local PAYMENT_TAG="$args_payment_image_tag" # --payment-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 @@ -848,6 +896,10 @@ devbox_init_command() { 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 + # --force flag to overwrite existing resources local FORCE_INIT="${args_force}" @@ -867,9 +919,6 @@ devbox_init_command() { 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 PAYMENT_REPO="$(get_arg '--payment-image-repo')" - local PAYMENT_IMAGE="$(get_arg '--payment-image-name')" - local PAYMENT_TAG="$(get_arg '--payment-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')" @@ -879,21 +928,58 @@ devbox_init_command() { 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 FORCE_INIT="$(get_arg '--force')" local is_pull_all_components=true - local components=("devsvc" "payment" "content" "central_storage" "authentication") - + local components=("devsvc" "notification" "content" "central_storage" "authentication") + local start_components=() + echo "==> Checking parameters..." for component in "${components[@]}"; do echo "==> Checking ${component} image repo...value: $(get_arg "--${component}-image-repo")" - # if any component image repo is provided, then don't pull all components - if [[ -n "$(get_arg "--${component}-image-repo")" ]]; then is_pull_all_components=false - break + + else + 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 + echo "==> is_pull_all_components: $is_pull_all_components" echo " ===================================================== " @@ -915,9 +1001,6 @@ devbox_init_command() { echo " DEVSVC_REPO = $DEVSVC_REPO" echo " DEVSVC_IMAGE = $DEVSVC_IMAGE" echo " DEVSVC_TAG = $DEVSVC_TAG" - echo " PAYMENT_REPO = $PAYMENT_REPO" - echo " PAYMENT_IMAGE= $PAYMENT_IMAGE" - echo " PAYMENT_TAG = $PAYMENT_TAG" echo " CONTENT_REPO = $CONTENT_REPO" echo " CONTENT_IMAGE = $CONTENT_IMAGE" echo " CONTENT_TAG = $CONTENT_TAG" @@ -927,6 +1010,9 @@ devbox_init_command() { 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 @@ -1014,10 +1100,6 @@ devbox_init_command() { exit 1 fi - - # 如有 --force 且存在旧容器,则可在此删除旧容器/文件(也可在下面先检查容器再删) - # ... - # ------------------------------------------------------------------- # 5.1 pull and start DevBox container # ------------------------------------------------------------------- @@ -1041,6 +1123,20 @@ devbox_init_command() { fi + 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="$( @@ -1051,6 +1147,7 @@ devbox_init_command() { -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 )" @@ -1070,98 +1167,67 @@ devbox_init_command() { echo "==> [INIT] Starting Freeleaps services... Use Local component $USE_LOCAL_COMPONENT" + if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then - # 3.Create and start MongoDB container - echo "Step 3. [INFO] Starting MongoDB container..." - - start_local_mongodb - - # 4. Pull and start RabbitMQ container - start_local_rabbitMQ - echo ' ===> Using local components for Freeleaps services.' + export ARCH="$ARCH" + 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" - # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) - for component in "${components[@]}"; do - repo_var="$(upper "$component")_REPO" - image_var="$(upper "$component")_IMAGE" - tag_var="$(upper "$component")_TAG" + # 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) + # start component service from start_components array + docker-compose -f docker-compose.dev.arm64.new.yaml up -d mongodb rabbitmq gitea "${start_components[@]}" + echo "==> Starting Gitea, MongoDB, RabbitMQ containers..." + gitea_container_id=$(docker ps --no-trunc -a --filter "name=^freeleaps2-gitea$" --format "{{.ID}}") + echo "$gitea_container_id" > "$WORKING_HOME/.gitea-instance" - # Get the component's repo, image, and tag - COMPONENT_REPO="${!repo_var}" - COMPONENT_IMAGE="${!image_var}" - COMPONENT_TAG="${!tag_var}" - - echo "Component: $component" - echo " Repo: $COMPONENT_REPO" - echo " Image: $COMPONENT_IMAGE" - echo " Tag: $COMPONENT_TAG" + mongo_container_id=$(docker ps --no-trunc -a --filter "name=^freeleaps2-mongodb$" --format "{{.ID}}") + echo "$mongo_container_id" > "$WORKING_HOME/.mongodb-instance" - # check if is_pull_all_components is false and component repo and component image parameter not empty - if [[ "$is_pull_all_components" == false && -n "${!COMPONENT_REPO}" && -n "${!COMPONENT_IMAGE}" ]]; then - echo "==> Using local components for Freeleaps services. For $component, COMPONENT_REPO - '$COMPONENT_REPO', COMPONENT_IMAGE - '$COMPONENT_IMAGE'" - continue - fi + rabbitmq_container_id=$(docker ps --no-trunc -a --filter "name=^freeleaps2-rabbitmq$" --format "{{.ID}}") + echo "$rabbitmq_container_id" > "$WORKING_HOME/.rabbitmq-instance" - component_full_image="" - - if [[ "$is_pull_all_components" == true ]]; then - component_full_image="docker.io/freeleaps/$component:$arch_tag" - else - component_full_image="${!COMPONENT_REPO}/${!COMPONENT_IMAGE}:${!COMPONENT_TAG}" - fi - - echo "==> Pulling ${component} image: $component_full_image" - if ! docker pull "$component_full_image"; then - echo "WARNING: Failed to pull ${component} image: $component_full_image, please Check the image and specify it to initialize the component." - fi - - # if container with same name exists, remove it - if docker ps -a --format '{{.Names}}' | grep -q "^${component}\$"; then - if [[ -n "$FORCE_INIT" ]]; then - echo "==> Removing existing container named $component ..." - docker stop "$component" &>/dev/null || true - docker rm "$component" &>/dev/null || true - else - echo "WARNING: Container named $component already exists. Use --force to remove it." - fi - fi - - DEFAULT_IP=$(docker network inspect bridge | grep -m1 '"Gateway":' | sed -E 's/.*"Gateway": "([^"]+)".*/\1/') - echo "Default gateway IP: $DEFAULT_IP" - - port=$(get_port $component) - echo "==> Creating and starting ${component} container... ${port}" - local component_container_id - component_container_id="$( - docker run -d \ - --name "$component" \ - --link "$DEVBOX_NAME" \ - -p "${port}:${port}" \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -e SERVICE_API_ACCESS_PORT=${port} \ - -e SERVICE_API_ACCESS_HOST=0.0.0.0 \ - -e MONGODB_URI="mongodb://$DEFAULT_IP:27017" \ - "$component_full_image" \ - uvicorn webapi.main:app --reload --port ${port} --host 0.0.0.0 2>/dev/null - )" - if [[ -z "$component_container_id" ]]; then - echo "WARNING: Failed to create ${component} container. please Check the image and specify it to initialize the component." - fi - - echo "$component_container_id" > "$WORKING_HOME/.${component}-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" - done else echo '============================================' echo ' ===> Using online components for Freeleaps services.' echo '============================================' + # Start Gitea, MongoDB, RabbitMQ containers + local_components_docker_compose_output=$(docker-compose -f docker-compose.dev.arm64.new.yaml up -d mongodb rabbitmq) + if [[ -z "$local_components_docker_compose_output" ]]; then + echo "ERROR: Failed to start MongoDB, RabbitMQ containers." + exit 1 + 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" fi pushd $WORKING_HOME +# 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 "==> [INIT] DevBox environment initialization completed." + exit 0 +fi + # 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" @@ -1169,8 +1235,6 @@ if [ ! -d $WORKING_HOME/freeleaps ]; then git clone --depth 5 $FRONTEND_GIT_URL else pushd $WORKING_HOME/freeleaps - pwd - ls -la echo "Git pulling freeleaps.com:3443/products/freeleaps.git" git pull fi @@ -1193,7 +1257,7 @@ echo "step 2: Update /home/.devbox/freeleaps/apps/.env" DEFAULT_IP=\$(ip route | grep default | sed -n 's/.*default via \([^ ]*\).*/\1/p') if [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "true" ]]; then echo "==> Using local components" - # Local components for Freeleaps services (devsvc, payment, content, central_storage, authentication) + # 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 @@ -1210,7 +1274,7 @@ if [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "true" ]]; then export SITE_URL_ROOT=http://\$DEFAULT_IP/ export FREELEAPS_DEVSVC_ENDPOINT=http://\$DEFAULT_IP:8007/api/devsvc/ export FREELEAPS_CONTENT_ENDPOINT=http://\$DEFAULT_IP:8013/api/content/ - export FREELEAPS_PAYMENT_ENDPOINT=http://\$DEFAULT_IP:8006/api/payment/ + export FREELEAPS_NOTIFICATION_ENDPOINT=http://\$DEFAULT_IP:8003/api/notification/ export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://\$DEFAULT_IP:8005/api/central_storage/ export FREELEAPS_AUTHENTICATION_ENDPOINT=http://\$DEFAULT_IP:8004/api/auth/ export FREELEAPS_AILAB_ENDPOINT=https://as010-w2-re-vm.mathmast.com:8009/api/ @@ -1233,7 +1297,7 @@ else export SITE_URL_ROOT=http://localhost/ export FREELEAPS_DEVSVC_ENDPOINT=http://52.149.3.85:8007/api/devsvc/ export FREELEAPS_CONTENT_ENDPOINT=http://52.149.35.244:8013/api/content/ - export FREELEAPS_PAYMENT_ENDPOINT=http://52.149.35.244:8006/api/payment/ + 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/ @@ -1405,8 +1469,7 @@ EOF echo echo "===========================================================" echo "DevBox init completed successfully!" - echo " DevBox container ID: $container_id" - [[ -f "${WORKING_HOME}/.devbox-instance" ]] && echo " devbox container ID: $(cat "${WORKING_HOME}/.devbox-instance")" + echo " DevBox container ID: $WORKING_HOME/.devbox-instance" echo " Repository cloned to: $WORKING_HOME/freeleaps" echo " Backend logs: $WORKING_HOME/logs/backend.logs" echo " Frontend logs: $WORKING_HOME/logs/frontend.logs" @@ -1472,6 +1535,15 @@ devbox_deinit_command() { 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 @@ -1493,7 +1565,7 @@ devbox_deinit_command() { fi # Stop and remove other components - local components=("devsvc" "payment" "content" "central_storage" "authentication") + local components=("devsvc" "notification" "content" "central_storage" "authentication") for component in "${components[@]}"; do if [[ -f "$WORKING_HOME/.${component}-instance" ]]; then local component_container_id @@ -1540,7 +1612,7 @@ devbox_start_command() { # If no component is specified, start all components if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1757,7 +1829,7 @@ EOF fi EOF ;; - "devsvc" | "payment" | "content" | "central_storage" | "authentication") + "devsvc" | "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" @@ -1801,7 +1873,7 @@ devbox_stop_command() { # If the DevBox container is not running, exit if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1849,7 +1921,7 @@ devbox_stop_command() { echo "==> Frontend service is not running." fi ;; - "devsvc" | "payment" | "content" | "central_storage" | "authentication") + "devsvc" | "content" | "central_storage" | "authentication") echo "==> Stopping $comp service..." if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then local container_id @@ -1893,7 +1965,7 @@ devbox_status_command() { # If no component is specified, check all components if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1990,20 +2062,6 @@ devbox_status_command() { echo "[RESULT]: devsvc container is not running." fi ;; - "payment") - echo "==> Checking payment service status..." - if [[ -f "${WORKING_HOME}/.payment-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.payment-instance") - if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then - echo "[RESULT]: payment container is running." - else - echo "[RESULT]: payment container is not running." - fi - else - echo "[RESULT]: payment container is not running." - fi - ;; "content") echo "==> Checking content service status..." if [[ -f "${WORKING_HOME}/.content-instance" ]]; then @@ -2084,7 +2142,7 @@ devbox_restart_command() { fi if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "payment" "content" "central_storage" "authentication") + COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -2132,7 +2190,7 @@ devbox_restart_command() { echo "==> Frontend service is not running." fi ;; - "devsvc" | "payment" | "content" | "central_storage" | "authentication") + "devsvc" | "content" | "central_storage" | "authentication") echo "==> Stopping $comp service..." if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then local container_id @@ -2329,7 +2387,7 @@ EOF echo "==> Frontend service is not running." fi ;; - "devsvc" | "payment" | "content" | "central_storage" | "authentication") + "devsvc" | "content" | "central_storage" | "authentication") echo "==> Restarting $comp service..." if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then local container_id @@ -2683,39 +2741,6 @@ devbox_init_parse_requirements() { fi ;; -# :flag.case - --payment-image-repo) - if [[ -n ${2+x} ]]; then - add_arg '--payment-image-repo' "$2" - shift 2 - else - printf "%s\n" "--payment-image-repo requires an argument: --payment-image-repo FREELEAPS_PAYMENT_IMAGE_REPO" >&2 - exit 1 - fi - ;; - - # :flag.case - --payment-image-name) - if [[ -n ${2+x} ]]; then - add_arg '--payment-image-name' "$2" - shift 2 - else - printf "%s\n" "--payment-image-name requires an argument: --payment-image-name FREELEAPS_PAYMENT_IMAGE_NAME" >&2 - exit 1 - fi - ;; - - # :flag.case - --payment-image-tag) - if [[ -n ${2+x} ]]; then - add_arg '--payment-image-tag' "$2" - shift 2 - else - printf "%s\n" "--payment-image-tag requires an argument: --payment-image-tag FREELEAPS_PAYMENT_IMAGE_TAG" >&2 - exit 1 - fi - ;; - # :flag.case --content-image-repo) if [[ -n ${2+x} ]]; then @@ -2892,10 +2917,6 @@ devbox_init_parse_requirements() { add_arg '--devsvc-image-tag' "latest-linux-arm64" fi - if [ -z "$(get_arg '--payment-image-tag')" ]; then - add_arg '--payment-image-tag' "latest-linux-arm64" - fi - if [ -z "$(get_arg '--content-image-tag')" ]; then add_arg '--content-image-tag' "latest-linux-arm64" fi diff --git a/devbox/devbox.local/docker-compose.dev.arm64.new.yaml b/devbox/devbox.local/docker-compose.dev.arm64.new.yaml index 8766ce5..734a663 100644 --- a/devbox/devbox.local/docker-compose.dev.arm64.new.yaml +++ b/devbox/devbox.local/docker-compose.dev.arm64.new.yaml @@ -2,7 +2,7 @@ services: gitea: # For apple chip, add: platform: linux/amd64 container_name: freeleaps2-gitea - platform: linux/arm64 + platform: linux/${ARCH:-arm64} image: gitea/gitea:latest restart: always ports: @@ -19,8 +19,8 @@ services: # For apple chip, add: platform: linux/amd64 # For apple chip, you may want to downgrade to public mongo:4.4 for log support container_name: freeleaps2-mongodb - platform: linux/arm64 - image: freeleaps.azurecr.io/mongo:latest-linux-arm64 + platform: linux/${ARCH:-arm64} + image: mongo:latest restart: always ports: - "27017:27017" @@ -31,7 +31,7 @@ services: rabbitmq: # For apple chip, add: platform: linux/amd64 - platform: linux/arm64 + platform: linux/${ARCH:-arm64} container_name: freeleaps2-rabbitmq image: rabbitmq:latest restart: always @@ -45,7 +45,7 @@ services: devsvc: container_name: devsvc - image: freeleaps.azurecr.io/devsvc:1.0.0 + image: freeleaps/devsvc:${DEVSVC_IMAGE_TAG:-latest-linux-arm64} restart: always environment: - APP_NAME=devsvc @@ -79,9 +79,9 @@ services: central_storage: container_name: central_storage - image: freeleaps.azurecr.io/central_storage:latest-linux-arm64 + image: freeleaps/central_storage:${CENTRAL_STORAGE_IMAGE_TAG:-latest-linux-arm64} # profiles: [ prod, alpha, dev ] - platform: linux/arm64 + platform: linux/${ARCH:-arm64} restart: always environment: - APP_NAME=central_storage @@ -111,9 +111,9 @@ services: authentication: container_name: authentication - image: freeleaps.azurecr.io/authentication:latest-linux-arm64 + image: freeleaps/authentication:${AUTHENTICATION_IMAGE_TAG:-latest-linux-arm64} # profiles: [ prod, alpha, dev ] - platform: linux/arm64 + platform: linux/${ARCH:-arm64} restart: always environment: - APP_NAME=authentication @@ -145,9 +145,9 @@ services: content: container_name: content - image: freeleaps.azurecr.io/content:latest-linux-arm64 + image: freeleaps/content:${CONTENT_IMAGE_TAG:-latest-linux-arm64} # profiles: [ prod, alpha, dev ] - platform: linux/arm64 + platform: linux/${ARCH:-arm64} restart: always environment: - APP_NAME=content @@ -176,9 +176,9 @@ services: notification: container_name: notification - image: freeleaps.azurecr.io/notification:latest-linux-arm64 + image: freeleaps/notification:${NOTIFICATION_IMAGE_TAG:-latest-linux-arm64} # profiles: [ prod, alpha, dev ] - platform: linux/arm64 + platform: linux/${ARCH:-arm64} restart: always environment: - APP_NAME=notification From 3f3580e25e405cff5320fc665ccbba19f5301b45 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Wed, 12 Feb 2025 00:37:14 +0800 Subject: [PATCH 15/32] Update for optimize all start/stop/restart logic and docker compose configuration --- devbox/devbox.local/devbox | 1008 +++++++---------- .../docker-compose.dev.arm64.new.yaml | 16 +- 2 files changed, 406 insertions(+), 618 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index ba530e4..e2731dc 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -687,153 +687,6 @@ check_docker_running() { return 1 } -start_local_gitea(){ - echo "[INFO] Starting Gitea container..." - - GITEA_CONTAINER_NAME="freeleaps2-gitea" - GITEA_IMAGE="gitea/gitea:latest" - - # If a container with the same name exists, remove it - if docker ps -a --format '{{.Names}}' | grep -q "^${GITEA_CONTAINER_NAME}\$"; then - echo "==> Removing existing Gitea container..." - docker stop "${GITEA_CONTAINER_NAME}" &>/dev/null || true - docker rm "${GITEA_CONTAINER_NAME}" &>/dev/null || true - fi - - # Pull the Gitea image - echo "==> Pulling Gitea image: ${GITEA_IMAGE}" - if ! docker pull "${GITEA_IMAGE}"; then - echo "ERROR: Failed to pull Gitea image: ${GITEA_IMAGE}" - exit 1 - fi - - # Run the Gitea container mapping port 3000 - gitea_container_id=$(docker run -d --name "${GITEA_CONTAINER_NAME}" \ - -v "${WORKING_HOME}/gitea:/data" \ - -e "DISABLE_REGISTRATION=true" \ - -e "REQUIRE_SIGNIN_VIEW=true" \ - -p 3000:3000 "${GITEA_IMAGE}" ) - - if [[ -z "${gitea_container_id}" ]]; then - echo "ERROR: Failed to start Gitea container." - exit 1 - fi - - echo "Gitea container started successfully: ${gitea_container_id}" - - # Allow Gitea some time to initialize - sleep 20 - - # Check Gitea health via curl - if curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 &>/dev/null; then - echo "Gitea health check passed." - else - echo "ERROR: Gitea health check failed." - exit 1 - fi - - echo "$gitea_container_id" > "$WORKING_HOME/.gitea-instance" - - echo "[INFO] Completed Gitea container..." -} - -start_local_mongodb() { - echo "==> Starting MongoDB service..." - - MONGO_CONTAINER_NAME="freeleaps2-mongodb" - MONGO_IMAGE="mongo:latest" - - # if a container with the same name exists, remove it - if docker ps -a --format '{{.Names}}' | grep -q "^${MONGO_CONTAINER_NAME}\$"; then - echo "==> Removing existing MongoDB container..." - docker stop "$MONGO_CONTAINER_NAME" &>/dev/null || true - docker rm "$MONGO_CONTAINER_NAME" &>/dev/null || true - fi - - echo "==> Pulling MongoDB image: $MONGO_IMAGE" - if ! docker pull "$MONGO_IMAGE"; then - echo "ERROR: Failed to pull MongoDB image: $MONGO_IMAGE" - exit 1 - fi - - echo "==> Starting MongoDB container..." - mongo_container_id=$(docker run -d --name "$MONGO_CONTAINER_NAME" -p 27017:27017 "$MONGO_IMAGE" mongod --bind_ip_all) - if [[ -z "$mongo_container_id" ]]; then - echo "ERROR: Failed to start MongoDB container." - exit 1 - fi - echo "MongoDB container started successfully: $mongo_container_id" - - sleep 10 - - MAX_ATTEMPTS=10 - ATTEMPT=0 - while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do - if docker exec "$MONGO_CONTAINER_NAME" mongosh --eval "db.adminCommand('ping')" 2>/dev/null | grep -q '{ ok: 1 }'; then - echo "MongoDB health check passed." - break - fi - echo "Waiting for MongoDB to be ready... (Attempt $((ATTEMPT+1)))" - sleep 10 - ATTEMPT=$((ATTEMPT+1)) - done - - - if [ $ATTEMPT -eq $MAX_ATTEMPTS ]; then - echo "ERROR: MongoDB health check failed." - exit 1 - fi - - echo "$mongo_container_id" > "$WORKING_HOME/.mongodb-instance" - echo "==> Completed MongoDB container..." -} - -start_local_rabbitMQ(){ - echo "[INFO] Starting RabbitMQ container..." - - RABBITMQ_CONTAINER_NAME="freeleaps2" - RABBITMQ_IMAGE="rabbitmq:latest" - - # If a container with the same name exists, remove it - if docker ps -a --format '{{.Names}}' | grep -q "^${RABBITMQ_CONTAINER_NAME}\$"; then - echo "==> Removing existing RabbitMQ container..." - docker stop "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true - docker rm "${RABBITMQ_CONTAINER_NAME}" &>/dev/null || true - fi - - # Pull the RabbitMQ image - echo "==> Pulling RabbitMQ image: ${RABBITMQ_IMAGE}" - if ! docker pull "${RABBITMQ_IMAGE}"; then - echo "ERROR: Failed to pull RabbitMQ image: ${RABBITMQ_IMAGE}" - exit 1 - fi - - # Run the RabbitMQ container mapping port 5672 - rabbitmq_container_id=$(docker run -d --name "${RABBITMQ_CONTAINER_NAME}" -p 5672:5672 "${RABBITMQ_IMAGE}") - - if [[ -z "${rabbitmq_container_id}" ]]; then - echo "ERROR: Failed to start RabbitMQ container." - exit 1 - fi - - echo "RabbitMQ container started successfully: ${rabbitmq_container_id}" - - # Allow RabbitMQ some time to initialize - sleep 20 - - # Check RabbitMQ health via rabbitmqctl - if docker exec "${RABBITMQ_CONTAINER_NAME}" rabbitmqctl status &>/dev/null; then - echo "RabbitMQ health check passed." - else - echo "ERROR: RabbitMQ health check failed." - exit 1 - fi - - echo "$rabbitmq_container_id" > "$WORKING_HOME/.rabbitmq-instance" - - echo "[INFO] Completed RabbitMQ container..." -} - # 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") @@ -900,6 +753,8 @@ devbox_init_command() { 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}" @@ -931,6 +786,7 @@ devbox_init_command() { 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')" @@ -1167,10 +1023,12 @@ devbox_init_command() { echo "==> [INIT] Starting Freeleaps services... Use Local component $USE_LOCAL_COMPONENT" +export ARCH="$ARCH" +export WORKING_HOME="$WORKING_HOME" if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then echo ' ===> Using local components for Freeleaps services.' - export ARCH="$ARCH" + export DEVSVC_IMAGE_TAG="$DEVSVC_TAG" export CONTENT_IMAGE_TAG="$CONTENT_TAG" export CENTRAL_STORAGE_IMAGE_TAG="$CENTRAL_STORAGE_TAG" @@ -1218,6 +1076,9 @@ else echo "$rabbitmq_container_id" > "$WORKING_HOME/.rabbitmq-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 # Make a user input (Y/N) to continue pull freeleaps.com code and start if N then exit @@ -1261,23 +1122,23 @@ if [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "true" ]]; then cat << 'EOFinner' > /home/.devbox/freeleaps/apps/.env # Online endpoint info export MONGODB_NAME=freeleaps2 - export MONGODB_URI=mongodb://\$DEFAULT_IP:27017/ + 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=\$DEFAULT_IP + 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://\$DEFAULT_IP/ - export FREELEAPS_DEVSVC_ENDPOINT=http://\$DEFAULT_IP:8007/api/devsvc/ - export FREELEAPS_CONTENT_ENDPOINT=http://\$DEFAULT_IP:8013/api/content/ - export FREELEAPS_NOTIFICATION_ENDPOINT=http://\$DEFAULT_IP:8003/api/notification/ - export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://\$DEFAULT_IP:8005/api/central_storage/ - export FREELEAPS_AUTHENTICATION_ENDPOINT=http://\$DEFAULT_IP:8004/api/auth/ - export FREELEAPS_AILAB_ENDPOINT=https://as010-w2-re-vm.mathmast.com:8009/api/ + export SITE_URL_ROOT=http://localhost + export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ + export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ + export FREELEAPS_NOTIFICATION_ENDPOINT=http://localhost:8003/api/notification/ + export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://localhost:8005/api/central_storage/ + export FREELEAPS_AUTHENTICATION_ENDPOINT=http://localhost:8004/api/auth/ + export FREELEAPS_AILAB_ENDPOINT=https://localhost:8009/api/ export KAFKA_SERVER_URL='' export EMAIL_FROM=freeleaps@freeleaps.com EOFinner @@ -1463,6 +1324,7 @@ echo "Freeleaps services started successfully" EOF + # ------------------------------------------------------------------- # 10. Final notification # ------------------------------------------------------------------- @@ -1505,6 +1367,7 @@ devbox_deinit_command() { if [[ "$CLEAR_LOGS" == "true" ]]; then echo "==> Clearing logs in $WORKING_HOME/logs..." rm -rf "$WORKING_HOME/logs"/* 2>/dev/null || true + ls -l "$WORKING_HOME/logs" else echo "==> Skipping log clearing." fi @@ -1577,6 +1440,9 @@ devbox_deinit_command() { fi done + # Remove the use-local-component file + rm -f "$WORKING_HOME/.use-local-component" + echo "==> DevBox deinitialization completed." } @@ -1585,6 +1451,7 @@ 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" @@ -1610,16 +1477,59 @@ devbox_start_command() { echo "==> DevBox container is already running." fi - # If no component is specified, start all components - if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") + # Check if use local component + if [[ -f "$WORKING_HOME/.use-local-component" ]]; then + USE_LOCAL_COMPONENT=$(cat "$WORKING_HOME/.use-local-component") else - COMPONENTS=("$COMPONENT") + 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" "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") + echo "==> Starting Gitea..." + # Check if Gitea container file path + local gitea_container_id_file_path="${WORKING_HOME}/.gitea-instance" + if [[ ! -f "$gitea_container_id_file_path" ]]; then + echo "ERROR: Gitea container is not running. Please run 'devbox init' first." + else + local gitea_container_id=$(cat "$gitea_container_id_file_path") + if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${gitea_container_id}\$"; then + echo "==> Gitea container is not running, starting container..." + # Start the container + if ! docker start "${gitea_container_id}"; then + echo "ERROR: Failed to start Gitea container." + exit 1 + fi + echo "==> Gitea container started successfully." + else + echo "==> Gitea container is already running." + fi + fi + ;; "mongodb") echo "==> Starting MongoDB..." # Check if MongoDB container file path @@ -1662,174 +1572,28 @@ devbox_start_command() { fi fi ;; - "backend") - echo "==> Starting backend service..." - # start the backend service - docker exec -i "$devbox_container_id" bash < /dev/null; then - echo "Backend service is already running." -else - # Remove the .backend.pid file - rm -f /home/.devbox/.backend.pid - - echo '============================================' - echo 'Start to run start_webapi.sh' - echo '============================================' - pushd /home/.devbox/freeleaps/apps - ./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 & - BACKEND_PID=\$! - - # Save BACKEND_PID to a file \${WORKING_HOME}/.frontend.pid: Stores the process id of frontend 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 - - # Check if \$DEVBOX_BACKEND_PORT exists - DEVBOX_BACKEND_PORT=\$(cat /home/.devbox/.devbox-backend-port) - if [ -z "\$DEVBOX_BACKEND_PORT" ]; then - echo "ERROR: DEVBOX_BACKEND_PORT is not set." - export DEVBOX_BACKEND_PORT=8002 - fi - - 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 -fi -EOF - - ;; - "frontend") - echo "==> Starting frontend service..." - # Start the frontend service - docker exec -i "$devbox_container_id" bash < /dev/null; then - echo "Frontend service is already running." - exit 0 - fi - fi - - # Remove the .frontend.pid file before starting the frontend service - rm -f /home/.devbox/.frontend.pid - fi - echo '============================================' - echo ' Start frontend service locally' - echo '============================================' - pushd /home/.devbox/freeleaps/frontend - - # Check if npm is installed and pnpm is installed and is npm run dev result is generated - if ! command -v npm &>/dev/null; then - echo "ERROR: npm is not installed." - exit 1 - fi - - if ! command -v pnpm &>/dev/null; then - echo "ERROR: pnpm is not installed." - exit 1 - fi - - if [ ! -f "package.json" ]; then - echo "ERROR: package.json not found." - exit 1 - fi - - # 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 - - # Wait for the frontend service to start - sleep 10 - - # 30 attempts, 10 seconds each, total wait time 5 minutes - MAX_ATTEMPTS=30 - ATTEMPT=0 - - DEVBOX_FRONTEND_PORT=\$(cat /home/.devbox/.devbox-frontend-port) - # get DEVBOX_FRONTEND_PORT from environment variables - if [ -z "\$DEVBOX_FRONTEND_PORT" ]; then - echo "ERROR: DEVBOX_FRONTEND_PORT is not set." - export DEVBOX_FRONTEND_PORT=5173 - fi - - 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 + "devbox") + echo "==> Starting DevBox..." + # Check if 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." else - echo "Waiting for Frontend to become available... (http://localhost:\$DEVBOX_FRONTEND_PORT), (HTTP \$HTTP_CODE) Attempt \$((ATTEMPT+1))" - ATTEMPT=\$((ATTEMPT+1)) - sleep 10 + local devbox_container_id=$(cat "$devbox_container_id_file_path") + if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then + echo "==> DevBox container is not running, starting container..." + # Start the container + if ! docker start "${devbox_container_id}"; then + echo "ERROR: Failed to start DevBox container." + exit 1 + fi + echo "==> DevBox container started successfully." + else + echo "==> DevBox container is already running." + fi fi - done - - if [ \$ATTEMPT -eq \$MAX_ATTEMPTS ]; then - echo "ERROR: Frontend failed to start after \$MAX_ATTEMPTS attempts" - exit 1 - fi -EOF ;; - "devsvc" | "content" | "central_storage" | "authentication") + "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" @@ -1857,11 +1621,92 @@ EOF esac done - # If the frontend component is started, print the URL - if [[ " ${COMPONENTS[@]} " =~ " frontend " ]]; then - echo "==> Frontend started. You can access the Freeleaps web application at: http://localhost:5173" + if [[ "$FREELEAPS_ENDPOINT" == "true" ]]; then + # Start the backend and frontend services + docker exec -i "$devbox_container_id" bash < /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 + +# 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 backend and frontend services +echo "Testing backend and frontend services..." + +# Test the backend service +echo "Testing backend service..." +curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$SERVICE_API_ACCESS_PORT/docs" +if [ "\$?" -ne 0 ]; then + echo "ERROR: Backend service is not available." + exit 1 +fi + +# Test the frontend service + +echo "Testing frontend service..." +curl -s -o /dev/null -w "%{http_code}" "http://localhost:5173/" +if [ "\$?" -ne 0 ]; then + echo "ERROR: Frontend service is not available." + exit 1 +fi + +echo "Backend and frontend services started successfully." +EOF fi + + echo "==> DevBox services started successfully." } @@ -1873,7 +1718,7 @@ devbox_stop_command() { # If the DevBox container is not running, exit if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") + COMPONENTS=("mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1901,27 +1746,21 @@ devbox_stop_command() { echo "==> RabbitMQ container is not running." fi ;; - "backend") - echo "==> Stopping backend service..." + "devbox") + echo "==> Stopping devbox..." if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then local container_id container_id=$(cat "${WORKING_HOME}/.devbox-instance") 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 "==> Backend service is not running." fi ;; - "frontend") - echo "==> Stopping frontend service..." - if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.devbox-instance") - docker stop "$container_id" &>/dev/null || true - else - echo "==> Frontend service is not running." - fi - ;; - "devsvc" | "content" | "central_storage" | "authentication") + + "devsvc" | "notification" | "content" | "central_storage" | "authentication") echo "==> Stopping $comp service..." if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then local container_id @@ -1962,10 +1801,9 @@ devbox_status_command() { exit 1 fi - # If no component is specified, check all components if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") + COMPONENTS=("mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -2003,107 +1841,36 @@ devbox_status_command() { fi ;; - "backend") - echo "==> Checking backend service status..." + "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 - if [[ -f "${WORKING_HOME}/.backend.pid" ]]; then - local backend_pid - backend_pid=$(cat "${WORKING_HOME}/.backend.pid") - if docker exec -i "$container_id" ps -p "$backend_pid" &>/dev/null; then - echo "[RESULT]: Backend service is running." - else - echo "[RESULT]: Backend service is not running." - fi - else - echo "[RESULT]: Backend service is not running." - fi + echo "[RESULT]: devbox container is running." else - echo "[RESULT]: Backend service is not running." - fi - fi - ;; - - "frontend") - echo "==> Checking frontend 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 - if [[ -f "${WORKING_HOME}/.frontend.pid" ]]; then - local frontend_pid - frontend_pid=$(cat "${WORKING_HOME}/.frontend.pid") - if docker exec -i "$container_id" ps -p "$frontend_pid" &>/dev/null; then - echo "[RESULT]: Frontend service is running." - else - echo "[RESULT]: Frontend service is not running." - fi - else - echo "[RESULT]: Frontend service is not running." - fi - else - echo "[RESULT]: Frontend service is not running." - fi - fi - ;; - "devsvc") - echo "==> Checking devsvc service status..." - if [[ -f "${WORKING_HOME}/.devsvc-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.devsvc-instance") - if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then - echo "[RESULT]: devsvc container is running." - else - echo "[RESULT]: devsvc container is not running." + echo "[RESULT]: devbox container is not running." fi else - echo "[RESULT]: devsvc container is not running." + echo "[RESULT]: devbox container is not running." fi ;; - "content") - echo "==> Checking content service status..." - if [[ -f "${WORKING_HOME}/.content-instance" ]]; then + + "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}/.content-instance") + container_id=$(cat "${WORKING_HOME}/.${comp}-instance") if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then - echo "[RESULT]: content container is running." + echo "[RESULT]: $comp service is running." else - echo "[RESULT]: content container is not running." + echo "[RESULT]: $comp service is not running." fi else - echo "[RESULT]: content container is not running." - fi - ;; - "central_storage") - echo "==> Checking central_storage service status..." - if [[ -f "${WORKING_HOME}/.central_storage-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.central_storage-instance") - if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then - echo "[RESULT]: central_storage container is running." - else - echo "[RESULT]: central_storage container is not running." - fi - else - echo "[RESULT]: central_storage container is not running." - fi - ;; - "authentication") - echo "==> Checking authentication service status..." - if [[ -f "${WORKING_HOME}/.authentication-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.authentication-instance") - if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${container_id}\$"; then - echo "[RESULT]: authentication container is running." - else - echo "[RESULT]: authentication container is not running." - fi - else - echo "[RESULT]: authentication container is not running." + echo "[RESULT]: $comp service is not running." fi ;; + *) echo "ERROR: Unknown component: $comp" exit 1 @@ -2118,6 +1885,7 @@ 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" @@ -2141,15 +1909,42 @@ devbox_restart_command() { sleep 20 fi - if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "backend" "frontend" "devsvc" "content" "central_storage" "authentication") + # 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 - COMPONENTS=("$COMPONENT") + 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 + "gitea") + echo "==> Stopping Gitea..." + if [[ -f "${WORKING_HOME}/.gitea-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.gitea-instance") + docker stop "$container_id" &>/dev/null || true + else + echo "==> Gitea container is not running." + fi + ;; "mongodb") echo "==> Stopping MongoDB..." if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then @@ -2170,27 +1965,22 @@ devbox_restart_command() { echo "==> RabbitMQ container is not running." fi ;; - "backend") - echo "==> Stopping backend service..." + "devbox") + echo "==> Stopping devbox service..." if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then local container_id container_id=$(cat "${WORKING_HOME}/.devbox-instance") 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 "==> Backend service is not running." + echo "==> Devbox is not running." fi ;; - "frontend") - echo "==> Stopping frontend service..." - if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.devbox-instance") - docker stop "$container_id" &>/dev/null || true - else - echo "==> Frontend service is not running." - fi - ;; - "devsvc" | "content" | "central_storage" | "authentication") + + "devsvc" | "notification" | "content" | "central_storage" | "authentication") echo "==> Stopping $comp service..." if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then local container_id @@ -2210,6 +2000,16 @@ devbox_restart_command() { # Start the specified components for comp in "${COMPONENTS[@]}"; do case "$comp" in + "gitea") + echo "==> Restarting Gitea..." + if [[ -f "${WORKING_HOME}/.gitea-instance" ]]; then + local container_id + container_id=$(cat "${WORKING_HOME}/.gitea-instance") + docker start "$container_id" &>/dev/null || true + else + echo "==> Gitea container is not running." + fi + ;; "mongodb") echo "==> Restarting MongoDB..." if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then @@ -2230,164 +2030,17 @@ devbox_restart_command() { echo "==> RabbitMQ container is not running." fi ;; - "backend") - echo "==> Restarting backend service..." + "devbox") + echo "==> Restarting devbox service..." if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then local container_id container_id=$(cat "${WORKING_HOME}/.devbox-instance") docker start "$container_id" &>/dev/null || true - - # Start backend service in the container - docker exec -i "$devbox_container_id" bash < /dev/null; then - echo "Backend service is already running." -else - echo '============================================' - echo 'Start to run start_webapi.sh' - echo '============================================' - pushd /home/.devbox/freeleaps/apps - ./start_webapi.sh > /home/.devbox/logs/backend.logs 2>&1 & - BACKEND_PID=\$! - - # Save BACKEND_PID to a file \${WORKING_HOME}/.frontend.pid: Stores the process id of frontend 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 - - # Check if \$DEVBOX_BACKEND_PORT exists - if [ -z "\$DEVBOX_BACKEND_PORT" ]; then - echo "WARNING: DEVBOX_BACKEND_PORT is not set." - export DEVBOX_BACKEND_PORT=8002 - fi - - echo "DEVBOX_BACKEND_PORT: \$DEVBOX_BACKEND_PORT" - 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 -fi -EOF - else - echo "==> Backend service is not running." + echo "==> Devbox is not running." fi ;; - "frontend") - echo "==> Restarting frontend service..." - if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.devbox-instance") - docker start "$container_id" &>/dev/null || true - - # Start frontend service in the container - docker exec -i "$DEVBOX_NAME" bash < /dev/null; then - echo "Frontend service is already running." - exit 0 - else - # Remove the frontend.pid file - rm -f /home/.devbox/.frontend.pid - fi - fi - - echo '============================================' - echo ' Start frontend service locally' - echo '============================================' - pushd /home/.devbox/freeleaps/frontend - - # Check if npm is installed and pnpm is installed and is npm run dev result is generated - if ! command -v npm &>/dev/null; then - echo "ERROR: npm is not installed." - exit 1 - fi - - if ! command -v pnpm &>/dev/null; then - echo "ERROR: pnpm is not installed." - exit 1 - fi - - if [ ! -f "package.json" ]; then - echo "ERROR: package.json not found." - exit 1 - fi - - # 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 - - # Wait for the frontend service to start - sleep 30 - - # 30 attempts, 10 seconds each, total wait time 5 minutes - MAX_ATTEMPTS=30 - ATTEMPT=0 - - # Check if \$DEVBOX_FRONTEND_PORT exists - if [ -z "\$DEVBOX_FRONTEND_PORT" ]; then - echo "ERROR: DEVBOX_FRONTEND_PORT is not set." - export DEVBOX_FRONTEND_PORT=5173 - fi - - 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 -EOF - else - echo "==> Frontend service is not running." - fi - ;; - "devsvc" | "content" | "central_storage" | "authentication") + "devsvc" | "notification" | "content" | "central_storage" | "authentication") echo "==> Restarting $comp service..." if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then local container_id @@ -2404,6 +2057,90 @@ EOF esac done +if [[ "$FREELEAPS_ENDPOINT" == "true" ]]; then + # Start the backend and frontend services + docker exec -i "$devbox_container_id" bash < /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 + +# 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 backend and frontend services +echo "Testing backend and frontend services..." + +# Test the backend service +echo "Testing backend service..." +curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$SERVICE_API_ACCESS_PORT/docs" +if [ "\$?" -ne 0 ]; then + echo "ERROR: Backend service is not available." + exit 1 +fi + +# Test the frontend service + +echo "Testing frontend service..." +curl -s -o /dev/null -w "%{http_code}" "http://localhost:5173/" +if [ "\$?" -ne 0 ]; then + echo "ERROR: Frontend service is not available." + exit 1 +fi + +echo "Backend and frontend services started successfully." +EOF + fi + echo "==> DevBox services restarted successfully." } @@ -2863,18 +2600,6 @@ devbox_init_parse_requirements() { esac done - # :command.required_flags_filter - - if [[ -z "$(get_arg '--freeleaps-username')" ]]; then - - printf "missing required flag: --freeleaps-username FREELEAPS_USERNAME\n" >&2 - exit 1 - fi - if [[ -z "$(get_arg '--freeleaps-password')" ]]; then - printf "missing required flag: --freeleaps-password FREELEAPS_PASSWORD\n" >&2 - exit 1 - fi - # :command.default_assignments if [ -z "$(get_arg '--os')" ]; then @@ -3049,6 +2774,7 @@ devbox_start_parse_requirements() { devbox_start_usage exit ;; + *) break @@ -3078,6 +2804,19 @@ devbox_start_parse_requirements() { fi ;; + --freeleaps-endpoint) + + # :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 @@ -3094,6 +2833,11 @@ devbox_start_parse_requirements() { esac done + # :command.default_assignments + if [ -z "$(get_arg '--freeleaps-endpoint')" ]; then + add_arg '--freeleaps-endpoint' "false" + fi + } # :command.parse_requirements @@ -3180,6 +2924,20 @@ devbox_status_parse_requirements() { while [[ $# -gt 0 ]]; do key="$1" case "$key" in + # :flag.case + --component) + + # :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 @@ -3225,6 +2983,31 @@ devbox_restart_parse_requirements() { while [[ $# -gt 0 ]]; do key="$1" case "$key" in + # :flag.case + --component) + + # :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) + + # :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 @@ -3242,6 +3025,11 @@ devbox_restart_parse_requirements() { esac done + # :command.default_assignments + if [ -z "$(get_arg '--freeleaps-endpoint')" ]; then + add_arg '--freeleaps-endpoint' "false" + fi + } diff --git a/devbox/devbox.local/docker-compose.dev.arm64.new.yaml b/devbox/devbox.local/docker-compose.dev.arm64.new.yaml index 734a663..5667519 100644 --- a/devbox/devbox.local/docker-compose.dev.arm64.new.yaml +++ b/devbox/devbox.local/docker-compose.dev.arm64.new.yaml @@ -11,7 +11,7 @@ services: - DISABLE_REGISTRATION=true - REQUIRE_SIGNIN_VIEW=true volumes: - - freeleaps2-gitea-data:/data + - ${WORKING_HOME}/freeleaps2-gitea/:/data networks: - devbox_freeleaps2-network @@ -25,7 +25,7 @@ services: ports: - "27017:27017" volumes: - - freeleaps2-mongodb-data:/data/db + - ${WORKING_HOME}/freeleaps2-mongodb-data:/data/db networks: - devbox_freeleaps2-network @@ -39,7 +39,7 @@ services: - "5672:5672" - "15672:15672" volumes: - - freeleaps2-rabbitmq-data:/var/lib/rabbitmq + - ${WORKING_HOME}/freeleaps2-rabbitmq-data:/var/lib/rabbitmq networks: - devbox_freeleaps2-network @@ -72,7 +72,7 @@ services: uvicorn webapi.main:app --reload --port=8007 --host=0.0.0.0 volumes: - type: bind - source: /var/lib/docker/app/devsvc/log + source: ${WORKING_HOME}/logs/devsvc target: /app/log/devsvc networks: - devbox_freeleaps2-network @@ -106,7 +106,7 @@ services: - devbox_freeleaps2-network volumes: - type: bind - source: /var/lib/docker/app/central_storage/log + source: ${WORKING_HOME}/logs/central_storage target: /app/log/central_storage authentication: @@ -140,7 +140,7 @@ services: - devbox_freeleaps2-network volumes: - type: bind - source: /var/lib/docker/app/authentication/log + source: ${WORKING_HOME}/logs/authentication target: /app/log/authentication content: @@ -171,7 +171,7 @@ services: - devbox_freeleaps2-network volumes: - type: bind - source: /var/lib/docker/app/content/log + source: ${WORKING_HOME}/logs/content target: /app/log/content notification: @@ -207,7 +207,7 @@ services: - devbox_freeleaps2-network volumes: - type: bind - source: /var/lib/docker/app/notification/log + source: ${WORKING_HOME}/logs/notification target: /app/log/notification volumes: From 137c9936b0485d4dd67fe2694928058becc008ed Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Wed, 12 Feb 2025 16:36:07 +0800 Subject: [PATCH 16/32] Update for component init checking logic --- devbox/devbox.local/devbox | 345 +++++++++++-------------------------- 1 file changed, 96 insertions(+), 249 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index e2731dc..6d77294 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -178,18 +178,18 @@ devbox_init_usage() { echo # :flag.usage freeleaps username - printf " %s\n" "--freeleaps-username FREELEAPS_USERNAME (required)" - printf " Specifies the Freeleaps.com repository username (Required).\n" + printf " %s\n" "--freeleaps-username FREELEAPS_USERNAME (optional)" + printf " Specifies the Freeleaps.com repository username (Optional).\n" echo # :flag.usage freeleaps password - printf " %s\n" "--freeleaps-password FREELEAPS_PASSWORD (required)" - printf " Specifies the Freeleaps.com password repository (Required).\n" + printf " %s\n" "--freeleaps-password FREELEAPS_PASSWORD (optional)" + printf " Specifies the Freeleaps.com password repository (Optional).\n" echo # :flag.usage use local component printf " %s\n" "--use-local-component USE_LOCAL_COMPONENT" - printf " Check if use local component or use online dev environment. (Default: false, use online service) (Optional)\n" + printf " Check if use local component or use online dev environment. (Optional, default=false)\n" echo # :flag.usage devsvc image repo @@ -204,7 +204,7 @@ devbox_init_usage() { # :flag.usage devsvc image tag printf " %s\n" "--devsvc-image-tag DEVSVC_IMAGE_TAG" - printf " Specifies the image tag for devsvc component. (Optional, default=latest-)\n" + printf " Specifies the image tag for devsvc component. (Optional, default=latest-linux-arm64)\n" printf " %s\n" "Default: latest" echo @@ -220,7 +220,7 @@ devbox_init_usage() { # :flag.usage notification image tag printf " %s\n" "--notification-image-tag NOTIFICATION_IMAGE_TAG" - printf " Specifies the image tag for notification component. (Optional, default=latest)\n" + printf " Specifies the image tag for notification component. (Optional, default=latest-linux-arm64)\n" printf " %s\n" "Default: latest" echo @@ -236,7 +236,7 @@ devbox_init_usage() { # :flag.usage content image tag printf " %s\n" "--content-image-tag CONTENT_IMAGE_TAG" - printf " Specifies the image tag for content component. (Optional, default=latest)\n" + printf " Specifies the image tag for content component. (Optional, default=latest-linux-arm64)\n" printf " %s\n" "Default: latest" echo @@ -252,7 +252,7 @@ devbox_init_usage() { # :flag.usage central storage image tag printf " %s\n" "--central_storage-image-tag CENTRAL_STORAGE_IMAGE_TAG" - printf " Specifies the image tag for central_storage component. (Optional, default=latest)\n" + printf " Specifies the image tag for central_storage component. (Optional, default=latest-linux-arm64)\n" printf " %s\n" "Default: latest" echo @@ -268,7 +268,7 @@ devbox_init_usage() { # :flag.usage authentication image tag printf " %s\n" "--authentication-image-tag AUTHENTICATION_IMAGE_TAG" - printf " Specifies the image tag for authentication component. (Optional, default=latest)\n" + printf " Specifies the image tag for authentication component. (Optional, default=latest-linux-arm64)\n" printf " %s\n" "Default: latest" echo @@ -725,11 +725,10 @@ devbox_init_command() { 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 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 @@ -796,11 +795,8 @@ devbox_init_command() { echo "==> Checking parameters..." for component in "${components[@]}"; do - echo "==> Checking ${component} image repo...value: $(get_arg "--${component}-image-repo")" if [[ -n "$(get_arg "--${component}-image-repo")" ]]; then is_pull_all_components=false - - else start_components+=("${component}") fi @@ -836,10 +832,12 @@ devbox_init_command() { fi done - echo "==> is_pull_all_components: $is_pull_all_components" + # 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" @@ -898,8 +896,7 @@ devbox_init_command() { exit 1 fi fi - - echo "==> Detected OS: $OS, ARCH: $ARCH" + # Default arch tag value if Arch is amd64 then latest-linux-amd64 else latest-linux-arm64 local arch_tag="latest-linux-${ARCH}" @@ -934,15 +931,21 @@ devbox_init_command() { exit 1 fi - # ------------------------------------------------------------------- - # 4. If .devbox-instance exists, --force,use it - # ------------------------------------------------------------------- if [[ -f "$WORKING_HOME/.devbox-instance" && -z "$FORCE_INIT" ]]; then - echo "ERROR: DevBox already initialized. Use --force to overwrite." - exit 1 + # 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 # ------------------------------------------------------------------- @@ -972,13 +975,20 @@ devbox_init_command() { 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...' @@ -1068,7 +1078,6 @@ else 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" @@ -1081,6 +1090,7 @@ echo "$USE_LOCAL_COMPONENT" > "$WORKING_HOME/.use-local-component" pushd $WORKING_HOME + # 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 @@ -1089,6 +1099,13 @@ if [[ "$user_input" == "N" || "$user_input" == "n" ]]; then exit 0 fi +# 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 + # 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" @@ -1462,21 +1479,6 @@ devbox_start_command() { local devbox_container_id=$(cat "$devbox_container_id_file_path") - echo "==> Starting DevBox services..." - - # Check if DevBox container is running - if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then - echo "==> DevBox container is not running, starting container..." - # Start the container - if ! docker start "${devbox_container_id}"; then - echo "ERROR: Failed to start DevBox container." - exit 1 - fi - echo "==> DevBox container started successfully." - else - echo "==> DevBox container is already running." - fi - # Check if use local component if [[ -f "$WORKING_HOME/.use-local-component" ]]; then USE_LOCAL_COMPONENT=$(cat "$WORKING_HOME/.use-local-component") @@ -1487,7 +1489,7 @@ devbox_start_command() { if [[ "$USE_LOCAL_COMPONENT" == "true" ]]; then # If no component is specified, start all components if [[ -z "$COMPONENT" ]]; then - COMPONENTS=("mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication") + COMPONENTS=( "gitea" "mongodb" "rabbitmq" "devbox" "devsvc" "notification" "content" "central_storage" "authentication") else COMPONENTS=("$COMPONENT") fi @@ -1509,91 +1511,7 @@ devbox_start_command() { # Start the specified components for comp in "${COMPONENTS[@]}"; do case "$comp" in - "gitea") - echo "==> Starting Gitea..." - # Check if Gitea container file path - local gitea_container_id_file_path="${WORKING_HOME}/.gitea-instance" - if [[ ! -f "$gitea_container_id_file_path" ]]; then - echo "ERROR: Gitea container is not running. Please run 'devbox init' first." - else - local gitea_container_id=$(cat "$gitea_container_id_file_path") - if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${gitea_container_id}\$"; then - echo "==> Gitea container is not running, starting container..." - # Start the container - if ! docker start "${gitea_container_id}"; then - echo "ERROR: Failed to start Gitea container." - exit 1 - fi - echo "==> Gitea container started successfully." - else - echo "==> Gitea container is already running." - fi - fi - ;; - "mongodb") - echo "==> Starting MongoDB..." - # Check if MongoDB container file path - local mongodb_container_id_file_path="${WORKING_HOME}/.mongodb-instance" - if [[ ! -f "$mongodb_container_id_file_path" ]]; then - echo "ERROR: MongoDB container is not running. Please run 'devbox init' first." - else - local mongodb_container_id=$(cat "$mongodb_container_id_file_path") - if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${mongodb_container_id}\$"; then - echo "==> MongoDB container is not running, starting container..." - # Start the container - if ! docker start "${mongodb_container_id}"; then - echo "ERROR: Failed to start MongoDB container." - exit 1 - fi - echo "==> MongoDB container started successfully." - else - echo "==> MongoDB container is already running." - fi - fi - ;; - "rabbitmq") - echo "==> Starting RabbitMQ..." - # Check if RabbitMQ container file path - local rabbitmq_container_id_file_path="${WORKING_HOME}/.rabbitmq-instance" - if [[ ! -f "$rabbitmq_container_id_file_path" ]]; then - echo "ERROR: RabbitMQ container is not running. Please run 'devbox init' first." - else - local rabbitmq_container_id=$(cat "$rabbitmq_container_id_file_path") - if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${rabbitmq_container_id}\$"; then - echo "==> RabbitMQ container is not running, starting container..." - # Start the container - if ! docker start "${rabbitmq_container_id}"; then - echo "ERROR: Failed to start RabbitMQ container." - exit 1 - fi - echo "==> RabbitMQ container started successfully." - else - echo "==> RabbitMQ container is already running." - fi - fi - ;; - "devbox") - echo "==> Starting DevBox..." - # Check if 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." - else - local devbox_container_id=$(cat "$devbox_container_id_file_path") - if ! docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then - echo "==> DevBox container is not running, starting container..." - # Start the container - if ! docker start "${devbox_container_id}"; then - echo "ERROR: Failed to start DevBox container." - exit 1 - fi - echo "==> DevBox container started successfully." - else - echo "==> DevBox container is already running." - fi - fi - ;; - "devsvc" | "notification" | "content" | "central_storage" | "authentication") + "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" @@ -1621,7 +1539,20 @@ devbox_start_command() { 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 < Stopping MongoDB..." - if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.mongodb-instance") - docker stop "$container_id" &>/dev/null || true - else - echo "==> MongoDB container is not running." - fi - ;; - "rabbitmq") - echo "==> Stopping RabbitMQ..." - if [[ -f "${WORKING_HOME}/.rabbitmq-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.rabbitmq-instance") - docker stop "$container_id" &>/dev/null || true - else - echo "==> RabbitMQ container is not running." - fi - ;; "devbox") - echo "==> Stopping devbox..." if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then local container_id container_id=$(cat "${WORKING_HOME}/.devbox-instance") - 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" + 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 ;; - "devsvc" | "notification" | "content" | "central_storage" | "authentication") - echo "==> Stopping $comp service..." + "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") - docker stop "$container_id" &>/dev/null || true + 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 @@ -1775,9 +1699,8 @@ devbox_stop_command() { exit 1 ;; esac - echo "==> $comp service stopped successfully." done - echo "==> DevBox services stopped successfully." + echo "==> All conponent services stopped successfully." } # :command.function @@ -1895,19 +1818,6 @@ devbox_restart_command() { fi local devbox_container_id=$(cat "$devbox_container_id_file_path") - if ! docker ps -a --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then - echo "ERROR: DevBox container is not running. Please run 'devbox init' first." - rm -f "$devbox_container_id_file_path" - exit 1 - fi - - if docker ps --no-trunc --format '{{.ID}}' | grep -q "^${devbox_container_id}\$"; then - echo "==> DevBox container is running." - else - echo "==> DevBox container is not running." - docker start "$devbox_container_id" &>/dev/null || true - sleep 20 - fi # Check if current environment is using local components USE_LOCAL_COMPONENT=$(cat "${WORKING_HOME}/.use-local-component" 2>/dev/null || true) @@ -1935,41 +1845,11 @@ devbox_restart_command() { # Stop the specified components for comp in "${COMPONENTS[@]}"; do case "$comp" in - "gitea") - echo "==> Stopping Gitea..." - if [[ -f "${WORKING_HOME}/.gitea-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.gitea-instance") - docker stop "$container_id" &>/dev/null || true - else - echo "==> Gitea container is not running." - fi - ;; - "mongodb") - echo "==> Stopping MongoDB..." - if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.mongodb-instance") - docker stop "$container_id" &>/dev/null || true - else - echo "==> MongoDB container is not running." - fi - ;; - "rabbitmq") - echo "==> Stopping RabbitMQ..." - if [[ -f "${WORKING_HOME}/.rabbitmq-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.rabbitmq-instance") - docker stop "$container_id" &>/dev/null || true - else - echo "==> RabbitMQ container is not running." - fi - ;; "devbox") - echo "==> Stopping devbox service..." 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 @@ -1980,11 +1860,12 @@ devbox_restart_command() { fi ;; - "devsvc" | "notification" | "content" | "central_storage" | "authentication") - echo "==> Stopping $comp service..." + "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." @@ -2000,47 +1881,7 @@ devbox_restart_command() { # Start the specified components for comp in "${COMPONENTS[@]}"; do case "$comp" in - "gitea") - echo "==> Restarting Gitea..." - if [[ -f "${WORKING_HOME}/.gitea-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.gitea-instance") - docker start "$container_id" &>/dev/null || true - else - echo "==> Gitea container is not running." - fi - ;; - "mongodb") - echo "==> Restarting MongoDB..." - if [[ -f "${WORKING_HOME}/.mongodb-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.mongodb-instance") - docker start "$container_id" &>/dev/null || true - else - echo "==> MongoDB container is not running." - fi - ;; - "rabbitmq") - echo "==> Restarting RabbitMQ..." - if [[ -f "${WORKING_HOME}/.rabbitmq-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.rabbitmq-instance") - docker start "$container_id" &>/dev/null || true - else - echo "==> RabbitMQ container is not running." - fi - ;; - "devbox") - echo "==> Restarting devbox service..." - if [[ -f "${WORKING_HOME}/.devbox-instance" ]]; then - local container_id - container_id=$(cat "${WORKING_HOME}/.devbox-instance") - docker start "$container_id" &>/dev/null || true - else - echo "==> Devbox is not running." - fi - ;; - "devsvc" | "notification" | "content" | "central_storage" | "authentication") + "gitea" | "mongodb" | "rabbitmq" | "devbox" | "devsvc" | "notification" | "content" | "central_storage" | "authentication") echo "==> Restarting $comp service..." if [[ -f "${WORKING_HOME}/.${comp}-instance" ]]; then local container_id @@ -2058,6 +1899,14 @@ devbox_restart_command() { 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 < Date: Wed, 12 Feb 2025 21:49:33 +0800 Subject: [PATCH 17/32] Update for check component start status when init --- devbox/devbox.local/devbox | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 6d77294..bb82fbe 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -792,6 +792,11 @@ devbox_init_command() { local is_pull_all_components=true local components=("devsvc" "notification" "content" "central_storage" "authentication") local start_components=() + + # if use online components, check if any component image repo is specified + if [[ "$USE_LOCAL_COMPONENT" == false ]]; then + is_pull_all_components=false + fi echo "==> Checking parameters..." for component in "${components[@]}"; do @@ -832,6 +837,7 @@ devbox_init_command() { fi done + # If is_pull_all_components is true, then pull all components if [[ "$is_pull_all_components" == true ]]; then start_components=("${components[@]}") @@ -1071,11 +1077,8 @@ else echo ' ===> Using online components for Freeleaps services.' echo '============================================' # Start Gitea, MongoDB, RabbitMQ containers - local_components_docker_compose_output=$(docker-compose -f docker-compose.dev.arm64.new.yaml up -d mongodb rabbitmq) - if [[ -z "$local_components_docker_compose_output" ]]; then - echo "ERROR: Failed to start MongoDB, RabbitMQ containers." - exit 1 - fi + 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\$") @@ -1085,6 +1088,15 @@ else 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" From 63f3158f04c38558d0cab632865b61cbf06ea683 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Fri, 14 Feb 2025 16:22:48 +0800 Subject: [PATCH 18/32] Update for check git repo freeleaps.com user&password access --- devbox/devbox.local/devbox | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index bb82fbe..a4f0283 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1118,6 +1118,14 @@ if [[ -z "$FREELEAPS_USERNAME" || -z "$FREELEAPS_PASSWORD" ]]; then 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." + echo "==> [INIT] DevBox environment initialization completed." + exit 1 +fi + # 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" @@ -1395,8 +1403,8 @@ devbox_deinit_command() { # Clear the DevBox container logs if [[ "$CLEAR_LOGS" == "true" ]]; then echo "==> Clearing logs in $WORKING_HOME/logs..." + sudo chown -R $(whoami):$(whoami) "$WORKING_HOME/logs" rm -rf "$WORKING_HOME/logs"/* 2>/dev/null || true - ls -l "$WORKING_HOME/logs" else echo "==> Skipping log clearing." fi @@ -1404,6 +1412,7 @@ devbox_deinit_command() { # 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" else echo "==> Skipping repository deletion." From a139aa37f72f041b9516dec515f2923a760ff512 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Fri, 14 Feb 2025 16:26:32 +0800 Subject: [PATCH 19/32] Update for error message when access freeleaps.com repository failed --- devbox/devbox.local/devbox | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index a4f0283..c6d2c84 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1121,8 +1121,8 @@ 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." - echo "==> [INIT] DevBox environment initialization completed." + 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 From 3d40de5a6f8f1b4e2ffb9254debe111c8aa1fd53 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Sun, 16 Feb 2025 08:57:18 +0800 Subject: [PATCH 20/32] Update for adding JWT_SECRET_KEY environment var and adding shorten parameters to each command --- devbox/devbox.local/devbox | 873 +++++++++++++------------------------ 1 file changed, 305 insertions(+), 568 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index c6d2c84..e320d5d 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -51,445 +51,187 @@ get_arg() { # :command.usage devbox_usage() { - printf "devbox - DevBox Command Line Tool\n\n" + printf "Command\n" + printf " devbox : DevBox Command Line Tool for managing the local development environment.\n\n" - printf "%s\n" "Usage:" - printf " devbox COMMAND\n" - printf " devbox [COMMAND] --help | -h\n" - printf " devbox --version | -v\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_commands - printf "%s\n" "Commands:" - printf " %s Initialize the local development environment based on DevBox container.\n" "init" - echo - - # :command.long_usage - if [[ -n "$long_usage" ]]; then - printf "%s\n" "Options:" - - # :command.usage_fixed_flags - printf " %s\n" "--help, -h" - printf " Show this help\n" - echo - printf " %s\n" "--version, -v" - printf " Show version number\n" - echo - - # :command.usage_environment_variables - printf "%s\n" "Environment Variables:" - - # :environment_variable.usage - printf " %s\n" "FREELEAPS_USERNAME" - printf " Set the Freeleaps username for cloning the source repository.\n" - echo - - # :environment_variable.usage - printf " %s\n" "FREELEAPS_PASSWORD" - printf " Set the Freeleaps password for cloning the source repository.\n" - echo - - # :environment_variable.usage - printf " %s\n" "WORKING_HOME" - printf " Set the working home directory for DevBox.\n" - echo - - fi } # :command.usage devbox_init_usage() { if [[ -n $long_usage ]]; then - printf "devbox init\n\n" - printf " Initialize the local development environment based on DevBox container.\n This command will pull the DevBox container image, create containers for \n various Freeleaps components, clone the source code repository, and \n persist relevant container/process information under WORKING_HOME.\n \n Sub-command \`init\` uses Docker (or another container runtime) to set \n up a local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Validate flags and environment.\n 2. Pull DevBox base image and create container.\n 3. Pull each required component image, create containers.\n 4. Clone remote source repository using FREELEAPS_USERNAME/PASSWORD.\n 5. Start back-end and front-end services.\n 6. Persist container IDs, logs, etc. into WORKING_HOME.\n\n" + 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 - printf "Alias: i\n" - echo - - printf "%s\n" "Usage:" - printf " devbox init [OPTIONS]\n" - printf " devbox init --help | -h\n" - echo - - # :command.long_usage - if [[ -n "$long_usage" ]]; then - printf "%s\n" "Options:" - - # :command.usage_flags - # :flag.usage os (auto, linux, darwin, wsl2) - printf " %s\n" "--os OS" - printf " Specifies the operating system (auto, linux, darwin, wsl2). Default is auto.\n" - printf " %s\n" "Default: auto" - echo - - # :flag.usage arch - printf " %s\n" "--arch ARCH" - printf " Specifies the architecture (auto, amd64, arm64). Default is auto.\n" - printf " %s\n" "Default: auto" - echo - - # :flag.usage devbox container name - printf " %s\n" "--devbox-container-name DEVBOX_CONTAINER_NAME" - printf " Specifies the DevBox container name. Default is devbox.\n" - printf " %s\n" "Default: devbox" - echo - - # :flag.usage devbox container port - printf " %s\n" "--devbox-container-port DEVBOX_CONTAINER_PORT" - printf " Specifies the container port for DevBox SSH access. Default is 22222.\n" - printf " %s\n" "Default: 22222" - echo - - # :flag.usage devbox image repo - printf " %s\n" "--devbox-image-repo DEVBOX_IMAGE_REPO" - printf " Specifies the DevBox container image repository. Default is\n docker.io/freeleaps.\n" - printf " %s\n" "Default: docker.io/freeleaps" - echo - - # :flag.usage devbox frontend port - printf " %s\n" "--devbox-frontend-port DEVBOX_FRONTEND_PORT" - printf " Specifies the container port for DevBox frontend access. Default is 5173.\n" - printf " %s\n" "Default: 5173" - echo - - # :flag.usage devbox backend port - printf " %s\n" "--devbox-backend-port DEVBOX_BACKEND_PORT" - printf " Specifies the container port for DevBox backend access. Default is 8002.\n" - printf " %s\n" "Default: 8002" - echo - - # :flag.usage devbox image name - printf " %s\n" "--devbox-image-name DEVBOX_IMAGE_NAME" - printf " Specifies the DevBox container image name. Default is devbox.\n" - printf " %s\n" "Default: devbox" - echo - - # :flag.usage devbox image tag - printf " %s\n" "--devbox-image-tag DEVBOX_IMAGE_TAG" - printf " Specifies the DevBox container image tag. Default is latest.\n" - printf " %s\n" "Default: latest" - echo - - # :flag.usage working home - printf " %s\n" "--working-home WORKING_HOME" - printf " Specifies the working home of DevBox CLI. Default is ${HOME}/.devbox.\n" - echo - - # :flag.usage freeleaps username - printf " %s\n" "--freeleaps-username FREELEAPS_USERNAME (optional)" - printf " Specifies the Freeleaps.com repository username (Optional).\n" - echo - - # :flag.usage freeleaps password - printf " %s\n" "--freeleaps-password FREELEAPS_PASSWORD (optional)" - printf " Specifies the Freeleaps.com password repository (Optional).\n" - echo - - # :flag.usage use local component - printf " %s\n" "--use-local-component USE_LOCAL_COMPONENT" - printf " Check if use local component or use online dev environment. (Optional, default=false)\n" - echo - - # :flag.usage devsvc image repo - printf " %s\n" "--devsvc-image-repo DEVSVC_IMAGE_REPO" - printf " Specifies the repository for devsvc component. (Optional)\n" - echo - - # :flag.usage devsvc image name - printf " %s\n" "--devsvc-image-name DEVSVC_IMAGE_NAME" - printf " Specifies the image name for devsvc component. (Optional)\n" - echo - - # :flag.usage devsvc image tag - printf " %s\n" "--devsvc-image-tag DEVSVC_IMAGE_TAG" - printf " Specifies the image tag for devsvc component. (Optional, default=latest-linux-arm64)\n" - printf " %s\n" "Default: latest" - echo - - # :flag.usage notification, image repo - printf " %s\n" "--notification-image-repo NOTIFICATION_IMAGE_REPO" - printf " Specifies the repository for notification component. (Optional)\n" - echo - - # :flag.usage notification image name - printf " %s\n" "--notification-image-name NOTIFICATION_IMAGE_NAME" - printf " Specifies the image name for notification component. (Optional)\n" - echo - - # :flag.usage notification image tag - printf " %s\n" "--notification-image-tag NOTIFICATION_IMAGE_TAG" - printf " Specifies the image tag for notification component. (Optional, default=latest-linux-arm64)\n" - printf " %s\n" "Default: latest" - echo - - # :flag.usage content image repo - printf " %s\n" "--content-image-repo CONTENT_IMAGE_REPO" - printf " Specifies the repository for content component. (Optional)\n" - echo - - # :flag.usage content image name - printf " %s\n" "--content-image-name CONTENT_IMAGE_NAME" - printf " Specifies the image name for content component. (Optional)\n" - echo - - # :flag.usage content image tag - printf " %s\n" "--content-image-tag CONTENT_IMAGE_TAG" - printf " Specifies the image tag for content component. (Optional, default=latest-linux-arm64)\n" - printf " %s\n" "Default: latest" - echo - - # :flag.usage central storage image repo - printf " %s\n" "--central_storage-image-repo CENTRAL_STORAGE_IMAGE_REPO" - printf " Specifies the repository for central_storage component. (Optional)\n" - echo - - # :flag.usage central storage image name - printf " %s\n" "--central_storage-image-name CENTRAL_STORAGE_IMAGE_NAME" - printf " Specifies the image name for central_storage component. (Optional)\n" - echo - - # :flag.usage central storage image tag - printf " %s\n" "--central_storage-image-tag CENTRAL_STORAGE_IMAGE_TAG" - printf " Specifies the image tag for central_storage component. (Optional, default=latest-linux-arm64)\n" - printf " %s\n" "Default: latest" - echo - - # :flag.usage authentication image repo - printf " %s\n" "--authentication-image-repo AUTHENTICATION_IMAGE_REPO" - printf " Specifies the repository for authentication component. (Optional)\n" - echo - - # :flag.usage authentication image name - printf " %s\n" "--authentication-image-name AUTHENTICATION_IMAGE_NAME" - printf " Specifies the image name for authentication component. (Optional)\n" - echo - - # :flag.usage authentication image tag - printf " %s\n" "--authentication-image-tag AUTHENTICATION_IMAGE_TAG" - printf " Specifies the image tag for authentication component. (Optional, default=latest-linux-arm64)\n" - printf " %s\n" "Default: latest" - echo - - # :flag.usage force - printf " %s\n" "--force, -f" - printf " Force initialization even if resources already exist.\n" - echo - - # :command.usage_fixed_flags - printf " %s\n" "--help, -h" - printf " Show this help\n" - echo - - # :command.usage_examples - printf "%s\n" "Examples:" - printf " devbox init --os=linux --arch=arm64 --freeleaps-username alice\n --freeleaps-password secret\n" - printf " devbox init \ --devbox-container-name custom-devbox \ --devbox-container-port\n 22222 \ --freeleaps-username alice \ --freeleaps-password secret\n" - echo - - fi } # :command.usage devbox_deinit_usage() { if [[ -n $long_usage ]]; then - printf "devbox deinit\n\n" - printf " De-initialize the local development environment based on DevBox container.\n This command will stop and remove all containers, clean up the working \n directory, and reset the environment to the initial state.\n \n Sub-command \`deinit\` uses Docker (or another container runtime) to clean \n up the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Stop and remove all containers.\n 2. Clean up the working directory.\n 3. Reset the environment to the initial state.\n\n" + 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 - printf "Alias: d\n" - echo - - printf "%s\n" "Usage:" - printf " devbox deinit [OPTIONS]\n" - printf " devbox deinit --help | -h\n" - echo - - # :command.long_usage - if [[ -n "$long_usage" ]]; then - printf "%s\n" "Options:" - - # :command.usage_flags - # :flag.usage - printf " %s\n" "--working-home WORKING_HOME" - printf " Specifies the working home of DevBox CLI. Default is ${HOME}/.devbox.\n" - printf " %s\n" "Default: ${HOME}/.devbox" - echo - - # :flag.usage - printf " %s\n" "--clear-logs CLEAR_LOGS" - printf " Specifies whether clear log files or not. Default is true.\n" - printf " %s\n" "Default: true" - echo - - # :flag.usage - printf " %s\n" "--clear-repo CLEAR_REPO" - printf " Specifies whether delete source repository or not. Default is false.\n" - printf " %s\n" "Default: false" - echo - - # :command.usage_fixed_flags - printf " %s\n" "--help, -h" - printf " Show this help\n" - echo - - # :command.usage_examples - printf "%s\n" "Examples:" - printf " devbox deinit\n" - printf " devbox deinit --working-home=/tmp/devbox --clear-logs=false --clear-repo=true\n" - echo - - fi } # :command.usage devbox_start_usage() { if [[ -n $long_usage ]]; then - printf "devbox start\n\n" - printf " Start the local development environment based on DevBox container.\n This command will start all containers, services, and processes that \n are required for Freeleaps development.\n \n Sub-command \`start\` uses Docker (or another container runtime) to start \n the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Start all containers.\n 2. Start all services and processes.\n\n" + 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 - - printf "%s\n" "Usage:" - printf " devbox start [OPTIONS]\n" - printf " devbox start --help | -h\n" - echo - - # :command.long_usage - if [[ -n "$long_usage" ]]; then - printf "%s\n" "Options:" - - # :command.usage_flags - # :flag.usage - printf " %s\n" "--component COMPONENT" - printf " Specifies the name of the component to start (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, content, central_storage,\n authentication).\n" - echo - - # :command.usage_fixed_flags - printf " %s\n" "--help, -h" - printf " Show this help\n" - echo - - # :command.usage_examples - printf "%s\n" "Examples:" - printf " devbox start\n" - printf " devbox start --component=backend\n" - echo - - fi } # :command.usage devbox_stop_usage() { if [[ -n $long_usage ]]; then - printf "devbox stop\n\n" - printf " Stop the local development environment based on DevBox container.\n This command will stop all containers, services, and processes that \n are required for Freeleaps development.\n \n Sub-command \`stop\` uses Docker (or another container runtime) to stop \n the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Stop all containers.\n 2. Stop all services and processes.\n\n" + 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\n" + printf "devbox stop - Stop the local development environment based on DevBox container.\n" fi - printf "Alias: p\n" echo - - printf "%s\n" "Usage:" - printf " devbox stop [OPTIONS]\n" - printf " devbox stop --help | -h\n" - echo - - # :command.long_usage - if [[ -n "$long_usage" ]]; then - printf "%s\n" "Options:" - - # :command.usage_flags - # :flag.usage - printf " %s\n" "--component COMPONENT" - printf " Specifies the name of the component to stop (e.g., mongodb, rabbitmq,\n backend, frontend, devsvc, content, central_storage,\n authentication).\n" - echo - - # :command.usage_fixed_flags - printf " %s\n" "--help, -h" - printf " Show this help\n" - echo - - # :command.usage_examples - printf "%s\n" "Examples:" - printf " devbox stop\n" - printf " devbox stop --component=backend\n" - echo - - fi } # :command.usage devbox_status_usage() { if [[ -n $long_usage ]]; then - printf "devbox status\n\n" - printf " Display the status of the local development environment based on DevBox\n container.\n This command will show the status of all containers, services, and processes \n that are required for Freeleaps development.\n \n Sub-command \`status\` uses Docker (or another container runtime) to show \n the status of the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Show the status of all containers.\n 2. Show the status of all services and processes.\n\n" + 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\n" + printf "devbox status - Display the status of the local development environment based on DevBox container.\n" fi - printf "Alias: t\n" echo - - printf "%s\n" "Usage:" - printf " devbox status\n" - printf " devbox status --help | -h\n" - echo - - # :command.long_usage - if [[ -n "$long_usage" ]]; then - printf "%s\n" "Options:" - - # :command.usage_fixed_flags - printf " %s\n" "--help, -h" - printf " Show this help\n" - echo - - # :command.usage_examples - printf "%s\n" "Examples:" - printf " devbox status\n" - printf " devbox status --component=backend\n" - echo - - fi } # :command.usage devbox_restart_usage() { if [[ -n $long_usage ]]; then - printf "devbox restart\n\n" - printf " Restart the local development environment based on DevBox container.\n This command will restart all containers, services, and processes that \n are required for Freeleaps development.\n \n Sub-command \`restart\` uses Docker (or another container runtime) to restart \n the local DevBox environment for Freeleaps development. \n It follows these major steps:\n 1. Stop all containers.\n 2. Start all containers.\n 3. Stop all services and processes.\n 4. Start all services and processes.\n\n" + 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 - - printf "%s\n" "Usage:" - printf " devbox restart\n" - printf " devbox restart --help | -h\n" - echo - - # :command.long_usage - if [[ -n "$long_usage" ]]; then - printf "%s\n" "Options:" - - # :command.usage_fixed_flags - printf " %s\n" "--help, -h" - printf " Show this help\n" - echo - - # :command.usage_examples - printf "%s\n" "Examples:" - printf " devbox restart\n" - printf " devbox restart --component=backend\n" - echo - - fi } # :command.normalize_input @@ -1201,6 +943,7 @@ else 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 @@ -2140,11 +1883,9 @@ devbox_init_parse_requirements() { devbox_init_usage exit ;; - *) break ;; - esac done @@ -2156,147 +1897,137 @@ devbox_init_parse_requirements() { key="$1" case "$key" in # :flag.case - --os) - + --os | -o) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--os' "$2" - shift - shift + shift 2 else printf "%s\n" "--os requires an argument: --os OS" >&2 exit 1 fi ;; - # :flag.case - --arch) - + --arch | -a) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--arch' "$2" - shift - shift + shift 2 else printf "%s\n" "--arch requires an argument: --arch ARCH" >&2 exit 1 fi ;; - # :flag.case - --devbox-container-name) - + --devbox-container-name | -N) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-container-name' "$2" - shift - shift + 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) - + --devbox-container-port | -P) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-container-port' "$2" - shift - shift + 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) - + --devbox-image-repo | -R) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-image-repo' "$2" - shift - shift + 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) - + --devbox-image-name | -I) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-image-name' "$2" - shift - shift + 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) - + --devbox-image-tag | -T) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devbox-image-tag' "$2" - shift - shift + 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) - + --working-home | -w) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--working-home' "$2" - shift - shift + shift 2 else printf "%s\n" "--working-home requires an argument: --working-home WORKING_HOME" >&2 exit 1 fi ;; - # :flag.case - --freeleaps-username) - + --freeleaps-username | -U) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--freeleaps-username' "$2" - shift - shift + shift 2 else printf "%s\n" "--freeleaps-username requires an argument: --freeleaps-username FREELEAPS_USERNAME" >&2 exit 1 fi ;; - # :flag.case - --freeleaps-password) - + --freeleaps-password | -X) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--freeleaps-password' "$2" - shift - shift + shift 2 else printf "%s\n" "--freeleaps-password requires an argument: --freeleaps-password FREELEAPS_PASSWORD" >&2 exit 1 fi ;; - - # :flag.case - --use-local-component) + # :flag.case + --use-local-component | -u) if [[ -n ${2+x} ]]; then add_arg '--use-local-component' "$2" shift 2 @@ -2305,173 +2036,180 @@ devbox_init_parse_requirements() { exit 1 fi ;; - # :flag.case - --devsvc-image-repo) - + --devsvc-image-repo | -D) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devsvc-image-repo' "$2" - shift - shift + 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) - + --devsvc-image-name | -M) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devsvc-image-name' "$2" - shift - shift + 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) - + --devsvc-image-tag | -G) # :flag.case_arg if [[ -n ${2+x} ]]; then add_arg '--devsvc-image-tag' "$2" - shift - shift + shift 2 else printf "%s\n" "--devsvc-image-tag requires an argument: --devsvc-image-tag DEVSVC_IMAGE_TAG" >&2 exit 1 fi ;; - - # :flag.case - --content-image-repo) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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) - 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 + --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 @@ -2545,7 +2283,6 @@ devbox_deinit_parse_requirements() { devbox_deinit_usage exit ;; - *) break ;; @@ -2561,7 +2298,7 @@ devbox_deinit_parse_requirements() { key="$1" case "$key" in # :flag.case - --working-home) + --working-home | -w) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2575,7 +2312,7 @@ devbox_deinit_parse_requirements() { ;; # :flag.case - --clear-logs) + --clear-logs | -l) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2589,7 +2326,7 @@ devbox_deinit_parse_requirements() { ;; # :flag.case - --clear-repo) + --clear-repo | -r) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2659,7 +2396,7 @@ devbox_start_parse_requirements() { key="$1" case "$key" in # :flag.case - --component) + --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2672,7 +2409,7 @@ devbox_start_parse_requirements() { fi ;; - --freeleaps-endpoint) + --freeleaps-endpoint | -e) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2735,7 +2472,7 @@ devbox_stop_parse_requirements() { key="$1" case "$key" in # :flag.case - --component) + --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2793,7 +2530,7 @@ devbox_status_parse_requirements() { key="$1" case "$key" in # :flag.case - --component) + --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2852,7 +2589,7 @@ devbox_restart_parse_requirements() { key="$1" case "$key" in # :flag.case - --component) + --component | -c) # :flag.case_arg if [[ -n ${2+x} ]]; then @@ -2864,7 +2601,7 @@ devbox_restart_parse_requirements() { exit 1 fi ;; - --freeleaps-endpoint) + --freeleaps-endpoint | -e) # :flag.case_arg if [[ -n ${2+x} ]]; then From 515e9ec1f54ba945995e73d068b1f4224f818144 Mon Sep 17 00:00:00 2001 From: Jet Li Date: Sat, 15 Feb 2025 17:37:47 -0800 Subject: [PATCH 21/32] Fix chat endpoint, fix jwt secret token --- devbox/devbox.local/devbox | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index c6d2c84..d730320 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1177,6 +1177,7 @@ if [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "true" ]]; then export FREELEAPS_AUTHENTICATION_ENDPOINT=http://localhost: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 @@ -1194,11 +1195,13 @@ else 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 JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0 export KAFKA_SERVER_URL='' export EMAIL_FROM=freeleaps@freeleaps.com EOFinner @@ -1311,6 +1314,15 @@ 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 [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "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 install npm update From a232587cdc9edee1bc2be29596945a2d97c4f86e Mon Sep 17 00:00:00 2001 From: Jet Li Date: Sat, 15 Feb 2025 17:39:21 -0800 Subject: [PATCH 22/32] Merge from latest --- devbox/devbox.local/devbox | 1 - 1 file changed, 1 deletion(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index d9d4c9e..747f30b 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -943,7 +943,6 @@ else 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 JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0 export KAFKA_SERVER_URL='' export EMAIL_FROM=freeleaps@freeleaps.com export JWT_SECRET_KEY=ea84edf152976b2fcec12b78aa8e45bc26a5cf0ef61bf16f5c317ae33b3fd8b0 From 76fa411edecbc19d7c5b19f1030c580d0f10f2e3 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Sun, 16 Feb 2025 22:26:56 +0800 Subject: [PATCH 23/32] Update for fix lower command invalid invoke in devbox container to load .env info --- devbox/devbox.local/devbox | 41 ++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index e320d5d..fb06441 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -535,8 +535,14 @@ devbox_init_command() { 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" == false ]]; then + if [[ $USE_LOCAL_COMPONENT_VAL == false ]]; then is_pull_all_components=false fi @@ -779,12 +785,13 @@ devbox_init_command() { # 6. linbwang: pull and start other components # ------------------------------------------------------------------- -echo "==> [INIT] Starting Freeleaps services... Use Local component $USE_LOCAL_COMPONENT" +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 -if [[ "$(lower "$USE_LOCAL_COMPONENT")" == "true" ]]; then echo ' ===> Using local components for Freeleaps services.' export DEVSVC_IMAGE_TAG="$DEVSVC_TAG" @@ -868,15 +875,23 @@ if ! git ls-remote "https://$FREELEAPS_USERNAME:$FREELEAPS_PASSWORD@freeleaps.co 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" - FRONTEND_GIT_URL="https://$FREELEAPS_USERNAME:$FREELEAPS_PASSWORD@freeleaps.com:3443/products/freeleaps.git" git clone --depth 5 $FRONTEND_GIT_URL else pushd $WORKING_HOME/freeleaps - echo "Git pulling freeleaps.com:3443/products/freeleaps.git" - git pull + # 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 @@ -885,7 +900,7 @@ docker exec -i "$DEVBOX_NAME" bash < Using local components" # Local components for Freeleaps services (devsvc, notification, content, central_storage, authentication) cat << 'EOFinner' > /home/.devbox/freeleaps/apps/.env @@ -922,6 +937,7 @@ if [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "true" ]]; then 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 @@ -1157,6 +1173,7 @@ devbox_deinit_command() { echo "==> Deleting source repository at $WORKING_HOME/freeleaps" sudo chown -R $(whoami):$(whoami) "$WORKING_HOME/freeleaps" rm -rf "$WORKING_HOME/freeleaps" + rmdir "$WORKING_HOME" 2>/dev/null || true else echo "==> Skipping repository deletion." fi @@ -1250,6 +1267,14 @@ devbox_start_command() { 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 From 5f3b149e596a82b528e4e20293cfd57e0e0424bb Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Sun, 16 Feb 2025 22:46:48 +0800 Subject: [PATCH 24/32] Update for init competed echo information --- devbox/devbox.local/devbox | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 02aff5e..df9b369 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -851,15 +851,25 @@ 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 "==> [INIT] DevBox environment initialization completed." + 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." @@ -875,8 +885,6 @@ if ! git ls-remote "https://$FREELEAPS_USERNAME:$FREELEAPS_PASSWORD@freeleaps.co 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 @@ -1138,10 +1146,13 @@ EOF 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 " Repository cloned to: $WORKING_HOME/freeleaps" - echo " Backend logs: $WORKING_HOME/logs/backend.logs" - echo " Frontend logs: $WORKING_HOME/logs/frontend.logs" echo " Backend PID: $WORKING_HOME/.backend.pid" echo " Frontend PID: $WORKING_HOME/.frontend.pid" echo "===========================================================" From 9b2c91473622dca2b0b7746064936797875be5f0 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Wed, 19 Feb 2025 01:28:13 +0800 Subject: [PATCH 25/32] Update for change local component service config url and update frontend init commands --- devbox/devbox.local/devbox | 66 +++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index df9b369..7624962 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -935,11 +935,11 @@ if [[ \$USE_LOCAL_COMPONENT_VAL == true ]]; then export STRIPE_WEBHOOK_SECRET=whsec_S6ZWjSAdR5Cpsn2USH6ZRBqbdBIENjTC export STRIPE_ACCOUNT_WEBHOOK_SECRET=whsec_PgPnkWGhEUiQfnV8aIb5Wmruz7XETJLm export SITE_URL_ROOT=http://localhost - export FREELEAPS_DEVSVC_ENDPOINT=http://localhost:8007/api/devsvc/ - export FREELEAPS_CONTENT_ENDPOINT=http://localhost:8013/api/content/ - export FREELEAPS_NOTIFICATION_ENDPOINT=http://localhost:8003/api/notification/ - export FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://localhost:8005/api/central_storage/ - export FREELEAPS_AUTHENTICATION_ENDPOINT=http://localhost:8004/api/auth/ + export FREELEAPS_DEVSVC_ENDPOINT=http://devsvc:8007/api/devsvc/ + 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 @@ -1089,13 +1089,25 @@ else export VITE_PROXY_API_CHAT_URL='https://freeleaps-alpha.com' fi -npm install npm update +# 1️⃣ Install pnpm globally npm install -g pnpm -pnpm install + +# 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 & @@ -1181,25 +1193,6 @@ devbox_deinit_command() { echo "==> Starting DevBox deinitialization..." - # Clear the DevBox container logs - if [[ "$CLEAR_LOGS" == "true" ]]; then - echo "==> Clearing logs in $WORKING_HOME/logs..." - sudo chown -R $(whoami):$(whoami) "$WORKING_HOME/logs" - rm -rf "$WORKING_HOME/logs"/* 2>/dev/null || true - 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" - rmdir "$WORKING_HOME" 2>/dev/null || true - else - echo "==> Skipping repository deletion." - fi - # Stop and remove DevBox container if [[ -f "$WORKING_HOME/.devbox-instance" ]]; then local container_id @@ -1260,6 +1253,27 @@ devbox_deinit_command() { fi done + + # Clear the DevBox container logs + if [[ "$CLEAR_LOGS" == "true" ]]; then + echo "==> Clearing logs in $WORKING_HOME/logs..." + 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 + 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 + # Remove the use-local-component file rm -f "$WORKING_HOME/.use-local-component" From dd10b3b062ce836110b89eee08d190a7f9dc0dfe Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Wed, 19 Feb 2025 14:50:31 +0800 Subject: [PATCH 26/32] Update for gitea data initialize --- devbox/devbox.local/devbox | 112 ++++++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 7624962..6c46d9f 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -446,6 +446,43 @@ get_port() { echo "$port" } +check_jq() { + # 从参数中获取 OS 和 ARCH,默认值分别为 auto + local target_os target_arch + target_os="$(get_arg '--os' 'auto')" + target_arch="$(get_arg '--arch' 'auto')" + + if ! command -v jq >/dev/null 2>&1; then + echo "[INFO] 'jq' is not installed. Installing jq..." + case "$target_os" in + darwin) + if command -v brew >/dev/null 2>&1; then + brew install jq + else + echo "[ERROR] brew not found. Please install jq manually." + exit 1 + fi + ;; + linux | wsl2 | auto) + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get update && sudo apt-get install -y jq + elif command -v yum >/dev/null 2>&1; then + sudo yum install -y epel-release && sudo yum install -y jq + else + echo "[ERROR] apt-get or yum not found. Please install jq manually." + exit 1 + fi + ;; + *) + echo "[ERROR] Unsupported OS: $target_os" + exit 1 + ;; + esac + else + echo "[INFO] 'jq' is already installed." + fi +} + # :command.command_functions # :command.function devbox_init_command() { @@ -800,10 +837,39 @@ if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then export AUTHENTICATION_IMAGE_TAG="$AUTHENTICATION_TAG" export NOTIFICATION_IMAGE_TAG="$NOTIFICATION_TAG" - # 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) - # start component service from start_components array - docker-compose -f docker-compose.dev.arm64.new.yaml up -d mongodb rabbitmq gitea "${start_components[@]}" + # 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 + + + # Check if jq is installed, if not install it + check_jq + + # Get Gitea data volume mount point + GITEA_DATA=$(docker volume inspect devbox_freeleaps2-gitea-data | jq -r '.[0].Mountpoint') + + echo "Gitea data volume mount point: $GITEA_DATA" + 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" @@ -814,6 +880,7 @@ if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then 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}}") @@ -821,6 +888,44 @@ if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then done echo "${component} container created: $component_container_id" + + # Check if devbox_freeleaps2-gitea-data exists, if not create a docker volume for gitea data, if exists then remove it and create a new one + if docker volume ls | grep -q "devbox_freeleaps2-gitea-data"; then + docker volume rm devbox_freeleaps2-gitea-data + fi + + docker volume create devbox_freeleaps2-gitea-data || { + echo "ERROR: Failed to create volume devbox_freeleaps2-gitea-data." + exit 1 + } + + # Check volume created successfully + if ! docker volume ls | grep -q "devbox_freeleaps2-gitea-data"; then + echo "ERROR: Failed to create volume devbox_freeleaps2-gitea-data." + exit 1 + fi + + if [[ -d "${GITEA_DATA}/gitea" ]]; then + echo "Gitea data exist, skipping..." + else + echo "Gitea data not exist, copying..." + sudo rm -rf ${GITEA_DATA}/git + sudo rm -rf ${GITEA_DATA}/gitea + sudo rm -rf ${GITEA_DATA}/ssh + sudo mv data/git ${GITEA_DATA}/ + sudo mv data/gitea ${GITEA_DATA}/ + sudo mv data/ssh ${GITEA_DATA}/ + sudo chown -R freedev:freedev ${GITEA_DATA} + echo "Gitea data copying is done" + # Check if gitea data copied successfully + if [[ ! -d "${GITEA_DATA}/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 gitea + fi else echo '============================================' echo ' ===> Using online components for Freeleaps services.' @@ -902,6 +1007,7 @@ else fi fi + # Run banckend service and frontend service in the container docker exec -i "$DEVBOX_NAME" bash < Date: Wed, 19 Feb 2025 15:33:15 +0800 Subject: [PATCH 27/32] Update for fix lower func cannot be used in EOF --- devbox/devbox.local/devbox | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index 6c46d9f..ffda6c6 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1187,7 +1187,7 @@ pushd /home/.devbox/freeleaps/frontend export VITE_API_URL='http://127.0.0.1:8002' export VITE_WEBSOCKET_URL='http://127.0.0.1:8002' -if [[ "\$(lower "\$USE_LOCAL_COMPONENT")" == "true" ]]; then +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 @@ -1363,9 +1363,11 @@ devbox_deinit_command() { # Clear the DevBox container logs if [[ "$CLEAR_LOGS" == "true" ]]; then echo "==> Clearing logs in $WORKING_HOME/logs..." - 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 + 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 @@ -1380,10 +1382,26 @@ devbox_deinit_command() { 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." + echo "> DevBox deinitialization completed." } # :command.function From 1e25c6452b898cd26fd9d4260d6b8ff9670500e5 Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Thu, 20 Feb 2025 17:35:39 +0800 Subject: [PATCH 28/32] Update for fixing gitea startup blocing signin issue --- devbox/devbox.local/devbox | 119 +++++++++++++------------------------ 1 file changed, 42 insertions(+), 77 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index ffda6c6..ffde28b 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -446,43 +446,6 @@ get_port() { echo "$port" } -check_jq() { - # 从参数中获取 OS 和 ARCH,默认值分别为 auto - local target_os target_arch - target_os="$(get_arg '--os' 'auto')" - target_arch="$(get_arg '--arch' 'auto')" - - if ! command -v jq >/dev/null 2>&1; then - echo "[INFO] 'jq' is not installed. Installing jq..." - case "$target_os" in - darwin) - if command -v brew >/dev/null 2>&1; then - brew install jq - else - echo "[ERROR] brew not found. Please install jq manually." - exit 1 - fi - ;; - linux | wsl2 | auto) - if command -v apt-get >/dev/null 2>&1; then - sudo apt-get update && sudo apt-get install -y jq - elif command -v yum >/dev/null 2>&1; then - sudo yum install -y epel-release && sudo yum install -y jq - else - echo "[ERROR] apt-get or yum not found. Please install jq manually." - exit 1 - fi - ;; - *) - echo "[ERROR] Unsupported OS: $target_os" - exit 1 - ;; - esac - else - echo "[INFO] 'jq' is already installed." - fi -} - # :command.command_functions # :command.function devbox_init_command() { @@ -852,14 +815,8 @@ if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then exit 1 fi - - # Check if jq is installed, if not install it - check_jq - # Get Gitea data volume mount point - GITEA_DATA=$(docker volume inspect devbox_freeleaps2-gitea-data | jq -r '.[0].Mountpoint') - echo "Gitea data volume mount point: $GITEA_DATA" 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) @@ -889,42 +846,49 @@ if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then echo "${component} container created: $component_container_id" - # Check if devbox_freeleaps2-gitea-data exists, if not create a docker volume for gitea data, if exists then remove it and create a new one - if docker volume ls | grep -q "devbox_freeleaps2-gitea-data"; then - docker volume rm devbox_freeleaps2-gitea-data - fi - - docker volume create devbox_freeleaps2-gitea-data || { - echo "ERROR: Failed to create volume devbox_freeleaps2-gitea-data." - exit 1 - } - - # Check volume created successfully - if ! docker volume ls | grep -q "devbox_freeleaps2-gitea-data"; then - echo "ERROR: Failed to create volume devbox_freeleaps2-gitea-data." - exit 1 - fi - - if [[ -d "${GITEA_DATA}/gitea" ]]; then - echo "Gitea data exist, skipping..." + # Get the owner group of the WORKING_HOME + if [[ "$(uname)" == "Darwin" ]]; then + OWNER_GROUP=$(stat -f "%Su:%Sg" "${WORKING_HOME}") else - echo "Gitea data not exist, copying..." - sudo rm -rf ${GITEA_DATA}/git - sudo rm -rf ${GITEA_DATA}/gitea - sudo rm -rf ${GITEA_DATA}/ssh - sudo mv data/git ${GITEA_DATA}/ - sudo mv data/gitea ${GITEA_DATA}/ - sudo mv data/ssh ${GITEA_DATA}/ - sudo chown -R freedev:freedev ${GITEA_DATA} - echo "Gitea data copying is done" - # Check if gitea data copied successfully - if [[ ! -d "${GITEA_DATA}/gitea" ]]; then - echo "ERROR: Failed to copy gitea data." - exit 1 - fi + OWNER_GROUP=$(stat -c "%U:%G" "${WORKING_HOME}") + fi - # restart gitea container - docker-compose -f docker-compose.dev.arm64.new.yaml restart gitea + # 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 '============================================' @@ -956,6 +920,7 @@ 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 From 4c7974ae8b746ff4dbdedcbd79c2d6abfb6b147d Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Fri, 21 Feb 2025 00:24:56 +0800 Subject: [PATCH 29/32] Update for start and restart logic --- devbox/devbox.local/devbox | 186 +++++++++++++++++++++++++------------ 1 file changed, 128 insertions(+), 58 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index ffde28b..218ecca 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -1472,29 +1472,36 @@ devbox_start_command() { # Start the backend and frontend services echo "Starting backend and frontend services..." -# Check if /home/.devbox/.backend.pid exists -if [ ! -f /home/.devbox/.backend.pid ]; then - echo "ERROR: Backend service is not running. Please run 'devbox init' first." - exit 1 -fi - -# Check if /home/.devbox/.frontend.pid exists -if [ ! -f /home/.devbox/.frontend.pid ]; then - echo "ERROR: Frontend service is not running. Please run 'devbox init' first." - exit 1 -fi - # Start the backend service echo '============================================' -echo ' Start to run start_webapi.sh' +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-frontend-port) -uvicorn webapi.main:app --reload --host 0.0.0.0 --port \$SERVICE_API_ACCESS_PORT > /home/.devbox/logs/backend.logs 2>&1 & +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. @@ -1507,6 +1514,28 @@ if ! ps -p "\$BACKEND_PID" &>/dev/null; then 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 '============================================' @@ -1526,23 +1555,29 @@ if ! ps -p "\$FRONTEND_PID" &>/dev/null; then exit 1 fi -# Test backend and frontend services -echo "Testing backend and frontend services..." - -# Test the backend service -echo "Testing backend service..." -curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$SERVICE_API_ACCESS_PORT/docs" -if [ "\$?" -ne 0 ]; then - echo "ERROR: Backend service is not available." - exit 1 -fi # Test the frontend service -echo "Testing frontend service..." -curl -s -o /dev/null -w "%{http_code}" "http://localhost:5173/" -if [ "\$?" -ne 0 ]; then - echo "ERROR: Frontend service is not available." +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 @@ -1826,29 +1861,38 @@ if [[ "$FREELEAPS_ENDPOINT" == "true" ]]; then # Start the backend and frontend services echo "Starting backend and frontend services..." -# Check if /home/.devbox/.backend.pid exists -if [ ! -f /home/.devbox/.backend.pid ]; then - echo "ERROR: Backend service is not running. Please run 'devbox init' first." - exit 1 -fi - -# Check if /home/.devbox/.frontend.pid exists -if [ ! -f /home/.devbox/.frontend.pid ]; then - echo "ERROR: Frontend service is not running. Please run 'devbox init' first." - exit 1 -fi - # Start the backend service echo '============================================' -echo ' Start to run start_webapi.sh' +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-frontend-port) -uvicorn webapi.main:app --reload --host 0.0.0.0 --port \$SERVICE_API_ACCESS_PORT > /home/.devbox/logs/backend.logs 2>&1 & +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. @@ -1861,6 +1905,28 @@ if ! ps -p "\$BACKEND_PID" &>/dev/null; then 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 '============================================' @@ -1880,23 +1946,27 @@ if ! ps -p "\$FRONTEND_PID" &>/dev/null; then exit 1 fi -# Test backend and frontend services -echo "Testing backend and frontend services..." - -# Test the backend service -echo "Testing backend service..." -curl -s -o /dev/null -w "%{http_code}" "http://localhost:\$SERVICE_API_ACCESS_PORT/docs" -if [ "\$?" -ne 0 ]; then - echo "ERROR: Backend service is not available." - exit 1 -fi # Test the frontend service +WEB_APP_ACCESS_PORT=\$(cat /home/.devbox/.devbox-frontend-port) echo "Testing frontend service..." -curl -s -o /dev/null -w "%{http_code}" "http://localhost:5173/" -if [ "\$?" -ne 0 ]; then - echo "ERROR: Frontend service is not available." +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 From 7976b5b7aa271c674ae453129d7f113391b1eb69 Mon Sep 17 00:00:00 2001 From: Jet Li Date: Thu, 20 Feb 2025 22:56:28 -0800 Subject: [PATCH 30/32] Add new chat service docker --- .../docker-compose.dev.arm64.new.yaml | 46 ++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/devbox/devbox.local/docker-compose.dev.arm64.new.yaml b/devbox/devbox.local/docker-compose.dev.arm64.new.yaml index 5667519..23543d6 100644 --- a/devbox/devbox.local/docker-compose.dev.arm64.new.yaml +++ b/devbox/devbox.local/docker-compose.dev.arm64.new.yaml @@ -10,8 +10,52 @@ services: environment: - DISABLE_REGISTRATION=true - REQUIRE_SIGNIN_VIEW=true + volumes: ${WORKING_HOME}/freeleaps2-gitea:/data:Z + networks: + - devbox_freeleaps2-network + + chat: + container_name: freeleaps-chat + build: + context: ${WORKING_HOME}/freeleaps/apps + dockerfile: ${WORKING_HOME}/freeleaps/dockers/dev.chat.Dockerfile + args: + CONTAINER_APP_ROOT: app + restart: always + ports: + - 8012:8012 + environment: + - CERT_PATH=/app/certs + - EMAIL_FROM=freeleaps@freeleaps.com + - MONGODB_NAME=freeleaps2 + - MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ + - SITE_URL_ROOT=http://localhost + - JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b + - JWT_ALGORITHM=HS256 + - RABBITMQ_HOST=freeleaps2-rabbitmq + - RABBITMQ_PORT=5672 + - LOG_BASE_PATH=/app/log/freeleaps-chat + - BACKEND_LOG_FILE_NAME=freeleaps-chat.log + - APPLICATION_ACTIVITY_LOG=freeleaps-chat.application.log + - FREELEAPS_ENV=dev + - FREELEAPS_CHAT_ENDPOINT=http://freeleaps-alpha.com/api/chat/ + - FREELEAPS_DEVSVC_ENDPOINT=http://devsvc:8007/api/devsvc/ + - FREELEAPS_CONTENT_ENDPOINT=http://content:8013/api/content/ + - FREELEAPS_NOTIFICATION_ENDPOINT=http://notification:8003/api/notification/ + - FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://central_storage:8005/api/central_storage/ + - FREELEAPS_AUTHENTICATION_ENDPOINT=http://authentication:8004/api/auth/ + - CERT_PATH=/app/certs + - TZ=Asia/Shanghai + command: + # Use a conditional check for RabbitMQ in alpha profile + - /bin/sh + - -c + - | + uvicorn chat.main:app --reload --host=0.0.0.0 --port=8012 --workers 4 --timeout-keep-alive 120 --log-level info volumes: - - ${WORKING_HOME}/freeleaps2-gitea/:/data + - type: bind + source: ${WORKING_HOME}/freeleaps/apps + target: /app/log/freeleaps-chat networks: - devbox_freeleaps2-network From b0607c6c8fc9f830834e57ef3d9cde4ed4d503bf Mon Sep 17 00:00:00 2001 From: Jet Li Date: Thu, 20 Feb 2025 22:57:56 -0800 Subject: [PATCH 31/32] Pull from master --- devbox/devbox.local/devbox | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/devbox index ffda6c6..3e9be69 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/devbox @@ -909,16 +909,17 @@ if [[ $USE_LOCAL_COMPONENT_VAL == true ]]; then echo "Gitea data exist, skipping..." else echo "Gitea data not exist, copying..." - sudo rm -rf ${GITEA_DATA}/git - sudo rm -rf ${GITEA_DATA}/gitea - sudo rm -rf ${GITEA_DATA}/ssh - sudo mv data/git ${GITEA_DATA}/ - sudo mv data/gitea ${GITEA_DATA}/ - sudo mv data/ssh ${GITEA_DATA}/ - sudo chown -R freedev:freedev ${GITEA_DATA} + sudo rm -rf ${WORKING_HOME}/freeleaps2-gitea/git + sudo rm -rf ${WORKING_HOME}/freeleaps2-gitea/gitea + sudo rm -rf ${WORKING_HOME}/freeleaps2-gitea/ssh + sudo mv data/git ${WORKING_HOME}/freeleaps2-gitea/ + sudo mv data/gitea ${WORKING_HOME}/freeleaps2-gitea/ + sudo mv data/ssh ${WORKING_HOME}/freeleaps2-gitea/ + sudo chown -R yaojing:staff ${WORKING_HOME}/freeleaps2-gitea + # sudo chmod -R 750 ${WORKING_HOME}/freeleaps2-gitea echo "Gitea data copying is done" # Check if gitea data copied successfully - if [[ ! -d "${GITEA_DATA}/gitea" ]]; then + if [[ ! -d "${WORKING_HOME}/freeleaps2-gitea/gitea" ]]; then echo "ERROR: Failed to copy gitea data." exit 1 fi @@ -1042,6 +1043,7 @@ if [[ \$USE_LOCAL_COMPONENT_VAL == true ]]; then 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/ From a24daf48620c96469931eeecf122c8957182070f Mon Sep 17 00:00:00 2001 From: Tianyong Qiu Date: Sun, 23 Feb 2025 23:22:37 +0800 Subject: [PATCH 32/32] Update for rename default devbox workspace name from .devbox to devbox --- devbox/devbox.local/{ => cli}/devbox | 95 ++++--- .../cli/docker-compose.dev.arm64.new.yaml | 266 ++++++++++++++++++ .../devbox.local/cli/gitea_data_backup.tar.gz | Bin 0 -> 46922 bytes .../docker-compose.dev.arm64.new.yaml | 3 +- 4 files changed, 317 insertions(+), 47 deletions(-) rename devbox/devbox.local/{ => cli}/devbox (97%) create mode 100644 devbox/devbox.local/cli/docker-compose.dev.arm64.new.yaml create mode 100644 devbox/devbox.local/cli/gitea_data_backup.tar.gz diff --git a/devbox/devbox.local/devbox b/devbox/devbox.local/cli/devbox similarity index 97% rename from devbox/devbox.local/devbox rename to devbox/devbox.local/cli/devbox index f123934..fe1ce9d 100644 --- a/devbox/devbox.local/devbox +++ b/devbox/devbox.local/cli/devbox @@ -79,7 +79,7 @@ devbox_init_usage() { 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 " --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" @@ -127,7 +127,7 @@ devbox_deinit_usage() { 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 " --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" @@ -466,7 +466,7 @@ devbox_init_command() { 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 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 @@ -508,7 +508,7 @@ devbox_init_command() { 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 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')" @@ -765,7 +765,7 @@ devbox_init_command() { -p "${DEVBOX_PORT}:22" \ -p "${DEVBOX_FRONTEND_PORT}:5173" \ -p "${DEVBOX_BACKEND_PORT}:8002" \ - -v "$WORKING_HOME:/home/.devbox" \ + -v "$WORKING_HOME:/home/devbox" \ -v /var/run/docker.sock:/var/run/docker.sock \ --network "$DEVBOX_FREELEAPS2_NETWORK" \ "$devbox_full_image" 2>/dev/null @@ -984,15 +984,15 @@ 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" +# 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 + cat << 'EOFinner' > /home/devbox/freeleaps/apps/.env # Online endpoint info export MONGODB_NAME=freeleaps2 export MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ @@ -1019,7 +1019,7 @@ if [[ \$USE_LOCAL_COMPONENT_VAL == true ]]; then EOFinner else echo "==> Using online components" - cat << 'EOFinner' > /home/.devbox/freeleaps/apps/.env + cat << 'EOFinner' > /home/devbox/freeleaps/apps/.env # Online endpoint info export MONGODB_NAME=freeleaps2 export MONGODB_PORT=27017 @@ -1047,15 +1047,15 @@ fi # Effect the environment variables in the current shell -source /home/.devbox/freeleaps/apps/.env +source /home/devbox/freeleaps/apps/.env -# Ensure /home/.devbox/logs exists -mkdir -p /home/.devbox/logs +# 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 +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" @@ -1089,7 +1089,7 @@ echo '============================================' echo ' Start to activate virtual environment' echo '============================================' source venv_t/bin/activate -source /home/.devbox/freeleaps/apps/.env +source /home/devbox/freeleaps/apps/.env # Verify the virtual environment is activated if [[ "\$VIRTUAL_ENV" != "" ]]; then @@ -1099,19 +1099,22 @@ else exit 1 fi + echo '============================================' echo ' Install requirements' echo '============================================' -pip install -r /home/.devbox/freeleaps/apps/requirements.txt +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 & +./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 "\$BACKEND_PID" > /home/devbox/.backend.pid echo '============================================' echo 'Check if the WebAPI service started successfully' @@ -1147,7 +1150,7 @@ fi echo '============================================' echo ' Start frontend service locally' echo '============================================' -pushd /home/.devbox/freeleaps/frontend +pushd /home/devbox/freeleaps/frontend # start the frontend service export VITE_API_URL='http://127.0.0.1:8002' @@ -1182,11 +1185,11 @@ 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 & +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 "\$FRONTEND_PID" > /home/devbox/.frontend.pid echo '============================================' @@ -1250,7 +1253,7 @@ 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 WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" local CLEAR_LOGS="$(get_arg '--clear-logs')" local CLEAR_REPO="$(get_arg '--clear-repo')" @@ -1374,7 +1377,7 @@ devbox_deinit_command() { devbox_start_command() { local COMPONENT="$(get_arg '--component')" - local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" local FREELEAPS_ENDPOINT="$(get_arg '--freeleaps-endpoint')" # Check if the DevBox container is running @@ -1478,7 +1481,7 @@ echo "Starting backend and frontend services..." echo '============================================' echo ' Start to run webapi.main:app ' echo '============================================' -pushd /home/.devbox/freeleaps/apps +pushd /home/devbox/freeleaps/apps # CHeck if the virtual environment is created if [ ! -f "venv_t/bin/activate" ]; then @@ -1490,7 +1493,7 @@ echo '============================================' echo ' Start to activate virtual environment' echo '============================================' source venv_t/bin/activate -source /home/.devbox/freeleaps/apps/.env +source /home/devbox/freeleaps/apps/.env # Verify the virtual environment is activated if [[ "\$VIRTUAL_ENV" != "" ]]; then @@ -1501,12 +1504,12 @@ else 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 & +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 +echo "\$BACKEND_PID" > /home/devbox/.backend.pid # Check if the backend service started successfully sleep 10 @@ -1542,12 +1545,12 @@ fi echo '============================================' echo ' Start frontend service locally' echo '============================================' -pushd /home/.devbox/freeleaps/frontend +pushd /home/devbox/freeleaps/frontend -npm run dev > /home/.devbox/logs/frontend.logs 2>&1 & +npm run dev > /home/devbox/logs/frontend.logs 2>&1 & FRONTEND_PID=\$! -echo "\$FRONTEND_PID" > /home/.devbox/.frontend.pid +echo "\$FRONTEND_PID" > /home/devbox/.frontend.pid # Check if the frontend service started successfully sleep 10 @@ -1559,7 +1562,7 @@ fi # Test the frontend service -WEB_APP_ACCESS_PORT=\$(cat /home/.devbox/.devbox-frontend-port) +WEB_APP_ACCESS_PORT=\$(cat /home/devbox/.devbox-frontend-port) echo "Testing frontend service... PORT: \$WEB_APP_ACCESS_PORT" attempt=0 @@ -1600,7 +1603,7 @@ fi devbox_stop_command() { echo "==> Stopping DevBox services..." local COMPONENT="$(get_arg '--component')" - local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" # If the DevBox container is not running, exit if [[ -z "$COMPONENT" ]]; then @@ -1658,7 +1661,7 @@ devbox_status_command() { echo "==> Checking DevBox services status..." local COMPONENT="$(get_arg '--component')" - local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" # Check if .devbox-instance file exists if [[ ! -f "${WORKING_HOME}/.devbox-instance" ]]; then @@ -1757,7 +1760,7 @@ devbox_status_command() { devbox_restart_command() { echo "==> Restarting DevBox services..." local COMPONENT="$(get_arg '--component')" - local WORKING_HOME="$(get_arg '--working-home' "${HOME}/.devbox")" + local WORKING_HOME="$(get_arg '--working-home' "${HOME}/devbox")" local FREELEAPS_ENDPOINT="$(get_arg '--freeleaps-endpoint')" # Check devbox container file path @@ -1868,7 +1871,7 @@ echo '============================================' echo ' Start to run webapi.main:app' echo '============================================' -pushd /home/.devbox/freeleaps/apps +pushd /home/devbox/freeleaps/apps # CHeck if the virtual environment is created @@ -1881,7 +1884,7 @@ echo '============================================' echo ' Start to activate virtual environment' echo '============================================' source venv_t/bin/activate -source /home/.devbox/freeleaps/apps/.env +source /home/devbox/freeleaps/apps/.env # Verify the virtual environment is activated if [[ "\$VIRTUAL_ENV" != "" ]]; then @@ -1892,12 +1895,12 @@ else 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 & +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 +echo "\$BACKEND_PID" > /home/devbox/.backend.pid # Check if the backend service started successfully sleep 10 @@ -1933,12 +1936,12 @@ fi echo '============================================' echo ' Start frontend service locally' echo '============================================' -pushd /home/.devbox/freeleaps/frontend +pushd /home/devbox/freeleaps/frontend -npm run dev > /home/.devbox/logs/frontend.logs 2>&1 & +npm run dev > /home/devbox/logs/frontend.logs 2>&1 & FRONTEND_PID=\$! -echo "\$FRONTEND_PID" > /home/.devbox/.frontend.pid +echo "\$FRONTEND_PID" > /home/devbox/.frontend.pid # Check if the frontend service started successfully sleep 10 @@ -1949,7 +1952,7 @@ fi # Test the frontend service -WEB_APP_ACCESS_PORT=\$(cat /home/.devbox/.devbox-frontend-port) +WEB_APP_ACCESS_PORT=\$(cat /home/devbox/.devbox-frontend-port) echo "Testing frontend service..." attempt=0 @@ -2484,7 +2487,7 @@ devbox_init_parse_requirements() { fi if [ -z "$(get_arg '--working-home')" ]; then - add_arg '--working-home' "${HOME}/.devbox" + add_arg '--working-home' "${HOME}/devbox" fi if [ -z "$(get_arg '--use-local-component')" ]; then @@ -2578,7 +2581,7 @@ devbox_deinit_parse_requirements() { # :command.default_assignments if [ -z "$(get_arg '--working-home')" ]; then - add_arg '--working-home' "${HOME}/.devbox" + add_arg '--working-home' "${HOME}/devbox" fi if [ -z "$(get_arg '--clear-logs')" ]; then add_arg '--clear-logs' "false" diff --git a/devbox/devbox.local/cli/docker-compose.dev.arm64.new.yaml b/devbox/devbox.local/cli/docker-compose.dev.arm64.new.yaml new file mode 100644 index 0000000..7c2861e --- /dev/null +++ b/devbox/devbox.local/cli/docker-compose.dev.arm64.new.yaml @@ -0,0 +1,266 @@ +services: + gitea: + # For apple chip, add: platform: linux/amd64 + container_name: freeleaps2-gitea + platform: linux/${ARCH:-arm64} + image: gitea/gitea:latest + restart: always + ports: + - "3000:3000" + environment: + - DISABLE_REGISTRATION=true + - REQUIRE_SIGNIN_VIEW=true + volumes: + - ${WORKING_HOME}/freeleaps2-gitea:/data:Z + networks: + - devbox_freeleaps2-network + + chat: + container_name: freeleaps-chat + build: + context: ${WORKING_HOME}/freeleaps/apps + dockerfile: ${WORKING_HOME}/freeleaps/dockers/dev.chat.Dockerfile + args: + CONTAINER_APP_ROOT: app + restart: always + ports: + - 8012:8012 + environment: + - CERT_PATH=/app/certs + - EMAIL_FROM=freeleaps@freeleaps.com + - MONGODB_NAME=freeleaps2 + - MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ + - SITE_URL_ROOT=http://localhost + - JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b + - JWT_ALGORITHM=HS256 + - RABBITMQ_HOST=freeleaps2-rabbitmq + - RABBITMQ_PORT=5672 + - LOG_BASE_PATH=/app/log/freeleaps-chat + - BACKEND_LOG_FILE_NAME=freeleaps-chat.log + - APPLICATION_ACTIVITY_LOG=freeleaps-chat.application.log + - FREELEAPS_ENV=dev + - FREELEAPS_CHAT_ENDPOINT=http://freeleaps-alpha.com/api/chat/ + - FREELEAPS_DEVSVC_ENDPOINT=http://devsvc:8007/api/devsvc/ + - FREELEAPS_CONTENT_ENDPOINT=http://content:8013/api/content/ + - FREELEAPS_NOTIFICATION_ENDPOINT=http://notification:8003/api/notification/ + - FREELEAPS_CENTRAL_STORAGE_ENDPOINT=http://central_storage:8005/api/central_storage/ + - FREELEAPS_AUTHENTICATION_ENDPOINT=http://authentication:8004/api/auth/ + - CERT_PATH=/app/certs + - TZ=Asia/Shanghai + command: + # Use a conditional check for RabbitMQ in alpha profile + - /bin/sh + - -c + - | + uvicorn chat.main:app --reload --host=0.0.0.0 --port=8012 --workers 4 --timeout-keep-alive 120 --log-level info + volumes: + - type: bind + source: ${WORKING_HOME}/freeleaps/apps + target: /app/log/freeleaps-chat + networks: + - devbox_freeleaps2-network + + mongodb: + # For apple chip, add: platform: linux/amd64 + # For apple chip, you may want to downgrade to public mongo:4.4 for log support + container_name: freeleaps2-mongodb + platform: linux/${ARCH:-arm64} + image: mongo:latest + restart: always + ports: + - "27017:27017" + volumes: + - ${WORKING_HOME}/freeleaps2-mongodb-data:/data/db + networks: + - devbox_freeleaps2-network + + rabbitmq: + # For apple chip, add: platform: linux/amd64 + platform: linux/${ARCH:-arm64} + container_name: freeleaps2-rabbitmq + image: rabbitmq:latest + restart: always + ports: + - "5672:5672" + - "15672:15672" + volumes: + - ${WORKING_HOME}/freeleaps2-rabbitmq-data:/var/lib/rabbitmq + networks: + - devbox_freeleaps2-network + + devsvc: + container_name: devsvc + image: freeleaps/devsvc:${DEVSVC_IMAGE_TAG:-latest-linux-arm64} + restart: always + environment: + - APP_NAME=devsvc + - SERVICE_API_ACCESS_HOST=localhost + - SERVICE_API_ACCESS_PORT=8007 + - MONGODB_NAME=freeleaps2 + - MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ + - GITEA_TOKEN=6786dc398b77d2a9c454b1943019425049deb667 + - GITEA_URL=http://freeleaps2-gitea:3000 + - CODE_DEPOT_HTTP_PORT=3443 + - CODE_DEPOT_SSH_PORT=22 + - CODE_DEPOT_DOMAIN_NAME=localhost + - RABBITMQ_HOST=freeleaps2-rabbitmq + - RABBITMQ_PORT=5672 + - LOG_BASE_PATH=/app/log/devsvc + - BACKEND_LOG_FILE_NAME=devsvc.log + - APPLICATION_ACTIVITY_LOG=devsvc-application.log + ports: + - 8007:8007 + command: + - /bin/sh + - -c + - | + uvicorn webapi.main:app --reload --port=8007 --host=0.0.0.0 + volumes: + - type: bind + source: ${WORKING_HOME}/logs/devsvc + target: /app/log/devsvc + networks: + - devbox_freeleaps2-network + + central_storage: + container_name: central_storage + image: freeleaps/central_storage:${CENTRAL_STORAGE_IMAGE_TAG:-latest-linux-arm64} + # profiles: [ prod, alpha, dev ] + platform: linux/${ARCH:-arm64} + restart: always + environment: + - APP_NAME=central_storage + - MONGODB_NAME=freeleaps2 + - MONGODB_PORT=27017 + - MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ + - SERVICE_API_ACCESS_HOST=localhost + - SERVICE_API_ACCESS_PORT=8005 + - AZURE_STORAGE_DOCUMENT_API_KEY=Cg+wtKpHt6Bo6bTUtjic40cRNRZ8NCI3JYBY69BNPgFJARVx/c18iVC7cASbJfzukyu4pksyhUep+AStFdpH7Q== + - AZURE_STORAGE_DOCUMENT_API_ENDPOINT=https://freeleaps1document.blob.core.windows.net/ + - LOG_BASE_PATH=/app/log/central_storage + - BACKEND_LOG_FILE_NAME=central_storage.log + - APPLICATION_ACTIVITY_LOG=central_storage-application.log + ports: + - 8005:8005 + command: + - /bin/sh + - -c + - | + uvicorn webapi.main:app --reload --port=8005 --host=0.0.0.0 + networks: + - devbox_freeleaps2-network + volumes: + - type: bind + source: ${WORKING_HOME}/logs/central_storage + target: /app/log/central_storage + + authentication: + container_name: authentication + image: freeleaps/authentication:${AUTHENTICATION_IMAGE_TAG:-latest-linux-arm64} + # profiles: [ prod, alpha, dev ] + platform: linux/${ARCH:-arm64} + restart: always + environment: + - APP_NAME=authentication + - MONGODB_NAME=freeleaps2 + - MONGODB_PORT=27017 + - MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ + - SERVICE_API_ACCESS_HOST=localhost + - SERVICE_API_ACCESS_PORT=8004 + - LOG_BASE_PATH=/app/log/authentication + - BACKEND_LOG_FILE_NAME=authentication.log + - APPLICATION_ACTIVITY_LOG=authentication-application.log + - NOTIFICATION_WEBAPI_URL_BASE=http://notification:8003/api/notification/ + - DEVSVC_WEBAPI_URL_BASE=http://devsvc:8007/api/devsvc + - JWT_SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b + - JWT_ALGORITHM=HS256 + ports: + - 8004:8004 + command: + - /bin/sh + - -c + - | + uvicorn webapi.main:app --reload --port=8004 --host=0.0.0.0 + networks: + - devbox_freeleaps2-network + volumes: + - type: bind + source: ${WORKING_HOME}/logs/authentication + target: /app/log/authentication + + content: + container_name: content + image: freeleaps/content:${CONTENT_IMAGE_TAG:-latest-linux-arm64} + # profiles: [ prod, alpha, dev ] + platform: linux/${ARCH:-arm64} + restart: always + environment: + - APP_NAME=content + - SERVICE_API_ACCESS_HOST=localhost + - SERVICE_API_ACCESS_PORT=8013 + - MONGODB_NAME=freeleaps2 + - MONGODB_URI=mongodb://freeleaps2-mongodb:27017/ + - FREELEAPS_WWW_AS_AZURE_CLIENT_SECRET=jTu8Q~WceiK-5dfZB44Ww-K4fVGi3_5tHNWYYbdX + - CENTRAL_STORAGE_WEBAPI_URL_BASE=http://central_storage:8005/api/central_storage/ + - LOG_BASE_PATH=/app/log/content + - BACKEND_LOG_FILE_NAME=content.log + - APPLICATION_ACTIVITY_LOG=content-application.log + ports: + - 8013:8013 + command: + - /bin/sh + - -c + - | + uvicorn webapi.main:app --reload --port=8013 --host=0.0.0.0 + networks: + - devbox_freeleaps2-network + volumes: + - type: bind + source: ${WORKING_HOME}/logs/content + target: /app/log/content + + notification: + container_name: notification + image: freeleaps/notification:${NOTIFICATION_IMAGE_TAG:-latest-linux-arm64} + # profiles: [ prod, alpha, dev ] + platform: linux/${ARCH:-arm64} + restart: always + environment: + - APP_NAME=notification + - SERVICE_API_ACCESS_HOST=localhost + - SERVICE_API_ACCESS_PORT=8003 + - RABBITMQ_HOST=freeleaps2-rabbitmq + - RABBITMQ_PORT=5672 + - SYSTEM_USER_ID=117f191e810c19729de860aa + - SMS_FROM=+16898887156 + - EMAIL_FROM=freeleaps@freeleaps.com + - SECRET_KEY=8f87ca8c3c9c3df09a9c78e0adb0927855568f6072d9efc892534aee35f5867b + - SENDGRID_API_KEY=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 + - TWILIO_ACCOUNT_SID=ACf8c9283a6acda060258eadb29be58bc8 + - TWILIO_AUTH_TOKEN=120165c0550111ddfd58efc97dafc2fe + - LOG_BASE_PATH=/app/log/notification + - BACKEND_LOG_FILE_NAME=notification.log + - APPLICATION_ACTIVITY_LOG=notification-application.log + ports: + - 8003:8003 + command: + - /bin/sh + - -c + - | + uvicorn webapi.main:app --reload --port=8003 --host=0.0.0.0 + networks: + - devbox_freeleaps2-network + volumes: + - type: bind + source: ${WORKING_HOME}/logs/notification + target: /app/log/notification + +volumes: + freeleaps2-mongodb-data: + freeleaps2-gitea-data: + freeleaps2-rabbitmq-data: + + +networks: + devbox_freeleaps2-network: + external: true diff --git a/devbox/devbox.local/cli/gitea_data_backup.tar.gz b/devbox/devbox.local/cli/gitea_data_backup.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..550787f1b0cb5fc5e3a94bf4ec29914d0d8ba3e9 GIT binary patch literal 46922 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

$mR%qvd*C#3U-Nob>b_yR%5L&yQ|~$vWbryd3ru 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Ð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