diff --git a/.github/typos.toml b/.github/typos.toml index f27257a23..7d53f9bf1 100644 --- a/.github/typos.toml +++ b/.github/typos.toml @@ -2,6 +2,8 @@ muc = "muc" # For Munich location code Hashi = "Hashi" HashiCorp = "HashiCorp" +mavrickrishi = "mavrickrishi" # Username +mavrick = "mavrick" # Username [files] extend-exclude = ["registry/coder/templates/aws-devcontainer/architecture.svg"] #False positive \ No newline at end of file diff --git a/registry/mavrickrishi/.images/avatar.jpeg b/registry/mavrickrishi/.images/avatar.jpeg new file mode 100644 index 000000000..a624cf8fc Binary files /dev/null and b/registry/mavrickrishi/.images/avatar.jpeg differ diff --git a/registry/mavrickrishi/README.md b/registry/mavrickrishi/README.md new file mode 100644 index 000000000..05ce961af --- /dev/null +++ b/registry/mavrickrishi/README.md @@ -0,0 +1,25 @@ +--- +display_name: "Rishi Mondal" +bio: "Breaking code, fixing bugs, and occasionally making it work! Always caffeinated, always committing" +avatar: "./.images/avatar.jpeg" +github: "MAVRICK-1" +linkedin: "https://www.linkedin.com/in/rishi-mondal-5238b2282/" +website: "https://mavrick-portfolio.vercel.app/" +support_email: "mavrickrishi@gmail.com" +status: "community" +--- + +# Rishi Mondal + +I'm Rishi Mondal, a passionate developer from Chinsurah Hooghly, West Bengal, India. +I'm a maintainer at CNCF KubeStellar, GSoC contributor at UCSC OSPO, and a Docker Captain. +When I'm not breaking code and fixing bugs, you'll find me contributing to open-source projects, +participating in LFX CNCF programs, and helping the developer community grow + +## Modules + +- [auto-start-dev-server](modules/auto-start-dev-server/README.md) - Automatically detect and start development servers for various project types + +## License + +These modules are provided under the same license as the Coder Registry. 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..63a5bac3e --- /dev/null +++ b/registry/mavrickrishi/modules/auto-start-dev-server/README.md @@ -0,0 +1,233 @@ +--- +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 + - nodejs + - python + - java + - go + - rust + - php + - rails + - django + - flask + - spring-boot + - dotnet +--- + +# 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` | + +## 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 +} +``` + +## Configuration Options + +### Required Variables + +- `agent_id` (string): The ID of a Coder agent + +## Devcontainer Integration + +When `enable_devcontainer` is true, the module will check for `.devcontainer/devcontainer.json` files and look for custom start commands in the VS Code settings: + +```json +{ + "customizations": { + "vscode": { + "settings": { + "npm.script.start": "npm run custom-dev-command" + } + } + } +} +``` + +If found, the custom command will be used instead of the default project detection logic. + +## Monitoring and Debugging + +### View Logs + +```bash +# Real-time log viewing +tail -f /tmp/dev-servers.log + +# View full log +cat /tmp/dev-servers.log +``` + +## Security Considerations + +- Servers are started with the same user permissions as the Coder agent +- All project detection is read-only (only checks for existence of files) +- Server processes run in the background and inherit workspace environment +- Log files contain server output which may include sensitive information + +## Troubleshooting + +### Common Issues + +1. **No servers starting**: Check that project files exist and scan depth covers your project directories +2. **Permission denied**: Ensure the script has execute permissions and dependencies are installed +3. **Wrong directory**: Verify `workspace_directory` path is correct and accessible +4. **Missing dependencies**: Install required runtimes (node, python, java, etc.) in your base image + +## Live Preview App + +The module automatically creates a "Live Preview" app in your Coder workspace that intelligently selects the best project for preview: + +- **Smart frontend detection**: Prioritizes frontend projects (React, Vue, Angular, etc.) over backend services +- **Automatic detection**: Uses the port from the primary frontend project, or first detected project if no frontend found +- **Dynamic URL**: Points to `http://localhost:{detected_port}` +- **Monorepo friendly**: In multi-project setups, automatically selects the most likely UI project +- **Configurable**: Can be disabled by setting `enable_preview_app = false` +- **Fallback**: Defaults to port 3000 if no projects are detected + +## Module Outputs + +| Output | Description | Example Value | +| ------------------------ | ---------------------------------------------------------------------- | --------------------------------- | +| `detected_projects_file` | Path to JSON file with detected projects (includes `is_frontend` flag) | `/tmp/detected-projects.json` | +| `log_path` | Path to dev server log file | `/tmp/dev-servers.log` | +| `common_ports` | Map of default ports by project type | `{nodejs=3000, django=8000, ...}` | +| `preview_url` | URL of the live preview app | `http://localhost:3000` | +| `detected_port` | Port of the first detected project | `3000` | + +## Examples + +### Basic Usage + +```hcl +module "auto_start" { + source = "./modules/auto-start-dev-server" + agent_id = coder_agent.main.id +} +``` + +### 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..c37a37a13 --- /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", + }); + }); +}); \ No newline at end of file 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..0683a4bcf --- /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 "enable_npm" { + type = bool + description = "Enable auto-detection and startup of npm projects." + default = true +} + +variable "enable_rails" { + type = bool + description = "Enable auto-detection and startup of Rails projects." + default = true +} + +variable "enable_django" { + type = bool + description = "Enable auto-detection and startup of Django projects." + default = true +} + +variable "enable_flask" { + type = bool + description = "Enable auto-detection and startup of Flask projects." + default = true +} + +variable "enable_spring_boot" { + type = bool + description = "Enable auto-detection and startup of Spring Boot projects." + default = true +} + +variable "enable_go" { + type = bool + description = "Enable auto-detection and startup of Go projects." + default = true +} + +variable "enable_php" { + type = bool + description = "Enable auto-detection and startup of PHP projects." + default = true +} + +variable "enable_rust" { + type = bool + description = "Enable auto-detection and startup of Rust projects." + default = true +} + +variable "enable_dotnet" { + type = bool + description = "Enable auto-detection and startup of .NET projects." + default = true +} + +variable "enable_devcontainer" { + type = bool + description = "Enable integration with devcontainer.json configuration." + default = true +} + +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 = var.enable_npm + ENABLE_RAILS = var.enable_rails + ENABLE_DJANGO = var.enable_django + ENABLE_FLASK = var.enable_flask + ENABLE_SPRING_BOOT = var.enable_spring_boot + ENABLE_GO = var.enable_go + ENABLE_PHP = var.enable_php + ENABLE_RUST = var.enable_rust + ENABLE_DOTNET = var.enable_dotnet + ENABLE_DEVCONTAINER = var.enable_devcontainer + 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 to expose detected projects +output "detected_projects_file" { + value = "/tmp/detected-projects.json" + description = "Path to JSON file containing detected projects with their types, ports, and commands" +} + +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" +} \ No newline at end of file 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..4da877be9 --- /dev/null +++ b/registry/mavrickrishi/modules/auto-start-dev-server/run.sh @@ -0,0 +1,474 @@ +#!/usr/bin/env bash + +set -e + +# 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) +} + +# Main execution +main() { + 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 +} + +# Run main function +main "$@" \ No newline at end of file