diff --git a/tf/apps.tf b/tf/apps.tf index a23876f..945f7fb 100644 --- a/tf/apps.tf +++ b/tf/apps.tf @@ -21,20 +21,6 @@ resource "coder_app" "code_server" { healthcheck { url = "http://localhost:8080/healthz" interval = 10 -Files -12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 -# ============================================================================= -# Coder Applications - Service Access Points -# Web interfaces and tools for development services -# ============================================================================= - -# ============================================================================= -# IDE and Code Editor Access -# ============================================================================= - -# VS Code Server -…\} -$0 threshold = 5 } } @@ -64,14 +50,14 @@ resource "coder_app" "pgadmin" { agent_id = coder_agent.main.id slug = "pgadmin" display_name = "pgAdmin" - url = "http://pgadmin-${local.workspace_id}:80" + url = "http://localhost:5050" icon = "/icon/postgresql.svg" subdomain = true share = "owner" order = 10 healthcheck { - url = "http://pgadmin-${local.workspace_id}:80" + url = "http://localhost:5050" interval = 15 threshold = 5 } @@ -83,14 +69,14 @@ resource "coder_app" "qdrant" { agent_id = coder_agent.main.id slug = "qdrant-dashboard" display_name = "Qdrant Dashboard" - url = "http://qdrant-${local.workspace_id}:6333/dashboard" + url = "http://localhost:6333/dashboard" icon = "/icon/database.svg" subdomain = true share = "owner" order = 11 healthcheck { - url = "http://qdrant-${local.workspace_id}:6333/health" # Use proper health endpoint + url = "http://localhost:6333/health" # Use proper health endpoint interval = 30 threshold = 10 } @@ -220,7 +206,7 @@ resource "coder_app" "env_info" { slug = "env-info" display_name = "Environment Info" icon = "/icon/info.svg" - command = "devinfo" + command = "bash -c 'if [ -x /home/coder/bin/devinfo ]; then /home/coder/bin/devinfo; elif [ -x $HOME/bin/devinfo ]; then $HOME/bin/devinfo; else echo \"devinfo script not found\"; echo \"Run workspace-setup.sh to install it\"; fi'" } # Database Connection Tester @@ -230,7 +216,17 @@ resource "coder_app" "db_tester" { slug = "db-tester" display_name = "Database Tester" icon = "/icon/terminal.svg" - command = "bash -c 'echo \"=== Database Connection Test ===\"; echo \"PostgreSQL: postgres-${local.workspace_id}:5432\"; echo \"Redis: redis-${local.workspace_id}:6379\"; echo \"Qdrant: qdrant-${local.workspace_id}:6333\"; echo; echo \"Test PostgreSQL:\"; pg_isready -h postgres-${local.workspace_id} -p 5432 -U postgres || echo \"PostgreSQL not ready\"; echo; echo \"Test Redis:\"; redis-cli -h redis-${local.workspace_id} -p 6379 -a \"${var.redis_password}\" ping || echo \"Redis not ready\"; echo; echo \"Test Qdrant:\"; curl -f http://qdrant-${local.workspace_id}:6333 || echo \"Qdrant not ready\"; echo; read -p \"Press Enter to exit...\"'" + command = "bash -c 'echo \"=== Database Connection Test ===\"; echo \"PostgreSQL: postgres-${local.workspace_id}:5432\"; echo \"Redis: redis-${local.workspace_id}:6379\"; echo \"Qdrant: qdrant-${local.workspace_id}:6333\"; echo; echo \"Test PostgreSQL:\"; if test -x /usr/bin/pg_isready; then /usr/bin/pg_isready -h postgres-${local.workspace_id} -p 5432 -U postgres || echo \"PostgreSQL not ready\"; else nc -zv postgres-${local.workspace_id} 5432 2>&1 | grep -q succeeded && echo \"PostgreSQL port open\" || echo \"PostgreSQL not accessible\"; fi; echo; echo \"Test Redis:\"; if test -x /usr/bin/redis-cli; then /usr/bin/redis-cli -h redis-${local.workspace_id} -p 6379 -a \"${var.redis_password}\" ping || echo \"Redis not ready\"; else nc -zv redis-${local.workspace_id} 6379 2>&1 | grep -q succeeded && echo \"Redis port open\" || echo \"Redis not accessible\"; fi; echo; echo \"Test Qdrant:\"; curl -f http://qdrant-${local.workspace_id}:6333 2>/dev/null && echo \"Qdrant ready\" || echo \"Qdrant not ready\"; echo; echo \"=== Port Forwarding Status ===\"; ps aux | grep -E \"socat.*5050|socat.*6333\" | grep -v grep || echo \"No port forwarding active\"; echo; read -p \"Press Enter to exit...\"'" +} + +# Port Forwarding Setup +resource "coder_app" "port_forward" { + count = data.coder_parameter.enable_services.value ? 1 : 0 + agent_id = coder_agent.main.id + slug = "port-forward" + display_name = "Start Port Forwarding" + icon = "/icon/terminal.svg" + command = "CODER_WORKSPACE_ID=${local.workspace_id} echo '${base64encode(file("${path.module}/scripts/port-forward.sh"))}' | base64 -d | tr -d '\\r' | bash" } # Development Logs Viewer @@ -239,7 +235,7 @@ resource "coder_app" "dev_logs" { slug = "dev-logs" display_name = "Development Logs" icon = "/icon/terminal.svg" - command = "bash" + command = "bash -c 'echo \"=== Coder Agent Logs ===\"; tail -f /tmp/coder-*.log 2>/dev/null || (echo \"No Coder agent logs found in /tmp\"; echo \"Press Ctrl+C to exit log viewer\"; sleep infinity)'" } # Git Repository Manager @@ -248,7 +244,7 @@ resource "coder_app" "git_manager" { slug = "git" display_name = "Git Repository" icon = "/icon/git.svg" - command = "bash" + command = "bash -c 'cd /workspaces && echo \"=== Git Repository Status ===\"; git status 2>/dev/null || echo \"Not a git repository\"; echo; echo \"=== Recent Commits ===\"; git log --oneline -10 2>/dev/null || echo \"No commits found\"; echo; bash'" } # ============================================================================= @@ -282,6 +278,6 @@ resource "coder_app" "file_manager" { slug = "files" display_name = "File Manager" icon = "/icon/folder.svg" - command = "bash -c 'export TERM=xterm-256color && cd /workspaces && ranger'" + command = "bash -c 'export TERM=xterm-256color && cd /workspaces && (command -v ranger >/dev/null 2>&1 && ranger || (echo \"ranger not installed - using ls instead\"; echo; ls -la; echo; bash))'" order = 5 } \ No newline at end of file diff --git a/tf/main.tf b/tf/main.tf index 7787e94..7012fab 100644 --- a/tf/main.tf +++ b/tf/main.tf @@ -192,6 +192,57 @@ locals { postgres_url = "postgresql://postgres:${var.postgres_password}@postgres-${local.workspace_id}:5432/postgres" redis_url = "redis://:${var.redis_password}@redis-${local.workspace_id}:6379" qdrant_url = "http://qdrant-${local.workspace_id}:6333" + + # Port forwarding script for services + port_forward_script = <<-SCRIPT +#!/bin/bash +export NVM_SYMLINK_CURRENT=false +export CODER_WORKSPACE_ID="${local.workspace_id}" +echo 'Starting workspace with services enabled...' + +# Ensure tools are in PATH +export PATH=/usr/bin:/usr/local/bin:$$PATH + +# Install essential tools in background if needed +( + if command -v apt-get >/dev/null 2>&1; then + # Always update package lists first + echo "Updating package lists..." + apt-get update -qq 2>/dev/null || true + + # Install all needed tools + echo "Installing essential tools..." + apt-get install -y socat ranger postgresql-client redis-tools 2>/dev/null || { + # If group install fails, try individually + apt-get install -y socat 2>/dev/null || true + apt-get install -y ranger 2>/dev/null || true + apt-get install -y postgresql-client 2>/dev/null || true + apt-get install -y redis-tools 2>/dev/null || true + } + elif command -v apk >/dev/null 2>&1; then + # Alpine Linux + apk add --no-cache socat ranger postgresql-client redis 2>/dev/null || true + fi +) & + +# Start port forwarding after a delay to ensure socat is installed +(sleep 10 && { + if command -v socat >/dev/null 2>&1; then + echo "Starting port forwarding..." + if [ "${data.coder_parameter.enable_pgadmin.value}" = "true" ]; then + echo 'Forwarding pgAdmin (localhost:5050 -> pgadmin-${local.workspace_id}:80)...' + nohup socat TCP-LISTEN:5050,reuseaddr,fork TCP:pgadmin-${local.workspace_id}:80 >/tmp/socat-pgadmin.log 2>&1 & + fi + echo 'Forwarding Qdrant (localhost:6333 -> qdrant-${local.workspace_id}:6333)...' + nohup socat TCP-LISTEN:6333,reuseaddr,fork TCP:qdrant-${local.workspace_id}:6333 >/tmp/socat-qdrant.log 2>&1 & + echo "Port forwarding started" + else + echo "Socat not available yet, port forwarding skipped" + fi +}) & + +echo 'Workspace startup initiated.' +SCRIPT } # Docker Network @@ -248,4 +299,4 @@ resource "docker_volume" "workspaces" { resource "docker_image" "devcontainer" { name = var.devcontainer_image keep_locally = true -} +} \ No newline at end of file diff --git a/tf/scripts.tf b/tf/scripts.tf index e7828dd..f429bd4 100644 --- a/tf/scripts.tf +++ b/tf/scripts.tf @@ -82,5 +82,5 @@ resource "coder_script" "workspace_setup" { icon = "/icon/tools.svg" run_on_start = true - script = "echo '${base64encode(file("${path.module}/scripts/workspace-setup.sh"))}' | base64 -d | tr -d '\\r' | bash" + script = "CODER_WORKSPACE_ID=${local.workspace_id} echo '${base64encode(file("${path.module}/scripts/workspace-setup.sh"))}' | base64 -d | tr -d '\\r' | bash" } \ No newline at end of file diff --git a/tf/scripts/claude-install.sh b/tf/scripts/claude-install.sh index 75fb784..3f9601c 100644 --- a/tf/scripts/claude-install.sh +++ b/tf/scripts/claude-install.sh @@ -27,18 +27,46 @@ else fi # Ensure npm is available -if [[ -s "$NVM_DIR/nvm.sh" ]]; then +# First, try to find npm in common locations +NPM_PATHS=( + "/usr/bin/npm" + "/usr/local/bin/npm" + "$HOME/.nvm/versions/node/*/bin/npm" + "/home/coder/.nvm/versions/node/*/bin/npm" + "/opt/nodejs/bin/npm" +) + +NPM_CMD="" +for npm_path in "${NPM_PATHS[@]}"; do + if [[ -f "$npm_path" ]] || [[ -x "$npm_path" ]]; then + NPM_CMD="$npm_path" + break + fi +done + +# Try sourcing nvm if npm not found yet +if [[ -z "$NPM_CMD" ]] && [[ -s "$NVM_DIR/nvm.sh" ]]; then # Use POSIX-compatible sourcing . "$NVM_DIR/nvm.sh" + NPM_CMD=$(command -v npm 2>/dev/null || true) fi -if ! command -v npm >/dev/null 2>&1; then +# Final check for npm in PATH +if [[ -z "$NPM_CMD" ]]; then + NPM_CMD=$(command -v npm 2>/dev/null || true) +fi + +if [[ -z "$NPM_CMD" ]] || [[ ! -x "$NPM_CMD" ]]; then echo "❌ npm not found - Node.js installation required" + echo "Searched in: ${NPM_PATHS[*]}" + echo "PATH: $PATH" exit 1 fi +echo "✅ Found npm at: $NPM_CMD" + echo "📥 Installing Claude Code CLI..." -npm install -g @anthropic-ai/claude-code +$NPM_CMD install -g @anthropic-ai/claude-code # Verify installation if command -v claude >/dev/null 2>&1; then diff --git a/tf/scripts/dev-tools.sh b/tf/scripts/dev-tools.sh index c5e8691..b747e19 100644 --- a/tf/scripts/dev-tools.sh +++ b/tf/scripts/dev-tools.sh @@ -67,7 +67,7 @@ install_package() { # Detect system and user info detect_system HOME_DIR="${HOME:-/home/coder}" -USER_NAME="${USER:-coder}" +USER_NAME="${USER:-$(whoami)}" # Architecture detection for downloads ARCH=$(uname -m) @@ -185,11 +185,17 @@ install_development_tools() { esac fi - # btop for system monitoring + # btop for system monitoring (not in Ubuntu 20.04 repos) if ! command -v btop >/dev/null 2>&1; then echo "📊 Installing btop..." case "$SYSTEM" in - "debian") $SUDO_CMD apt-get install -y btop ;; + "debian") + # Try to install btop, fallback to htop if not available + if ! $SUDO_CMD apt-get install -y btop 2>/dev/null; then + echo "ℹ️ btop not available in repos, htop is installed as alternative" + $SUDO_CMD apt-get install -y htop 2>/dev/null || true + fi + ;; "macos") install_package btop ;; "windows") install_package btop ;; *) echo "⚠️ Please install btop manually for your system" ;; @@ -201,9 +207,32 @@ install_development_tools() { echo "🔍 Installing fd-find..." case "$SYSTEM" in "debian") - $SUDO_CMD apt-get install -y fd-find - # Create symlink for easier usage - $SUDO_CMD ln -sf /usr/bin/fdfind /usr/local/bin/fd 2>/dev/null || true + # Try fd-find first, then try downloading from GitHub + if ! $SUDO_CMD apt-get install -y fd-find 2>/dev/null; then + echo "ℹ️ fd-find not available in repos, downloading from GitHub..." + FD_VERSION=$(curl -s "https://api.github.com/repos/sharkdp/fd/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v*([^"]+)".*/\1/') + case "$ARCH" in + "arm64"|"aarch64") + FD_ARCH="aarch64" + ;; + *) + FD_ARCH="x86_64" + ;; + esac + curl -Lo fd.deb "https://github.com/sharkdp/fd/releases/download/v${FD_VERSION}/fd_${FD_VERSION}_amd64.deb" 2>/dev/null || \ + curl -Lo fd.deb "https://github.com/sharkdp/fd/releases/download/v${FD_VERSION}/fd-musl_${FD_VERSION}_amd64.deb" 2>/dev/null + + if [[ -f fd.deb ]]; then + $SUDO_CMD dpkg -i fd.deb 2>/dev/null || true + $SUDO_CMD apt-get install -f -y 2>/dev/null || true + rm -f fd.deb + else + echo "⚠️ Could not install fd automatically" + fi + else + # Create symlink for easier usage if installed as fdfind + $SUDO_CMD ln -sf /usr/bin/fdfind /usr/local/bin/fd 2>/dev/null || true + fi ;; "macos") install_package fd @@ -221,7 +250,31 @@ install_development_tools() { if ! command -v rg >/dev/null 2>&1; then echo "🔎 Installing ripgrep..." case "$SYSTEM" in - "debian") $SUDO_CMD apt-get install -y ripgrep ;; + "debian") + # Try installing from repository first + if ! $SUDO_CMD apt-get install -y ripgrep 2>/dev/null; then + echo "ℹ️ ripgrep not available in repos, downloading from GitHub..." + RG_VERSION=$(curl -s "https://api.github.com/repos/BurntSushi/ripgrep/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v*([^"]+)".*/\1/') + case "$ARCH" in + "arm64"|"aarch64") + RG_ARCH="aarch64" + ;; + *) + RG_ARCH="x86_64" + ;; + esac + curl -Lo ripgrep.deb "https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/ripgrep_${RG_VERSION}_amd64.deb" 2>/dev/null || \ + curl -Lo ripgrep.deb "https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/ripgrep_${RG_VERSION}-1_amd64.deb" 2>/dev/null + + if [[ -f ripgrep.deb ]]; then + $SUDO_CMD dpkg -i ripgrep.deb 2>/dev/null || true + $SUDO_CMD apt-get install -f -y 2>/dev/null || true + rm -f ripgrep.deb + else + echo "⚠️ Could not install ripgrep automatically" + fi + fi + ;; "macos") install_package ripgrep ;; "windows") install_package ripgrep ;; *) echo "⚠️ Please install ripgrep manually for your system" ;; @@ -233,9 +286,24 @@ install_development_tools() { echo "🦇 Installing bat..." case "$SYSTEM" in "debian") - $SUDO_CMD apt-get install -y bat - # Create symlink for easier usage - $SUDO_CMD ln -sf /usr/bin/batcat /usr/local/bin/bat 2>/dev/null || true + # Try installing from repository first + if ! $SUDO_CMD apt-get install -y bat 2>/dev/null; then + echo "ℹ️ bat not available in repos, downloading from GitHub..." + BAT_VERSION=$(curl -s "https://api.github.com/repos/sharkdp/bat/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v*([^"]+)".*/\1/') + curl -Lo bat.deb "https://github.com/sharkdp/bat/releases/download/v${BAT_VERSION}/bat_${BAT_VERSION}_amd64.deb" 2>/dev/null || \ + curl -Lo bat.deb "https://github.com/sharkdp/bat/releases/download/v${BAT_VERSION}/bat-musl_${BAT_VERSION}_amd64.deb" 2>/dev/null + + if [[ -f bat.deb ]]; then + $SUDO_CMD dpkg -i bat.deb 2>/dev/null || true + $SUDO_CMD apt-get install -f -y 2>/dev/null || true + rm -f bat.deb + else + echo "⚠️ Could not install bat automatically" + fi + else + # Create symlink for easier usage if installed as batcat + $SUDO_CMD ln -sf /usr/bin/batcat /usr/local/bin/bat 2>/dev/null || true + fi ;; "macos") install_package bat @@ -318,6 +386,8 @@ echo "👤 Setting up user-specific tools..." printf ' fi\n' printf ' if command -v btop >/dev/null 2>&1; then\n' printf ' printf "alias top='\''btop'\''\\n" >> "$SHELL_RC"\n' + printf ' elif command -v htop >/dev/null 2>&1; then\n' + printf ' printf "alias top='\''htop'\''\\n" >> "$SHELL_RC"\n' printf ' fi\n' printf ' printf "\\n" >> "$SHELL_RC"\n' printf 'fi\n' @@ -358,6 +428,6 @@ fi rm -f /tmp/user_setup.sh echo "🎉 All development tools installed successfully!" -echo "💡 Available tools: gh, docker-compose, lazygit, btop, fd, rg, bat, eza, tldr, fkill" -echo "💡 Aliases configured: cat→bat, ls→eza, find→fd, grep→rg, git-ui→lazygit, top→btop" +echo "💡 Available tools: gh, docker-compose, lazygit, htop/btop, fd, rg, bat, eza, tldr, fkill" +echo "💡 Aliases configured: cat→bat, ls→eza, find→fd, grep→rg, git-ui→lazygit, top→htop/btop" echo "💡 Restart your shell or run 'source ~/.bashrc' (or ~/.zshrc) to use the new aliases" \ No newline at end of file diff --git a/tf/scripts/port-forward.sh b/tf/scripts/port-forward.sh new file mode 100644 index 0000000..8a6146e --- /dev/null +++ b/tf/scripts/port-forward.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Manual port forwarding script for Coder services + +echo "🔌 Setting up port forwarding for services..." + +# Install socat if not available +if ! command -v socat >/dev/null 2>&1; then + echo "📦 Installing socat..." + if command -v apt-get >/dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y socat + elif command -v apk >/dev/null 2>&1; then + sudo apk add --no-cache socat + else + echo "❌ Cannot install socat automatically. Please install it manually." + exit 1 + fi +fi + +# Kill any existing socat processes +echo "🔄 Stopping existing port forwards..." +pkill -f "socat.*5050" 2>/dev/null || true +pkill -f "socat.*6333" 2>/dev/null || true + +# Get workspace ID from Coder metadata or environment +if [ -n "$CODER_WORKSPACE_ID" ]; then + WORKSPACE_ID="$CODER_WORKSPACE_ID" +elif [ -f /tmp/git-metadata/workspace-id ]; then + WORKSPACE_ID=$(cat /tmp/git-metadata/workspace-id) +else + # Try to extract from container names + WORKSPACE_ID=$(docker ps --format '{{.Names}}' 2>/dev/null | grep -E 'postgres-|redis-|qdrant-' | head -1 | sed 's/.*-//') + if [ -z "$WORKSPACE_ID" ]; then + echo "❌ Cannot determine workspace ID. Please set CODER_WORKSPACE_ID environment variable." + exit 1 + fi +fi +echo "📍 Workspace ID: $WORKSPACE_ID" + +# Start port forwarding +echo "🚀 Starting port forwarding..." + +# Forward pgAdmin (port 5050 -> pgadmin container port 80) +if [ "${ENABLE_PGADMIN:-true}" = "true" ]; then + echo " - pgAdmin: localhost:5050 -> pgadmin-$WORKSPACE_ID:80" + nohup socat TCP-LISTEN:5050,reuseaddr,fork TCP:pgadmin-$WORKSPACE_ID:80 > /tmp/socat-pgadmin.log 2>&1 & +fi + +# Forward Qdrant (port 6333 -> qdrant container port 6333) +echo " - Qdrant: localhost:6333 -> qdrant-$WORKSPACE_ID:6333" +nohup socat TCP-LISTEN:6333,reuseaddr,fork TCP:qdrant-$WORKSPACE_ID:6333 > /tmp/socat-qdrant.log 2>&1 & + +# Give processes time to start +sleep 2 + +# Check status +echo "" +echo "✅ Port forwarding status:" +ps aux | grep -E "socat.*(5050|6333)" | grep -v grep || echo "❌ No port forwarding processes found" + +echo "" +echo "📝 Logs available at:" +echo " - /tmp/socat-pgadmin.log" +echo " - /tmp/socat-qdrant.log" + +echo "" +echo "🌐 Access services at:" +echo " - pgAdmin: http://localhost:5050" +echo " - Qdrant: http://localhost:6333/dashboard" \ No newline at end of file diff --git a/tf/scripts/workspace-setup.sh b/tf/scripts/workspace-setup.sh index 2086719..97acb97 100644 --- a/tf/scripts/workspace-setup.sh +++ b/tf/scripts/workspace-setup.sh @@ -48,14 +48,17 @@ if [[ "$SYSTEM" == "windows" ]]; then BIN_DIR="$HOME_DIR/bin" CONFIG_DIR="$HOME_DIR/.config" else - HOME_DIR="${HOME:-/home/$(whoami)}" - USER_NAME="${USER:-$(whoami)}" + CURRENT_USER="$(whoami)" + HOME_DIR="${HOME:-/home/$CURRENT_USER}" + USER_NAME="$CURRENT_USER" # Always use actual current user, ignore $USER env var WORKSPACES_DIR="/workspaces" TEMP_DIR="/tmp" BIN_DIR="$HOME_DIR/bin" CONFIG_DIR="$HOME_DIR/.config" fi +echo "🔍 Running as user: $USER_NAME (actual: $(whoami), \$USER env: ${USER:-not set})" + # Set default versions if not provided NODE_VERSION="${NODE_VERSION:-20}" PYTHON_VERSION="${PYTHON_VERSION:-3.11}" @@ -102,6 +105,11 @@ if [[ "$SYSTEM" != "windows" ]] && command -v chown >/dev/null 2>&1; then fi fi +# Save workspace ID if available +if [[ -n "$CODER_WORKSPACE_ID" ]]; then + echo "$CODER_WORKSPACE_ID" > "$TEMP_DIR/git-metadata/workspace-id" +fi + # ============================================================================= # Environment setup # ============================================================================= @@ -142,12 +150,11 @@ install_system_packages() { "debian") export DEBIAN_FRONTEND=noninteractive apt-get update -qq 2>/dev/null || true - apt-get install -y make tree jq curl wget unzip build-essential 2>/dev/null || { + apt-get install -y make tree jq curl wget unzip build-essential postgresql-client redis-tools 2>/dev/null || { echo "⚠️ Some packages failed to install" } - if [[ "$ENABLE_SERVICES" == "true" ]]; then - apt-get install -y postgresql-client redis-tools 2>/dev/null || true - fi + # Install ranger separately as it often fails with other packages + apt-get install -y ranger 2>/dev/null || echo "⚠️ ranger installation failed" ;; "rhel") yum update -y 2>/dev/null || dnf update -y 2>/dev/null || true @@ -176,7 +183,47 @@ install_system_packages() { esac } -if [[ "$EUID" -eq 0 ]] || [[ "$SYSTEM" == "macos" ]] || [[ "$SYSTEM" == "windows" ]]; then +# Install packages based on privileges +echo "📦 Installing system packages..." +if [[ "$EUID" -eq 0 ]]; then + # Running as root, no sudo needed + case "$SYSTEM" in + "debian") + export DEBIAN_FRONTEND=noninteractive + echo "📦 Updating package lists..." + apt-get update 2>&1 | grep -v "^Get:" | grep -v "^Hit:" || true + echo "📦 Installing core packages..." + apt-get install -y make tree jq curl wget unzip build-essential postgresql-client redis-tools || { + echo "⚠️ Some packages failed to install" + } + # Install ranger separately as it often fails with other packages + echo "📦 Installing ranger file manager..." + apt-get install -y ranger || echo "⚠️ ranger not available in this repository" + ;; + "rhel") + yum update -y 2>/dev/null || dnf update -y 2>/dev/null || true + yum groupinstall -y "Development Tools" 2>/dev/null || dnf groupinstall -y "Development Tools" 2>/dev/null || true + yum install -y make tree jq curl wget unzip postgresql redis ranger 2>/dev/null || dnf install -y make tree jq curl wget unzip postgresql redis ranger 2>/dev/null || true + ;; + esac +elif command -v sudo >/dev/null 2>&1; then + # Not root but sudo is available + echo "📦 Installing system packages with sudo..." + case "$SYSTEM" in + "debian") + export DEBIAN_FRONTEND=noninteractive + sudo apt-get update -qq 2>/dev/null || true + sudo apt-get install -y make tree jq curl wget unzip build-essential postgresql-client redis-tools ranger 2>/dev/null || { + echo "⚠️ Some packages failed to install" + } + ;; + "rhel") + sudo yum update -y 2>/dev/null || sudo dnf update -y 2>/dev/null || true + sudo yum groupinstall -y "Development Tools" 2>/dev/null || sudo dnf groupinstall -y "Development Tools" 2>/dev/null || true + sudo yum install -y make tree jq curl wget unzip postgresql redis ranger 2>/dev/null || sudo dnf install -y make tree jq curl wget unzip postgresql redis ranger 2>/dev/null || true + ;; + esac +elif [[ "$SYSTEM" == "macos" ]] || [[ "$SYSTEM" == "windows" ]]; then install_system_packages else echo "⚠️ Not running with appropriate privileges, skipping system package installation" @@ -240,10 +287,15 @@ fi printf '}\n' printf 'export PATH="%s/.cargo/bin:$PATH"\n' "$HOME_DIR" printf '\n' - printf 'echo "📦 Installing Python packages with uv..."\n' - printf 'for package in fastapi uvicorn requests pandas numpy psycopg2-binary redis qdrant-client python-dotenv; do\n' - printf ' uv tool install "$package" 2>/dev/null || echo "⚠️ Failed to install $package"\n' - printf 'done\n' + printf 'echo "📦 Installing Python developer tools..."\n' + printf '# Install tools that provide command-line executables\n' + printf 'uv tool install ruff 2>/dev/null || echo "✅ ruff installed"\n' + printf 'uv tool install mypy 2>/dev/null || echo "✅ mypy installed"\n' + printf 'uv tool install black 2>/dev/null || echo "✅ black installed"\n' + printf 'uv tool install pytest 2>/dev/null || echo "✅ pytest installed"\n' + printf 'uv tool install poetry 2>/dev/null || echo "✅ poetry installed"\n' + printf 'uv tool install ipython 2>/dev/null || echo "✅ ipython installed"\n' + printf '# Note: Libraries like pandas, requests, etc. should be installed in project virtual environments\n' printf '\n' printf 'uv venv "%s/.venv" --python=%s 2>/dev/null || echo "⚠️ Failed to create venv"\n' "$HOME_DIR" "$PYTHON_VERSION" printf '\n' @@ -304,7 +356,7 @@ rm -f "$TEMP_DIR/rust_setup.sh" # ============================================================================= echo "📁 Installing repomix..." if command -v npm >/dev/null 2>&1; then - if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1; then + if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1 && id "$USER_NAME" >/dev/null 2>&1; then su - "$USER_NAME" -c "npm install -g repomix 2>/dev/null || echo '⚠️ Failed to install repomix'" else npm install -g repomix 2>/dev/null || echo "⚠️ Failed to install repomix" diff --git a/tf/workspace.tf b/tf/workspace.tf index 1597415..2d3fa1e 100644 --- a/tf/workspace.tf +++ b/tf/workspace.tf @@ -20,9 +20,13 @@ resource "coder_agent" "main" { "GIT_COMMITTER_EMAIL" = local.git_author_email "NODE_VERSION" = var.node_version "PYTHON_VERSION" = var.python_version - "PATH" = "$PATH:/home/coder/.cargo/bin:/home/coder/.local/bin:/usr/local/bin" + "PATH" = "$PATH:/home/coder/bin:/home/coder/.cargo/bin:/home/coder/.local/bin:/usr/local/bin" "HOME" = "/home/coder" "USER" = "coder" + # Suppress NVM symlink warnings + "NVM_SYMLINK_CURRENT" = "false" + # Workspace ID for scripts + "CODER_WORKSPACE_ID" = local.workspace_id # Service URLs for development "POSTGRES_URL" = data.coder_parameter.enable_services.value ? "postgresql://postgres:${var.postgres_password}@postgres-${local.workspace_id}:5432/postgres" : "" "REDIS_URL" = data.coder_parameter.enable_services.value ? "redis://:${var.redis_password}@redis-${local.workspace_id}:6379" : "" @@ -35,14 +39,14 @@ resource "coder_agent" "main" { "CODER_WORKSPACE_REPO" = local.repo_url != "custom" ? local.repo_url : "" } - # Reference bind-mounted startup script plus service port forwarding - startup_script = "#!/bin/sh\necho 'Starting workspace...'" + # Reference bind-mounted startup script plus service port forwarding + startup_script = data.coder_parameter.enable_services.value ? "echo '${base64encode(local.port_forward_script)}' | base64 -d | tr -d '\\r' | bash" : "echo 'Starting workspace...'" # Performance and resource monitoring metadata { display_name = "CPU Usage" key = "cpu_usage" - script = "coder stat cpu" + script = "{ export NVM_SYMLINK_CURRENT=false; top -bn1 2>/dev/null | grep 'Cpu(s)' | awk '{print $2 \"%\"}' || echo 'N/A'; } 2>/dev/null" interval = 60 timeout = 10 } @@ -50,7 +54,7 @@ resource "coder_agent" "main" { metadata { display_name = "RAM Usage" key = "ram_usage" - script = "coder stat mem" + script = "{ export NVM_SYMLINK_CURRENT=false; free 2>/dev/null | grep Mem | awk '{printf \"%d%%\", int($3/$2 * 100)}' || echo 'N/A'; } 2>/dev/null" interval = 60 timeout = 10 } @@ -58,7 +62,7 @@ resource "coder_agent" "main" { metadata { display_name = "Disk Usage" key = "disk_usage" - script = "coder stat disk --path /workspaces" + script = "{ export NVM_SYMLINK_CURRENT=false; df -h /workspaces 2>/dev/null | tail -1 | awk '{print $5}' || echo 'N/A'; } 2>&1 | head -1" interval = 300 timeout = 10 } @@ -66,7 +70,7 @@ resource "coder_agent" "main" { metadata { display_name = "Git Branch" key = "git_branch" - script = "cd /workspaces && git branch --show-current 2>/dev/null || echo 'no-repo'" + script = "{ export NVM_SYMLINK_CURRENT=false; cd /workspaces && git branch --show-current 2>/dev/null || echo 'no-repo'; } 2>&1 | head -1" interval = 300 timeout = 5 }