diff --git a/registry/mavrickrishi/README.md b/registry/mavrickrishi/README.md index f4326ac39..c2f53d986 100644 --- a/registry/mavrickrishi/README.md +++ b/registry/mavrickrishi/README.md @@ -19,3 +19,4 @@ participating in LFX CNCF programs, and helping the developer community grow. ## Modules - **aws-ami-snapshot**: Create and manage AMI snapshots for Coder workspaces with restore capabilities +- [auto-start-dev-server](modules/auto-start-dev-server/README.md) - Automatically detect and start development servers for various project types diff --git a/registry/mavrickrishi/modules/auto-start-dev-server/README.md b/registry/mavrickrishi/modules/auto-start-dev-server/README.md new file mode 100644 index 000000000..d569dac60 --- /dev/null +++ b/registry/mavrickrishi/modules/auto-start-dev-server/README.md @@ -0,0 +1,146 @@ +--- +display_name: Auto-Start Development Servers +description: Automatically detect and start development servers for various project types +icon: ../../../../.icons/server.svg +verified: false +tags: [development, automation, servers] +--- + +# Auto-Start Development Servers + +Automatically detect and start development servers for various project types when a workspace starts. This module scans your workspace for common project structures and starts the appropriate development servers in the background without manual intervention. + +```tf +module "auto_start_dev_servers" { + source = "registry.coder.com/mavrickrishi/auto-start-dev-server/coder" + version = "1.0.0" + agent_id = coder_agent.main.id +} +``` + +## Features + +- **Multi-language support**: Detects and starts servers for Node.js, Python (Django/Flask), Ruby (Rails), Java (Spring Boot), Go, PHP, Rust, and .NET projects +- **Smart script prioritization**: Prioritizes `dev` scripts over `start` scripts for better development experience +- **Intelligent frontend detection**: Automatically identifies frontend projects (React, Vue, Angular, Next.js, Nuxt, Svelte, Vite) and prioritizes them for preview apps +- **Devcontainer integration**: Respects custom start commands defined in `.devcontainer/devcontainer.json` +- **Configurable scanning**: Adjustable directory scan depth and project type toggles +- **Non-blocking startup**: Servers start in the background with configurable startup delay +- **Comprehensive logging**: All server output and detection results logged to a central file +- **Smart detection**: Uses project-specific files and configurations to identify project types +- **Integrated live preview**: Automatically creates a preview app for the primary frontend project + +## Supported Project Types + +| Framework/Language | Detection Files | Start Commands (in priority order) | +| ------------------ | -------------------------------------------- | ----------------------------------------------------- | +| **Node.js/npm** | `package.json` | `npm run dev`, `npm run serve`, `npm start` (or yarn) | +| **Ruby on Rails** | `Gemfile` with rails gem | `bundle exec rails server` | +| **Django** | `manage.py` | `python manage.py runserver` | +| **Flask** | `requirements.txt` with Flask | `python app.py/main.py/run.py` | +| **Spring Boot** | `pom.xml` or `build.gradle` with spring-boot | `mvn spring-boot:run`, `gradle bootRun` | +| **Go** | `go.mod` | `go run main.go` | +| **PHP** | `composer.json` | `php -S 0.0.0.0:8080` | +| **Rust** | `Cargo.toml` | `cargo run` | +| **.NET** | `*.csproj` | `dotnet run` | + +## Examples + +### Basic Usage + +```hcl +module "auto_start" { + source = "./modules/auto-start-dev-server" + agent_id = coder_agent.main.id +} +``` + +### Advanced Usage + +```hcl +module "auto_start_dev_servers" { + source = "./modules/auto-start-dev-server" + agent_id = coder_agent.main.id + + # Optional: Configure which project types to detect + enable_npm = true + enable_rails = true + enable_django = true + enable_flask = true + enable_spring_boot = true + enable_go = true + enable_php = true + enable_rust = true + enable_dotnet = true + + # Optional: Enable devcontainer.json integration + enable_devcontainer = true + + # Optional: Workspace directory to scan (supports environment variables) + workspace_directory = "$HOME" + + # Optional: Directory scan depth (1-5) + scan_depth = 2 + + # Optional: Startup delay in seconds + startup_delay = 10 + + # Optional: Log file path + log_path = "/tmp/dev-servers.log" + + # Optional: Enable automatic preview app (default: true) + enable_preview_app = true +} +``` + +### Disable Preview App + +```hcl +module "auto_start" { + source = "./modules/auto-start-dev-server" + agent_id = coder_agent.main.id + + # Disable automatic preview app creation + enable_preview_app = false +} +``` + +### Selective Project Types + +```hcl +module "auto_start" { + source = "./modules/auto-start-dev-server" + agent_id = coder_agent.main.id + + # Only enable web development projects + enable_npm = true + enable_rails = true + enable_django = true + enable_flask = true + + # Disable other project types + enable_spring_boot = false + enable_go = false + enable_php = false + enable_rust = false + enable_dotnet = false +} +``` + +### Deep Workspace Scanning + +```hcl +module "auto_start" { + source = "./modules/auto-start-dev-server" + agent_id = coder_agent.main.id + + workspace_directory = "/workspaces" + scan_depth = 3 + startup_delay = 5 + log_path = "/var/log/dev-servers.log" +} +``` + +## License + +This module is provided under the same license as the Coder Registry. diff --git a/registry/mavrickrishi/modules/auto-start-dev-server/main.test.ts b/registry/mavrickrishi/modules/auto-start-dev-server/main.test.ts new file mode 100644 index 000000000..058931945 --- /dev/null +++ b/registry/mavrickrishi/modules/auto-start-dev-server/main.test.ts @@ -0,0 +1,109 @@ +import { describe, expect, it } from "bun:test"; +import { + runTerraformApply, + runTerraformInit, + testRequiredVariables, +} from "~test"; + +describe("auto-start-dev-server", async () => { + await runTerraformInit(import.meta.dir); + + testRequiredVariables(import.meta.dir, { + agent_id: "test-agent-123", + }); + + it("validates scan_depth range", () => { + const t1 = async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + scan_depth: "0", + }); + }; + expect(t1).toThrow("Scan depth must be between 1 and 5"); + + const t2 = async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + scan_depth: "6", + }); + }; + expect(t2).toThrow("Scan depth must be between 1 and 5"); + }); + + it("applies successfully with default values", async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + }); + }); + + it("applies successfully with all project types enabled", async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + enable_npm: "true", + enable_rails: "true", + enable_django: "true", + enable_flask: "true", + enable_spring_boot: "true", + enable_go: "true", + enable_php: "true", + enable_rust: "true", + enable_dotnet: "true", + enable_devcontainer: "true", + }); + }); + + it("applies successfully with all project types disabled", async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + enable_npm: "false", + enable_rails: "false", + enable_django: "false", + enable_flask: "false", + enable_spring_boot: "false", + enable_go: "false", + enable_php: "false", + enable_rust: "false", + enable_dotnet: "false", + enable_devcontainer: "false", + }); + }); + + it("applies successfully with custom configuration", async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + workspace_directory: "/custom/workspace", + scan_depth: "3", + startup_delay: "5", + log_path: "/var/log/custom-dev-servers.log", + display_name: "Custom Dev Server Startup", + }); + }); + + it("validates scan_depth boundary values", async () => { + // Test valid boundary values + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + scan_depth: "1", + }); + + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + scan_depth: "5", + }); + }); + + it("applies with selective project type configuration", async () => { + await runTerraformApply(import.meta.dir, { + agent_id: "test-agent-123", + enable_npm: "true", + enable_django: "true", + enable_go: "true", + enable_rails: "false", + enable_flask: "false", + enable_spring_boot: "false", + enable_php: "false", + enable_rust: "false", + enable_dotnet: "false", + }); + }); +}); diff --git a/registry/mavrickrishi/modules/auto-start-dev-server/main.tf b/registry/mavrickrishi/modules/auto-start-dev-server/main.tf new file mode 100644 index 000000000..e4227ee79 --- /dev/null +++ b/registry/mavrickrishi/modules/auto-start-dev-server/main.tf @@ -0,0 +1,195 @@ +terraform { + required_version = ">= 1.0" + + required_providers { + coder = { + source = "coder/coder" + version = ">= 2.5" + } + } +} + +variable "agent_id" { + type = string + description = "The ID of a Coder agent." +} + +variable "workspace_directory" { + type = string + description = "The directory to scan for development projects." + default = "$HOME" +} + +variable "project_detection" { + type = bool + description = "Enable automatic project detection for all supported types. When true, all project types are detected unless individually disabled. When false, only explicitly enabled project types are detected." + default = true +} + +variable "enable_npm" { + type = bool + description = "Enable auto-detection and startup of npm projects." + default = null +} + +variable "enable_rails" { + type = bool + description = "Enable auto-detection and startup of Rails projects." + default = null +} + +variable "enable_django" { + type = bool + description = "Enable auto-detection and startup of Django projects." + default = null +} + +variable "enable_flask" { + type = bool + description = "Enable auto-detection and startup of Flask projects." + default = null +} + +variable "enable_spring_boot" { + type = bool + description = "Enable auto-detection and startup of Spring Boot projects." + default = null +} + +variable "enable_go" { + type = bool + description = "Enable auto-detection and startup of Go projects." + default = null +} + +variable "enable_php" { + type = bool + description = "Enable auto-detection and startup of PHP projects." + default = null +} + +variable "enable_rust" { + type = bool + description = "Enable auto-detection and startup of Rust projects." + default = null +} + +variable "enable_dotnet" { + type = bool + description = "Enable auto-detection and startup of .NET projects." + default = null +} + +variable "enable_devcontainer" { + type = bool + description = "Enable integration with devcontainer.json configuration." + default = null +} + +variable "log_path" { + type = string + description = "The path to log development server output to." + default = "/tmp/dev-servers.log" +} + +variable "scan_depth" { + type = number + description = "Maximum directory depth to scan for projects (1-5)." + default = 2 + validation { + condition = var.scan_depth >= 1 && var.scan_depth <= 5 + error_message = "Scan depth must be between 1 and 5." + } +} + +variable "startup_delay" { + type = number + description = "Delay in seconds before starting dev servers (allows other setup to complete)." + default = 10 +} + +variable "display_name" { + type = string + description = "Display name for the auto-start dev server script." + default = "Auto-Start Dev Servers" +} + +variable "enable_preview_app" { + type = bool + description = "Enable automatic creation of a preview app for the first detected project." + default = true +} + +# Read the detected port from the file written by the script +locals { + detected_port = var.enable_preview_app ? try(tonumber(trimspace(file("/tmp/detected-port.txt"))), 3000) : 3000 + # Attempt to read project information for better preview naming + detected_projects = try(jsondecode(file("/tmp/detected-projects.json")), []) + preview_project = length(local.detected_projects) > 0 ? local.detected_projects[0] : null +} + +resource "coder_script" "auto_start_dev_server" { + agent_id = var.agent_id + display_name = var.display_name + icon = "/icon/server.svg" + script = templatefile("${path.module}/run.sh", { + WORKSPACE_DIR = var.workspace_directory + ENABLE_NPM = coalesce(var.enable_npm, var.project_detection) + ENABLE_RAILS = coalesce(var.enable_rails, var.project_detection) + ENABLE_DJANGO = coalesce(var.enable_django, var.project_detection) + ENABLE_FLASK = coalesce(var.enable_flask, var.project_detection) + ENABLE_SPRING_BOOT = coalesce(var.enable_spring_boot, var.project_detection) + ENABLE_GO = coalesce(var.enable_go, var.project_detection) + ENABLE_PHP = coalesce(var.enable_php, var.project_detection) + ENABLE_RUST = coalesce(var.enable_rust, var.project_detection) + ENABLE_DOTNET = coalesce(var.enable_dotnet, var.project_detection) + ENABLE_DEVCONTAINER = coalesce(var.enable_devcontainer, var.project_detection) + LOG_PATH = var.log_path + SCAN_DEPTH = var.scan_depth + STARTUP_DELAY = var.startup_delay + }) + run_on_start = true +} + +# Create preview app for first detected project +resource "coder_app" "preview" { + count = var.enable_preview_app ? 1 : 0 + agent_id = var.agent_id + slug = "dev-preview" + display_name = "Live Preview" + url = "http://localhost:${local.detected_port}" + icon = "/icon/globe.svg" + subdomain = true + share = "owner" +} + +output "log_path" { + value = var.log_path + description = "Path to the log file for dev server output" +} + +# Example output values for common port mappings +output "common_ports" { + value = { + nodejs = 3000 + rails = 3000 + django = 8000 + flask = 5000 + spring = 8080 + go = 8080 + php = 8080 + rust = 8000 + dotnet = 5000 + } + description = "Common default ports for different project types" +} + +output "preview_url" { + value = var.enable_preview_app ? try(coder_app.preview[0].url, null) : null + description = "URL of the live preview app (if enabled)" +} + +output "detected_port" { + value = local.detected_port + description = "Port of the first detected development server" +} diff --git a/registry/mavrickrishi/modules/auto-start-dev-server/run.sh b/registry/mavrickrishi/modules/auto-start-dev-server/run.sh new file mode 100755 index 000000000..d0386e741 --- /dev/null +++ b/registry/mavrickrishi/modules/auto-start-dev-server/run.sh @@ -0,0 +1,468 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Color codes for output +BOLD='\033[0;1m' +GREEN='\033[0;32m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +RED='\033[0;31m' +RESET='\033[0m' + +echo -e "$${BOLD}🚀 Auto-Start Development Servers$${RESET}" +echo "Workspace Directory: ${WORKSPACE_DIR}" +echo "Log Path: ${LOG_PATH}" +echo "Scan Depth: ${SCAN_DEPTH}" + +# Wait for startup delay to allow other setup to complete +if [ "${STARTUP_DELAY}" -gt 0 ]; then + echo -e "$${YELLOW}⏳ Waiting ${STARTUP_DELAY} seconds for system initialization...$${RESET}" + sleep "${STARTUP_DELAY}" +fi + +# Initialize log file +echo "=== Auto-Start Dev Servers Log ===" > "${LOG_PATH}" +echo "Started at: $(date)" >> "${LOG_PATH}" + +# Initialize detected projects JSON file +DETECTED_PROJECTS_FILE="/tmp/detected-projects.json" +echo '[]' > "$DETECTED_PROJECTS_FILE" + +# Initialize detected port file for preview app +DETECTED_PORT_FILE="/tmp/detected-port.txt" +FIRST_PORT_DETECTED=false +FRONTEND_PROJECT_DETECTED=false + +# Function to log messages +log_message() { + echo -e "$1" + echo "$1" >> "${LOG_PATH}" +} + +# Function to determine if a project is likely a frontend project +is_frontend_project() { + local project_dir="$1" + local project_type="$2" + + # Check for common frontend indicators + if [ "$project_type" = "nodejs" ]; then + # Check package.json for frontend dependencies + if [ -f "$project_dir/package.json" ] && command -v jq &> /dev/null; then + # Check for common frontend frameworks + local has_react=$(jq '.dependencies.react // .devDependencies.react // empty' "$project_dir/package.json") + local has_vue=$(jq '.dependencies.vue // .devDependencies.vue // empty' "$project_dir/package.json") + local has_angular=$(jq '.dependencies["@angular/core"] // .devDependencies["@angular/core"] // empty' "$project_dir/package.json") + local has_next=$(jq '.dependencies.next // .devDependencies.next // empty' "$project_dir/package.json") + local has_nuxt=$(jq '.dependencies.nuxt // .devDependencies.nuxt // empty' "$project_dir/package.json") + local has_svelte=$(jq '.dependencies.svelte // .devDependencies.svelte // empty' "$project_dir/package.json") + local has_vite=$(jq '.dependencies.vite // .devDependencies.vite // empty' "$project_dir/package.json") + + if [ -n "$has_react" ] || [ -n "$has_vue" ] || [ -n "$has_angular" ] \ + || [ -n "$has_next" ] || [ -n "$has_nuxt" ] || [ -n "$has_svelte" ] \ + || [ -n "$has_vite" ]; then + return 0 # It's a frontend project + fi + fi + + # Check for common frontend directory structures + if [ -d "$project_dir/src/components" ] || [ -d "$project_dir/components" ] \ + || [ -d "$project_dir/pages" ] || [ -d "$project_dir/views" ] \ + || [ -f "$project_dir/index.html" ] || [ -f "$project_dir/public/index.html" ]; then + return 0 # It's likely a frontend project + fi + fi + + # Rails projects with webpack/webpacker are frontend-enabled + if [ "$project_type" = "rails" ]; then + if [ -f "$project_dir/config/webpacker.yml" ] || [ -f "$project_dir/webpack.config.js" ]; then + return 0 + fi + fi + + # Django projects with static/templates are frontend-enabled + if [ "$project_type" = "django" ]; then + if [ -d "$project_dir/static" ] || [ -d "$project_dir/templates" ]; then + return 0 + fi + fi + + return 1 # Not a frontend project +} + +# Function to add detected project to JSON +add_detected_project() { + local project_dir="$1" + local project_type="$2" + local port="$3" + local command="$4" + + # Check if this is a frontend project + local is_frontend=false + if is_frontend_project "$project_dir" "$project_type"; then + is_frontend=true + log_message "$${BLUE}🎨 Detected frontend project at $project_dir$${RESET}" + fi + + # Prioritize frontend projects for the preview app + # Set port if: 1) No port set yet, OR 2) This is frontend and no frontend detected yet + if [ "$FIRST_PORT_DETECTED" = false ] || ([ "$is_frontend" = true ] && [ "$FRONTEND_PROJECT_DETECTED" = false ]); then + echo "$port" > "$DETECTED_PORT_FILE" + FIRST_PORT_DETECTED=true + if [ "$is_frontend" = true ]; then + FRONTEND_PROJECT_DETECTED=true + log_message "$${BLUE}🎯 Frontend project detected - Preview app will be available on port $port$${RESET}" + else + log_message "$${BLUE}🎯 Project detected - Preview app will be available on port $port$${RESET}" + fi + fi + + # Create JSON entry for this project + local project_json=$(jq -n \ + --arg dir "$project_dir" \ + --arg type "$project_type" \ + --arg port "$port" \ + --arg cmd "$command" \ + --arg frontend "$is_frontend" \ + '{"directory": $dir, "type": $type, "port": $port, "command": $cmd, "is_frontend": ($frontend == "true")}') + + # Append to the detected projects file + jq ". += [$project_json]" "$DETECTED_PROJECTS_FILE" > "$DETECTED_PROJECTS_FILE.tmp" \ + && mv "$DETECTED_PROJECTS_FILE.tmp" "$DETECTED_PROJECTS_FILE" +} + +# Function to detect and start npm/yarn projects +detect_npm_projects() { + if [ "${ENABLE_NPM}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for Node.js/npm projects...$${RESET}" + + # Use find with maxdepth to respect scan depth + while IFS= read -r -d '' package_json; do + project_dir=$(dirname "$package_json") + log_message "$${GREEN}📦 Found Node.js project: $project_dir$${RESET}" + + cd "$project_dir" + + # Check package.json for start script + if [ -f "package.json" ] && command -v jq &> /dev/null; then + start_script=$(jq -r '.scripts.start // empty' package.json) + dev_script=$(jq -r '.scripts.dev // empty' package.json) + serve_script=$(jq -r '.scripts.serve // empty' package.json) + + # Determine port (check for common port configurations) + local project_port=3000 + if [ -n "$dev_script" ] && echo "$dev_script" | grep -q "\-\-port"; then + project_port=$(echo "$dev_script" | grep -oE "\-\-port[[:space:]]+[0-9]+" | grep -oE "[0-9]+$" || echo "3000") + fi + + # Use yarn if yarn.lock exists + local pkg_manager="npm" + local cmd_prefix="" + if [ -f "yarn.lock" ] && command -v yarn &> /dev/null; then + pkg_manager="yarn" + cmd_prefix="" + else + cmd_prefix="run " + fi + + # Prioritize scripts: 'dev' > 'serve' > 'start' for development environments + if [ -n "$dev_script" ]; then + if [ "$pkg_manager" = "yarn" ]; then + log_message "$${GREEN}🟢 Starting project with 'yarn dev' in $project_dir$${RESET}" + nohup yarn dev >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "nodejs" "$project_port" "yarn dev" + else + log_message "$${GREEN}🟢 Starting project with 'npm run dev' in $project_dir$${RESET}" + nohup npm run dev >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "nodejs" "$project_port" "npm run dev" + fi + elif [ -n "$serve_script" ]; then + if [ "$pkg_manager" = "yarn" ]; then + log_message "$${GREEN}🟢 Starting project with 'yarn serve' in $project_dir$${RESET}" + nohup yarn serve >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "nodejs" "$project_port" "yarn serve" + else + log_message "$${GREEN}🟢 Starting project with 'npm run serve' in $project_dir$${RESET}" + nohup npm run serve >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "nodejs" "$project_port" "npm run serve" + fi + elif [ -n "$start_script" ]; then + if [ "$pkg_manager" = "yarn" ]; then + log_message "$${GREEN}🟢 Starting project with 'yarn start' in $project_dir$${RESET}" + nohup yarn start >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "nodejs" "$project_port" "yarn start" + else + log_message "$${GREEN}🟢 Starting project with 'npm start' in $project_dir$${RESET}" + nohup npm start >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "nodejs" "$project_port" "npm start" + fi + fi + fi + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "package.json" -type f -print0) +} + +# Function to detect and start Rails projects +detect_rails_projects() { + if [ "${ENABLE_RAILS}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for Ruby on Rails projects...$${RESET}" + + while IFS= read -r -d '' gemfile; do + project_dir=$(dirname "$gemfile") + log_message "$${GREEN}💎 Found Rails project: $project_dir$${RESET}" + + cd "$project_dir" + + # Check if it's actually a Rails project + if grep -q "gem ['\"]rails['\"]" Gemfile 2> /dev/null; then + log_message "$${GREEN}🟢 Starting Rails server in $project_dir$${RESET}" + nohup bundle exec rails server >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "rails" "3000" "bundle exec rails server" + fi + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "Gemfile" -type f -print0) +} + +# Function to detect and start Django projects +detect_django_projects() { + if [ "${ENABLE_DJANGO}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for Django projects...$${RESET}" + + while IFS= read -r -d '' manage_py; do + project_dir=$(dirname "$manage_py") + log_message "$${GREEN}🐍 Found Django project: $project_dir$${RESET}" + + cd "$project_dir" + log_message "$${GREEN}🟢 Starting Django development server in $project_dir$${RESET}" + nohup python manage.py runserver 0.0.0.0:8000 >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "django" "8000" "python manage.py runserver" + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "manage.py" -type f -print0) +} + +# Function to detect and start Flask projects +detect_flask_projects() { + if [ "${ENABLE_FLASK}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for Flask projects...$${RESET}" + + while IFS= read -r -d '' requirements_txt; do + project_dir=$(dirname "$requirements_txt") + + # Check if Flask is in requirements + if grep -q -i "flask" "$requirements_txt" 2> /dev/null; then + log_message "$${GREEN}🌶️ Found Flask project: $project_dir$${RESET}" + + cd "$project_dir" + + # Look for common Flask app files + for app_file in app.py main.py run.py; do + if [ -f "$app_file" ]; then + log_message "$${GREEN}🟢 Starting Flask application ($app_file) in $project_dir$${RESET}" + export FLASK_ENV=development + nohup python "$app_file" >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "flask" "5000" "python $app_file" + break + fi + done + fi + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "requirements.txt" -type f -print0) +} + +# Function to detect and start Spring Boot projects +detect_spring_boot_projects() { + if [ "${ENABLE_SPRING_BOOT}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for Spring Boot projects...$${RESET}" + + # Maven projects + while IFS= read -r -d '' pom_xml; do + project_dir=$(dirname "$pom_xml") + + # Check if it's a Spring Boot project + if grep -q "spring-boot" "$pom_xml" 2> /dev/null; then + log_message "$${GREEN}🍃 Found Spring Boot Maven project: $project_dir$${RESET}" + + cd "$project_dir" + if command -v ./mvnw &> /dev/null; then + log_message "$${GREEN}🟢 Starting Spring Boot application with Maven wrapper in $project_dir$${RESET}" + nohup ./mvnw spring-boot:run >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "spring-boot" "8080" "./mvnw spring-boot:run" + elif command -v mvn &> /dev/null; then + log_message "$${GREEN}🟢 Starting Spring Boot application with Maven in $project_dir$${RESET}" + nohup mvn spring-boot:run >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "spring-boot" "8080" "mvn spring-boot:run" + fi + fi + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "pom.xml" -type f -print0) + + # Gradle projects + while IFS= read -r -d '' build_gradle; do + project_dir=$(dirname "$build_gradle") + + # Check if it's a Spring Boot project + if grep -q "spring-boot" "$build_gradle" 2> /dev/null; then + log_message "$${GREEN}🍃 Found Spring Boot Gradle project: $project_dir$${RESET}" + + cd "$project_dir" + if command -v ./gradlew &> /dev/null; then + log_message "$${GREEN}🟢 Starting Spring Boot application with Gradle wrapper in $project_dir$${RESET}" + nohup ./gradlew bootRun >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "spring-boot" "8080" "./gradlew bootRun" + elif command -v gradle &> /dev/null; then + log_message "$${GREEN}🟢 Starting Spring Boot application with Gradle in $project_dir$${RESET}" + nohup gradle bootRun >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "spring-boot" "8080" "gradle bootRun" + fi + fi + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "build.gradle" -type f -print0) +} + +# Function to detect and start Go projects +detect_go_projects() { + if [ "${ENABLE_GO}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for Go projects...$${RESET}" + + while IFS= read -r -d '' go_mod; do + project_dir=$(dirname "$go_mod") + log_message "$${GREEN}🐹 Found Go project: $project_dir$${RESET}" + + cd "$project_dir" + + # Look for main.go or check if there's a main function + if [ -f "main.go" ]; then + log_message "$${GREEN}🟢 Starting Go application in $project_dir$${RESET}" + nohup go run main.go >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "go" "8080" "go run main.go" + elif [ -f "cmd/main.go" ]; then + log_message "$${GREEN}🟢 Starting Go application (cmd/main.go) in $project_dir$${RESET}" + nohup go run cmd/main.go >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "go" "8080" "go run cmd/main.go" + fi + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "go.mod" -type f -print0) +} + +# Function to detect and start PHP projects +detect_php_projects() { + if [ "${ENABLE_PHP}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for PHP projects...$${RESET}" + + while IFS= read -r -d '' composer_json; do + project_dir=$(dirname "$composer_json") + log_message "$${GREEN}🐘 Found PHP project: $project_dir$${RESET}" + + cd "$project_dir" + + # Look for common PHP entry points + for entry_file in index.php public/index.php; do + if [ -f "$entry_file" ]; then + log_message "$${GREEN}🟢 Starting PHP development server in $project_dir$${RESET}" + nohup php -S 0.0.0.0:8080 -t "$(dirname "$entry_file")" >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "php" "8080" "php -S 0.0.0.0:8080" + break + fi + done + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "composer.json" -type f -print0) +} + +# Function to detect and start Rust projects +detect_rust_projects() { + if [ "${ENABLE_RUST}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for Rust projects...$${RESET}" + + while IFS= read -r -d '' cargo_toml; do + project_dir=$(dirname "$cargo_toml") + log_message "$${GREEN}🦀 Found Rust project: $project_dir$${RESET}" + + cd "$project_dir" + + # Check if it's a binary project (has [[bin]] or default main.rs) + if grep -q "\[\[bin\]\]" Cargo.toml 2> /dev/null || [ -f "src/main.rs" ]; then + log_message "$${GREEN}🟢 Starting Rust application in $project_dir$${RESET}" + nohup cargo run >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "rust" "8000" "cargo run" + fi + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "Cargo.toml" -type f -print0) +} + +# Function to detect and start .NET projects +detect_dotnet_projects() { + if [ "${ENABLE_DOTNET}" != "true" ]; then + return + fi + + log_message "$${BLUE}🔍 Scanning for .NET projects...$${RESET}" + + while IFS= read -r -d '' csproj; do + project_dir=$(dirname "$csproj") + log_message "$${GREEN}🔷 Found .NET project: $project_dir$${RESET}" + + cd "$project_dir" + log_message "$${GREEN}🟢 Starting .NET application in $project_dir$${RESET}" + nohup dotnet run >> "${LOG_PATH}" 2>&1 & + add_detected_project "$project_dir" "dotnet" "5000" "dotnet run" + + done < <(find "${WORKSPACE_DIR}" -maxdepth "${SCAN_DEPTH}" -name "*.csproj" -type f -print0) +} + +log_message "Starting auto-detection of development projects..." + +# Expand workspace directory if it contains variables +WORKSPACE_DIR=$(eval echo "${WORKSPACE_DIR}") + +# Check if workspace directory exists +if [ ! -d "$WORKSPACE_DIR" ]; then + log_message "$${RED}❌ Workspace directory does not exist: $WORKSPACE_DIR$${RESET}" + exit 1 +fi + +cd "$WORKSPACE_DIR" + +# Run all detection functions +detect_npm_projects +detect_rails_projects +detect_django_projects +detect_flask_projects +detect_spring_boot_projects +detect_go_projects +detect_php_projects +detect_rust_projects +detect_dotnet_projects + +log_message "$${GREEN}✅ Auto-start scan completed!$${RESET}" +log_message "$${YELLOW}💡 Check running processes with 'ps aux | grep -E \"(npm|rails|python|java|go|php|cargo|dotnet)\"'$${RESET}" +log_message "$${YELLOW}💡 View logs: tail -f ${LOG_PATH}$${RESET}" + +# Set default port if no projects were detected +if [ "$FIRST_PORT_DETECTED" = false ]; then + echo "3000" > "$DETECTED_PORT_FILE" + log_message "$${YELLOW}⚠️ No projects detected - Preview app will default to port 3000$${RESET}" +fi