feat: enhance workspace setup with port forwarding and improved service management

This commit is contained in:
2025-09-08 01:02:48 +00:00
parent 59fdf55518
commit d7fd50d7a3
8 changed files with 328 additions and 59 deletions

View File

@@ -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
}

View File

@@ -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
}
}

View File

@@ -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"
}

View File

@@ -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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
}