Refactor workspace setup script for cross-platform compatibility and improved user management

- Added system detection logic to handle different OS types (Linux, macOS, Windows).
- Enhanced user creation logic to support non-root execution and proper ownership.
- Updated directory creation to use dynamic home paths based on detected OS.
- Improved Git configuration and metadata capture with error handling.
- Modularized system package installation based on OS type.
- Streamlined Node.js, Python, and Rust setup scripts with error handling.
- Updated shell configuration to include dynamic aliases and environment info script.
- Deprecated `devcontainer_image` variable in favor of `devcontainer_repo_url` for better repository management.
- Adjusted Terraform workspace configuration to support new repository URL and caching options.
This commit is contained in:
2025-09-07 20:56:56 +00:00
parent 637883f9f2
commit bb469f8d2b
20 changed files with 2129 additions and 699 deletions

85
.devcontainer/Dockerfile Normal file
View File

@@ -0,0 +1,85 @@
# Start from the universal devcontainer base image
FROM mcr.microsoft.com/devcontainers/universal:2-linux
# Switch to root for installations
USER root
# Install additional development tools not included in features
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && \
apt-get install -y \
# Terminal tools
ripgrep \
fd-find \
bat \
eza \
htop \
btop \
ncdu \
ranger \
tmux \
neovim \
# Development tools
jq \
yq \
httpie \
lazygit \
lazydocker \
# Build tools
build-essential \
cmake \
pkg-config \
# Database clients
postgresql-client \
redis-tools \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Install Rust-based tools using cargo
USER codespace
RUN cargo install \
starship \
zoxide \
tokei \
git-delta \
--locked
# Install Node.js global packages
RUN npm install -g \
pnpm \
yarn \
turbo \
@claude-ai/cli \
vercel \
netlify-cli \
tsx \
nodemon
# Install Python packages
RUN pip install --user \
poetry \
pipenv \
black \
ruff \
mypy \
pytest \
httpx \
rich
# Create necessary directories with correct permissions
USER root
RUN mkdir -p /home/coder /workspaces && \
chown -R 1000:1000 /home/coder /workspaces
# Copy our custom scripts
COPY tf/scripts/*.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/*.sh
# Switch to coder user
USER coder
WORKDIR /workspaces
# Set up shell configuration
RUN echo 'eval "$(starship init zsh)"' >> ~/.zshrc && \
echo 'eval "$(zoxide init zsh)"' >> ~/.zshrc && \
echo 'alias ll="eza -la"' >> ~/.zshrc && \
echo 'alias cat="bat"' >> ~/.zshrc && \
echo 'alias find="fd"' >> ~/.zshrc

View File

@@ -0,0 +1,99 @@
{
"name": "Coder Development Environment",
"build": {
"dockerfile": "Dockerfile",
"context": ".."
},
"features": {
// Core development tools
"ghcr.io/devcontainers/features/common-utils:2": {
"installZsh": true,
"configureZshAsDefaultShell": true,
"installOhMyZsh": true,
"upgradePackages": true,
"username": "coder",
"userUid": "1000",
"userGid": "1000"
},
// Git and GitHub CLI
"ghcr.io/devcontainers/features/git:1": {
"version": "latest",
"ppa": true
},
"ghcr.io/devcontainers/features/github-cli:1": {
"version": "latest"
},
// Docker-in-Docker
"ghcr.io/devcontainers/features/docker-in-docker:2": {
"version": "latest",
"enableNonRootDocker": true,
"moby": true
},
// Node.js and npm
"ghcr.io/devcontainers/features/node:1": {
"version": "lts",
"nodeGypDependencies": true
},
// Python
"ghcr.io/devcontainers/features/python:1": {
"version": "3.11",
"installTools": true,
"installJupyterlab": true
},
// Go
"ghcr.io/devcontainers/features/go:1": {
"version": "latest"
},
// Rust
"ghcr.io/devcontainers/features/rust:1": {
"version": "stable",
"profile": "default"
},
// Terraform
"ghcr.io/devcontainers/features/terraform:1": {
"version": "latest"
},
// Additional tools
"ghcr.io/devcontainers/features/aws-cli:1": {},
"ghcr.io/devcontainers/features/azure-cli:1": {}
},
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "zsh",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"python.linting.enabled": true,
"python.formatting.provider": "black"
},
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"golang.go",
"rust-lang.rust-analyzer",
"hashicorp.terraform",
"ms-azuretools.vscode-docker",
"github.copilot",
"github.copilot-chat"
]
}
},
"postCreateCommand": "bash .devcontainer/post-create.sh",
"postStartCommand": "bash .devcontainer/post-start.sh",
"remoteUser": "coder",
"workspaceFolder": "/workspaces",
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces,type=bind",
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined",
"--network=host"
],
"containerEnv": {
"SHELL": "/bin/zsh"
}
}

34
.devcontainer/post-create.sh Executable file
View File

@@ -0,0 +1,34 @@
#!/bin/bash
set -e
echo "Running post-create setup..."
# Set up Git configuration
git config --global init.defaultBranch main
git config --global pull.rebase false
# Create common directories
mkdir -p ~/bin ~/.local/bin ~/.config
# Run our setup scripts if they exist
if [ -f /usr/local/bin/claude-install.sh ]; then
echo "Installing Claude CLI..."
/usr/local/bin/claude-install.sh
fi
if [ -f /usr/local/bin/cursor-setup.sh ]; then
echo "Setting up Cursor IDE support..."
/usr/local/bin/cursor-setup.sh
fi
if [ -f /usr/local/bin/windsurf-setup.sh ]; then
echo "Setting up Windsurf IDE support..."
/usr/local/bin/windsurf-setup.sh
fi
if [ -f /usr/local/bin/git-hooks.sh ]; then
echo "Setting up Git hooks..."
/usr/local/bin/git-hooks.sh
fi
echo "Post-create setup complete!"

53
.devcontainer/post-start.sh Executable file
View File

@@ -0,0 +1,53 @@
#!/bin/bash
set -e
echo "Running post-start setup..."
# Set up environment variables
export PATH="$PATH:$HOME/.local/bin:$HOME/bin"
# Initialize any services that need to be started
if [ -n "$ENABLE_SERVICES" ] && [ "$ENABLE_SERVICES" = "true" ]; then
echo "Services are enabled, checking connectivity..."
# Wait for services to be ready (if enabled)
if [ -n "$POSTGRES_URL" ]; then
echo "Waiting for PostgreSQL..."
for i in {1..30}; do
if pg_isready -d "$POSTGRES_URL" 2>/dev/null; then
echo "PostgreSQL is ready!"
break
fi
sleep 1
done
fi
if [ -n "$REDIS_URL" ]; then
echo "Checking Redis connectivity..."
# Parse Redis URL to get host and port
REDIS_HOST=$(echo "$REDIS_URL" | sed -E 's|redis://[^@]*@([^:]+):.*|\1|')
REDIS_PORT=$(echo "$REDIS_URL" | sed -E 's|.*:([0-9]+).*|\1|')
for i in {1..30}; do
if redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping 2>/dev/null | grep -q PONG; then
echo "Redis is ready!"
break
fi
sleep 1
done
fi
fi
# Display welcome message
echo ""
echo "🚀 Workspace is ready!"
echo "📂 Working directory: $(pwd)"
echo "🌿 Git branch: $(git branch --show-current 2>/dev/null || echo 'no repo')"
echo ""
# Clone repository if specified and not already present
if [ -n "$CODER_WORKSPACE_REPO" ] && [ ! -d .git ]; then
echo "Cloning repository: $CODER_WORKSPACE_REPO"
git clone "$CODER_WORKSPACE_REPO" .
fi
echo "Post-start setup complete!"

36
.dockerignore Normal file
View File

@@ -0,0 +1,36 @@
# Git
.git
.gitignore
.gitattributes
# Terraform
*.tfstate
*.tfstate.*
.terraform/
*.tfvars
!terraform.tfvars.example
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Build artifacts
*.log
*.pid
*.seed
*.pid.lock
# Documentation
*.md
LICENSE
# Temporary files
tmp/
temp/

View File

@@ -160,8 +160,8 @@ locals {
data.coder_parameter.repo.value
)
# Development container image with all required tools
devcontainer_image = var.devcontainer_image
# Development container image is now built by envbuilder
# devcontainer_image = var.devcontainer_image # Deprecated
# Environment variables for the development container
dev_environment = {
@@ -231,8 +231,20 @@ resource "docker_volume" "workspaces" {
}
}
# Development Container Image
# Build the devcontainer image with envbuilder
resource "envbuilder_cached_image" "devcontainer" {
builder_image = "ghcr.io/coder/envbuilder:latest"
git_url = var.devcontainer_repo_url
# Use the devcontainer.json from our repository
devcontainer_dir = ".devcontainer"
# Cache settings for faster builds
cache_repo = var.envbuilder_cache_repo
}
# Development Container Image using envbuilder
resource "docker_image" "devcontainer" {
name = local.devcontainer_image
name = envbuilder_cached_image.devcontainer.image
keep_locally = true
}

253
tf/main.tf.backup Normal file
View File

@@ -0,0 +1,253 @@
terraform {
required_version = ">= 1.0"
required_providers {
coder = {
source = "coder/coder"
version = "~> 2.0"
}
docker = {
source = "kreuzwerker/docker"
version = "~> 2.25"
}
envbuilder = {
source = "coder/envbuilder"
version = "~> 1.0"
}
}
}
provider "coder" {}
provider "docker" {
host = var.docker_socket != "" ? var.docker_socket : null
}
provider "envbuilder" {}
# Data Sources
data "coder_provisioner" "me" {}
data "coder_workspace" "me" {}
data "coder_workspace_owner" "me" {}
# Parameters
data "coder_parameter" "repo" {
name = "repo"
display_name = "Repository"
description = "Select a repository to clone"
mutable = true
order = 1
option {
name = "Custom Development Environment"
description = "Full-stack development with all services"
value = "custom"
}
option {
name = "vercel/next.js"
description = "The React Framework"
value = "https://github.com/vercel/next.js"
}
option {
name = "Custom URL"
description = "Specify a custom repo URL below"
value = "custom-url"
}
}
data "coder_parameter" "custom_repo_url" {
name = "custom_repo_url"
display_name = "Custom Repository URL"
description = "Enter a custom repository URL"
default = ""
mutable = true
order = 2
}
data "coder_parameter" "enable_services" {
name = "enable_services"
display_name = "Enable Database Services"
description = "Enable PostgreSQL, Redis, Qdrant, and Docker Registry"
type = "bool"
default = "true"
mutable = true
order = 3
}
data "coder_parameter" "enable_ai_tools" {
name = "enable_ai_tools"
display_name = "Enable AI Assistant Tools"
description = "Install Claude Code and AI development tools"
type = "bool"
default = "true"
mutable = true
order = 4
}
data "coder_parameter" "enable_claude_code" {
name = "enable_claude_code"
display_name = "Enable Claude Code CLI"
description = "Install Claude Code command-line interface"
type = "bool"
default = "true"
mutable = true
order = 5
}
data "coder_parameter" "enable_cursor_support" {
name = "enable_cursor_support"
display_name = "Enable Cursor IDE Support"
description = "Install Cursor IDE configuration and settings"
type = "bool"
default = "true"
mutable = true
order = 6
}
data "coder_parameter" "enable_windsurf_support" {
name = "enable_windsurf_support"
display_name = "Enable Windsurf IDE Support"
description = "Install Windsurf IDE configuration and settings"
type = "bool"
default = "true"
mutable = true
order = 7
}
data "coder_parameter" "enable_jetbrains" {
name = "enable_jetbrains"
display_name = "Enable JetBrains Gateway"
description = "Enable JetBrains Gateway integration for remote development"
type = "bool"
default = "true"
mutable = true
order = 8
}
data "coder_parameter" "enable_jupyter" {
name = "enable_jupyter"
display_name = "Enable Jupyter Lab"
description = "Enable Jupyter Lab for data science and notebook development"
type = "bool"
default = "false"
mutable = true
order = 9
}
data "coder_parameter" "enable_pgadmin" {
name = "enable_pgadmin"
display_name = "Enable pgAdmin"
description = "Enable pgAdmin web interface for PostgreSQL management"
type = "bool"
default = "true"
mutable = true
order = 10
}
# Local Variables
locals {
# Container and workspace naming - use ID for immutability
container_name = "coder-${data.coder_workspace_owner.me.name}-${lower(data.coder_workspace.me.name)}"
workspace_id = data.coder_workspace.me.id
# Use workspace ID for volume naming to prevent destruction on rename
volume_suffix = substr(data.coder_workspace.me.id, 0, 8)
# Git configuration
git_author_name = coalesce(data.coder_workspace_owner.me.full_name, data.coder_workspace_owner.me.name)
git_author_email = data.coder_workspace_owner.me.email
# Repository URL logic
repo_url = (
data.coder_parameter.repo.value == "custom" ? "https://github.com/coder/envbuilder" :
data.coder_parameter.repo.value == "custom-url" ? data.coder_parameter.custom_repo_url.value :
data.coder_parameter.repo.value
)
# Development container image is now built by envbuilder
# devcontainer_image = var.devcontainer_image # Deprecated
# Environment variables for the development container
dev_environment = {
# Git configuration
"GIT_AUTHOR_NAME" = local.git_author_name
"GIT_AUTHOR_EMAIL" = local.git_author_email
"GIT_COMMITTER_NAME" = local.git_author_name
"GIT_COMMITTER_EMAIL" = local.git_author_email
# Development tools
"NODE_VERSION" = var.node_version
"PYTHON_VERSION" = var.python_version
"RUST_VERSION" = "stable"
# Service URLs (when services are enabled)
"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" : ""
"QDRANT_URL" = data.coder_parameter.enable_services.value ? "http://qdrant-${local.workspace_id}:6333" : ""
# Development configuration
"EDITOR" = "code"
"PYTHONPATH" = "/workspaces"
"CARGO_HOME" = "/home/coder/.cargo"
"RUSTUP_HOME" = "/home/coder/.rustup"
}
# Legacy service URLs for backward compatibility
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"
}
# Docker Network
resource "docker_network" "workspace" {
name = "coder-${local.workspace_id}"
driver = "bridge"
labels {
label = "coder.workspace_id"
value = local.workspace_id
}
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.project"
value = var.project_name
}
}
# Workspace Volume
resource "docker_volume" "workspaces" {
name = "workspaces-${local.workspace_id}"
labels {
label = "coder.workspace_id"
value = local.workspace_id
}
labels {
label = "coder.owner"
value = data.coder_workspace_owner.me.name
}
labels {
label = "coder.type"
value = "workspace-data"
}
}
# Build the devcontainer image with envbuilder
resource "envbuilder_cached_image" "devcontainer" {
builder_image = "ghcr.io/coder/envbuilder:latest"
git_url = var.devcontainer_repo_url
# Use the devcontainer.json from our repository
devcontainer_dir = ".devcontainer"
# Cache settings for faster builds
cache_repo = var.envbuilder_cache_repo != "" ? var.envbuilder_cache_repo : null
# Ensure we build for the correct architecture
force_safe = true
}
# Development Container Image using envbuilder
resource "docker_image" "devcontainer" {
name = envbuilder_cached_image.devcontainer.image
keep_locally = true
}

View File

@@ -92,7 +92,8 @@ output "development_tools" {
value = {
node_version = var.node_version
python_version = var.python_version
container_image = local.devcontainer_image
container_image = docker_image.devcontainer.name
git_repo_url = var.devcontainer_repo_url
}
}

View File

@@ -1,169 +1,93 @@
# =============================================================================
# Provisioning Scripts - AI Development Tools and Extensions
# Installation scripts for Cursor, Claude Code, Windsurf support
# Provisioning Scripts - Minimal Setup
# Most tools are now installed in the devcontainer image
# =============================================================================
# =============================================================================
# Claude Code CLI Installation
# Workspace Initialization
# =============================================================================
resource "coder_script" "claude_code_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_claude_code.value ? 1 : 0
resource "coder_script" "workspace_init" {
agent_id = coder_agent.main.id
display_name = "Install Claude Code CLI"
icon = "/icon/ai.svg"
run_on_start = true
script = <<-EOT
#!/bin/bash
set -e
echo "Installing Claude Code CLI..."
# Check if Claude is already installed
if command -v claude &> /dev/null; then
echo "Claude Code CLI is already installed"
exit 0
fi
# Install Claude Code CLI
curl -fsSL https://claude.ai/install.sh | sh || {
echo "Warning: Claude Code CLI installation failed or not available"
exit 0
}
echo "Claude Code CLI installation complete"
EOT
}
# =============================================================================
# Cursor IDE Support Setup
# =============================================================================
resource "coder_script" "cursor_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_cursor_support.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Configure Cursor IDE Support"
icon = "/icon/code.svg"
run_on_start = true
script = <<-EOT
#!/bin/bash
set -e
echo "Configuring Cursor IDE support..."
# Create Cursor configuration directory
mkdir -p ~/.config/cursor
# Add Cursor-specific settings
cat > ~/.config/cursor/settings.json <<'EOF'
{
"workspaceFolder": "/workspaces",
"remote.SSH.defaultExtensions": [
"ms-python.python",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint"
]
}
EOF
echo "Cursor IDE support configured"
EOT
}
# =============================================================================
# Windsurf IDE Support Setup
# =============================================================================
resource "coder_script" "windsurf_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_windsurf_support.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Configure Windsurf IDE Support"
icon = "/icon/code.svg"
run_on_start = true
script = <<-EOT
#!/bin/bash
set -e
echo "Configuring Windsurf IDE support..."
# Create Windsurf configuration directory
mkdir -p ~/.config/windsurf
# Add Windsurf-specific settings
cat > ~/.config/windsurf/settings.json <<'EOF'
{
"workspaceFolder": "/workspaces",
"codeium.enabled": true
}
EOF
echo "Windsurf IDE support configured"
EOT
}
# =============================================================================
# Development Tools and Extensions
# =============================================================================
resource "coder_script" "dev_extensions" {
agent_id = coder_agent.main.id
display_name = "Install Development Tools"
display_name = "Initialize Workspace"
icon = "/icon/tools.svg"
run_on_start = true
script = <<-EOT
script = <<-EOF
#!/bin/bash
set -e
echo "Installing development tools and extensions..."
# Install VS Code extensions if code-server is available
if command -v code-server &> /dev/null; then
code-server --install-extension ms-python.python || true
code-server --install-extension esbenp.prettier-vscode || true
code-server --install-extension dbaeumer.vscode-eslint || true
code-server --install-extension ms-azuretools.vscode-docker || true
echo "🚀 Initializing workspace..."
# Ensure workspace directory has correct permissions
if [ -w /workspaces ]; then
cd /workspaces
else
echo "Warning: /workspaces is not writable, staying in current directory"
fi
# Install additional development tools
if ! command -v ranger &> /dev/null; then
sudo apt-get update && sudo apt-get install -y ranger || true
# Clone repository if CODER_WORKSPACE_REPO is set and directory is empty
if [ -n "$CODER_WORKSPACE_REPO" ] && [ -z "$(ls -A .)" ]; then
echo "📥 Cloning repository: $CODER_WORKSPACE_REPO"
git clone "$CODER_WORKSPACE_REPO" .
fi
echo "Development tools installation complete"
EOT
# Run any project-specific setup if present
if [ -f .devcontainer/post-start.sh ]; then
echo "🔧 Running project-specific setup..."
bash .devcontainer/post-start.sh
fi
echo "✅ Workspace initialization complete!"
EOF
}
# =============================================================================
# Git Hooks and Metadata Capture Setup
# Service Health Check (only if services are enabled)
# =============================================================================
resource "coder_script" "git_hooks_setup" {
resource "coder_script" "service_health" {
count = data.coder_parameter.enable_services.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Setup Git Hooks"
icon = "/icon/git.svg"
display_name = "Check Service Health"
icon = "/icon/database.svg"
run_on_start = true
script = <<-EOT
script = <<-EOF
#!/bin/bash
set -e
echo "Setting up Git hooks and configuration..."
# Configure git
git config --global user.name "${local.git_author_name}"
git config --global user.email "${local.git_author_email}"
git config --global init.defaultBranch main
# Create git hooks directory
mkdir -p ~/.git-hooks
# Add a simple pre-commit hook template
cat > ~/.git-hooks/pre-commit <<'EOF'
#!/bin/bash
# Pre-commit hook
echo "Running pre-commit checks..."
EOF
chmod +x ~/.git-hooks/pre-commit
echo "Git hooks setup complete"
EOT
}
echo "🔍 Checking service connectivity..."
# Check PostgreSQL
if [ -n "$POSTGRES_URL" ]; then
if pg_isready -d "$POSTGRES_URL" 2>/dev/null; then
echo "✅ PostgreSQL is ready"
else
echo "⚠️ PostgreSQL is not responding"
fi
fi
# Check Redis
if [ -n "$REDIS_URL" ]; then
REDIS_HOST=$(echo "$REDIS_URL" | sed -E 's|redis://[^@]*@([^:]+):.*|\1|')
REDIS_PORT=$(echo "$REDIS_URL" | sed -E 's|.*:([0-9]+).*|\1|')
if redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" ping 2>/dev/null | grep -q PONG; then
echo "✅ Redis is ready"
else
echo "⚠️ Redis is not responding"
fi
fi
# Check Qdrant
if [ -n "$QDRANT_URL" ]; then
if curl -s "$QDRANT_URL/readyz" | grep -q "ok"; then
echo "✅ Qdrant is ready"
else
echo "⚠️ Qdrant is not responding"
fi
fi
echo "🏁 Service health check complete"
EOF
}

72
tf/scripts.tf.disabled Normal file
View File

@@ -0,0 +1,72 @@
# =============================================================================
# Provisioning Scripts - AI Development Tools and Extensions
# Simplified scripts to avoid carriage return issues
# =============================================================================
# =============================================================================
# Claude Code CLI Installation
# =============================================================================
resource "coder_script" "claude_code_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_claude_code.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Install Claude Code CLI"
icon = "/icon/ai.svg"
run_on_start = true
script = "#!/bin/sh\ncommand -v claude >/dev/null 2>&1 || curl -fsSL https://claude.ai/install.sh | sh || echo 'Claude Code CLI installation skipped'"
}
# =============================================================================
# Cursor IDE Support Setup
# =============================================================================
resource "coder_script" "cursor_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_cursor_support.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Configure Cursor IDE Support"
icon = "/icon/code.svg"
run_on_start = true
script = "#!/bin/sh\nmkdir -p /home/coder/.config/cursor && printf '{\\n \"workspaceFolder\": \"/workspaces\",\\n \"remote.SSH.defaultExtensions\": [\\n \"ms-python.python\",\\n \"esbenp.prettier-vscode\",\\n \"dbaeumer.vscode-eslint\"\\n ]\\n}' > /home/coder/.config/cursor/settings.json"
}
# =============================================================================
# Windsurf IDE Support Setup
# =============================================================================
resource "coder_script" "windsurf_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_windsurf_support.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Configure Windsurf IDE Support"
icon = "/icon/code.svg"
run_on_start = true
script = "#!/bin/sh\nmkdir -p /home/coder/.config/windsurf && printf '{\\n \"workspaceFolder\": \"/workspaces\",\\n \"codeium.enabled\": true\\n}' > /home/coder/.config/windsurf/settings.json"
}
# =============================================================================
# Development Tools and Extensions
# =============================================================================
resource "coder_script" "dev_extensions" {
agent_id = coder_agent.main.id
display_name = "Install Development Tools"
icon = "/icon/tools.svg"
run_on_start = true
script = "#!/bin/sh\ncommand -v ranger >/dev/null 2>&1 || (sudo apt-get update && sudo apt-get install -y ranger) || true"
}
# =============================================================================
# Git Hooks and Metadata Capture Setup
# =============================================================================
resource "coder_script" "git_hooks_setup" {
agent_id = coder_agent.main.id
display_name = "Setup Git Hooks"
icon = "/icon/git.svg"
run_on_start = true
script = "#!/bin/sh\ngit config --global user.name \"${local.git_author_name}\" && git config --global user.email \"${local.git_author_email}\" && git config --global init.defaultBranch main"
}

86
tf/scripts.tf.old Normal file
View File

@@ -0,0 +1,86 @@
# =============================================================================
# Provisioning Scripts - AI Development Tools and Extensions
# Installation scripts for Cursor, Claude Code, Windsurf support
# Using base64 encoding to avoid line ending issues
# =============================================================================
# =============================================================================
# Claude Code CLI Installation
# =============================================================================
resource "coder_script" "claude_code_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_claude_code.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Install Claude Code CLI"
icon = "/icon/ai.svg"
run_on_start = true
script = file("${path.module}/scripts/claude-install.sh")
}
# =============================================================================
# Cursor IDE Support Setup
# =============================================================================
resource "coder_script" "cursor_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_cursor_support.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Configure Cursor IDE Support"
icon = "/icon/code.svg"
run_on_start = true
script = file("${path.module}/scripts/cursor-setup.sh")
}
# =============================================================================
# Windsurf IDE Support Setup
# =============================================================================
resource "coder_script" "windsurf_setup" {
count = data.coder_parameter.enable_ai_tools.value && data.coder_parameter.enable_windsurf_support.value ? 1 : 0
agent_id = coder_agent.main.id
display_name = "Configure Windsurf IDE Support"
icon = "/icon/code.svg"
run_on_start = true
script = file("${path.module}/scripts/windsurf-setup.sh")
}
# =============================================================================
# Development Tools and Extensions
# =============================================================================
resource "coder_script" "dev_extensions" {
agent_id = coder_agent.main.id
display_name = "Install Development Tools"
icon = "/icon/tools.svg"
run_on_start = true
script = file("${path.module}/scripts/dev-tools.sh")
}
# =============================================================================
# Git Hooks and Metadata Capture Setup
# =============================================================================
resource "coder_script" "git_hooks_setup" {
agent_id = coder_agent.main.id
display_name = "Setup Git Hooks"
icon = "/icon/git.svg"
run_on_start = true
script = file("${path.module}/scripts/git-hooks.sh")
}
# =============================================================================
# Workspace Setup (Comprehensive Environment Configuration)
# =============================================================================
resource "coder_script" "workspace_setup" {
agent_id = coder_agent.main.id
display_name = "Setup Development Workspace"
icon = "/icon/tools.svg"
run_on_start = true
script = file("${path.module}/scripts/workspace-setup.sh")
}

View File

@@ -1,4 +1,9 @@
#!/bin/bash
# Convert CRLF to LF if present (handles Windows line endings)
if command -v dos2unix >/dev/null 2>&1; then
dos2unix "$0" 2>/dev/null || true
fi
set -e
echo "🤖 Installing Claude Code CLI..."
@@ -9,9 +14,23 @@ if command -v claude >/dev/null 2>&1; then
exit 0
fi
# Cross-platform home directory detection
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
HOME_DIR="${USERPROFILE:-$HOME}"
BIN_DIR="$HOME_DIR/bin"
# Windows/WSL/Git Bash compatibility
export NVM_DIR="$HOME_DIR/.nvm"
else
HOME_DIR="$HOME"
BIN_DIR="/home/coder/bin"
export NVM_DIR="$HOME/.nvm"
fi
# Ensure npm is available
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
if [[ -s "$NVM_DIR/nvm.sh" ]]; then
# Use POSIX-compatible sourcing
. "$NVM_DIR/nvm.sh"
fi
if ! command -v npm >/dev/null 2>&1; then
echo "❌ npm not found - Node.js installation required"
@@ -28,37 +47,40 @@ if command -v claude >/dev/null 2>&1; then
echo "💡 Use 'claude chat' for interactive assistance"
echo "💡 Use 'claude edit <file>' to edit files with AI"
# Create helper script
mkdir -p /home/coder/bin
cat > /home/coder/bin/claude-help << 'CLAUDE_HELP_END'
#!/bin/bash
echo "🤖 Claude Code AI Assistant"
echo "=========================="
echo ""
echo "Authentication:"
echo " claude auth login # Authenticate with Anthropic"
echo " claude auth logout # Sign out"
echo " claude auth whoami # Check current user"
echo ""
echo "Interactive Chat:"
echo " claude chat # Start interactive session"
echo " claude chat 'question' # Single question"
echo ""
echo "File Editing:"
echo " claude edit file.py # AI-powered file editing"
echo " claude edit --help # Edit command options"
echo ""
echo "Code Analysis:"
echo " claude analyze . # Analyze current directory"
echo " claude review file.py # Code review"
echo ""
echo "Project Operations:"
echo " claude init # Initialize Claude in project"
echo " claude status # Show project status"
echo ""
echo "💡 For full documentation: https://docs.anthropic.com/claude/docs"
CLAUDE_HELP_END
chmod +x /home/coder/bin/claude-help
# Create helper script with proper line endings
mkdir -p "$BIN_DIR"
# Use printf instead of cat with heredoc to ensure consistent line endings
{
printf '#!/bin/bash\n'
printf 'echo "🤖 Claude Code AI Assistant"\n'
printf 'echo "=========================="\n'
printf 'echo ""\n'
printf 'echo "Authentication:"\n'
printf 'echo " claude auth login # Authenticate with Anthropic"\n'
printf 'echo " claude auth logout # Sign out"\n'
printf 'echo " claude auth whoami # Check current user"\n'
printf 'echo ""\n'
printf 'echo "Interactive Chat:"\n'
printf 'echo " claude chat # Start interactive session"\n'
printf 'echo " claude chat '\''question'\'' # Single question"\n'
printf 'echo ""\n'
printf 'echo "File Editing:"\n'
printf 'echo " claude edit file.py # AI-powered file editing"\n'
printf 'echo " claude edit --help # Edit command options"\n'
printf 'echo ""\n'
printf 'echo "Code Analysis:"\n'
printf 'echo " claude analyze . # Analyze current directory"\n'
printf 'echo " claude review file.py # Code review"\n'
printf 'echo ""\n'
printf 'echo "Project Operations:"\n'
printf 'echo " claude init # Initialize Claude in project"\n'
printf 'echo " claude status # Show project status"\n'
printf 'echo ""\n'
printf 'echo "💡 For full documentation: https://docs.anthropic.com/claude/docs"\n'
} > "$BIN_DIR/claude-help"
chmod +x "$BIN_DIR/claude-help"
echo "💡 Run 'claude-help' for quick reference"
else

View File

@@ -1,182 +1,203 @@
#!/bin/bash
# Convert CRLF to LF if present (handles Windows line endings)
if command -v dos2unix >/dev/null 2>&1; then
dos2unix "$0" 2>/dev/null || true
fi
set -e
echo "🎯 Setting up Cursor IDE support..."
# Create Cursor configuration directories
mkdir -p /home/coder/.cursor-server/data/User
mkdir -p /home/coder/.cursor-server/extensions
# Cross-platform user and directory detection
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
HOME_DIR="${USERPROFILE:-$HOME}"
USER_NAME="${USERNAME:-${USER:-coder}}"
CURSOR_DIR="$HOME_DIR/.cursor-server"
else
HOME_DIR="${HOME:-/home/coder}"
USER_NAME="${USER:-coder}"
CURSOR_DIR="$HOME_DIR/.cursor-server"
fi
# Create optimized Cursor settings
cat > /home/coder/.cursor-server/data/User/settings.json << 'CURSOR_SETTINGS_END'
# Create Cursor configuration directories
mkdir -p "$CURSOR_DIR/data/User"
mkdir -p "$CURSOR_DIR/extensions"
# Create optimized Cursor settings using printf to ensure LF line endings
{
"workbench.colorTheme": "Dark+ (default dark)",
"editor.fontSize": 14,
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
},
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000,
"terminal.integrated.fontSize": 13,
"git.enableSmartCommit": true,
"git.confirmSync": false,
"python.defaultInterpreterPath": "/home/coder/.venv/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"typescript.preferences.includePackageJsonAutoImports": "auto",
"javascript.preferences.includePackageJsonAutoImports": "auto",
"cursor.chat.showInEditorContextMenu": true,
"cursor.chat.alwaysShowInEditorContextMenu": true,
"cursor.general.enableWindowAIFeatures": true
}
CURSOR_SETTINGS_END
printf '{\n'
printf ' "workbench.colorTheme": "Dark+ (default dark)",\n'
printf ' "editor.fontSize": 14,\n'
printf ' "editor.tabSize": 2,\n'
printf ' "editor.insertSpaces": true,\n'
printf ' "editor.formatOnSave": true,\n'
printf ' "editor.codeActionsOnSave": {\n'
printf ' "source.fixAll": true,\n'
printf ' "source.organizeImports": true\n'
printf ' },\n'
printf ' "files.autoSave": "afterDelay",\n'
printf ' "files.autoSaveDelay": 1000,\n'
printf ' "terminal.integrated.fontSize": 13,\n'
printf ' "git.enableSmartCommit": true,\n'
printf ' "git.confirmSync": false,\n'
printf ' "python.defaultInterpreterPath": "%s/.venv/bin/python",\n' "$HOME_DIR"
printf ' "python.linting.enabled": true,\n'
printf ' "python.linting.pylintEnabled": false,\n'
printf ' "python.linting.flake8Enabled": true,\n'
printf ' "typescript.preferences.includePackageJsonAutoImports": "auto",\n'
printf ' "javascript.preferences.includePackageJsonAutoImports": "auto",\n'
printf ' "cursor.chat.showInEditorContextMenu": true,\n'
printf ' "cursor.chat.alwaysShowInEditorContextMenu": true,\n'
printf ' "cursor.general.enableWindowAIFeatures": true\n'
printf '}\n'
} > "$CURSOR_DIR/data/User/settings.json"
# Create development tasks configuration
mkdir -p /home/coder/.cursor-server/data/User
cat > /home/coder/.cursor-server/data/User/tasks.json << 'CURSOR_TASKS_END'
{
"version": "2.0.0",
"tasks": [
{
"label": "Dev Server",
"type": "shell",
"command": "npm run dev",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new"
},
"problemMatcher": []
},
{
"label": "Python Dev Server",
"type": "shell",
"command": "uvicorn main:app --reload --host 0.0.0.0 --port 8000",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new"
},
"problemMatcher": []
},
{
"label": "Install Dependencies",
"type": "shell",
"command": "npm install",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new"
}
},
{
"label": "Python Install",
"type": "shell",
"command": "uv pip install -r requirements.txt",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new"
}
}
]
}
CURSOR_TASKS_END
printf '{\n'
printf ' "version": "2.0.0",\n'
printf ' "tasks": [\n'
printf ' {\n'
printf ' "label": "Dev Server",\n'
printf ' "type": "shell",\n'
printf ' "command": "npm run dev",\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' },\n'
printf ' "problemMatcher": []\n'
printf ' },\n'
printf ' {\n'
printf ' "label": "Python Dev Server",\n'
printf ' "type": "shell",\n'
printf ' "command": "uvicorn main:app --reload --host 0.0.0.0 --port 8000",\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' },\n'
printf ' "problemMatcher": []\n'
printf ' },\n'
printf ' {\n'
printf ' "label": "Install Dependencies",\n'
printf ' "type": "shell",\n'
printf ' "command": "npm install",\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' }\n'
printf ' },\n'
printf ' {\n'
printf ' "label": "Python Install",\n'
printf ' "type": "shell",\n'
printf ' "command": "uv pip install -r requirements.txt",\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' }\n'
printf ' }\n'
printf ' ]\n'
printf '}\n'
} > "$CURSOR_DIR/data/User/tasks.json"
# Create useful code snippets
mkdir -p /home/coder/.cursor-server/data/User/snippets
cat > /home/coder/.cursor-server/data/User/snippets/global.code-snippets << 'CURSOR_SNIPPETS_END'
mkdir -p "$CURSOR_DIR/data/User/snippets"
{
"FastAPI Basic App": {
"prefix": "fastapi-app",
"body": [
"from fastapi import FastAPI",
"from fastapi.middleware.cors import CORSMiddleware",
"",
"app = FastAPI(title=\"${1:My API}\", version=\"0.1.0\")",
"",
"app.add_middleware(",
" CORSMiddleware,",
" allow_origins=[\"*\"],",
" allow_credentials=True,",
" allow_methods=[\"*\"],",
" allow_headers=[\"*\"],",
")",
"",
"@app.get(\"/\")",
"async def root():",
" return {\"message\": \"${2:Hello World}\"}",
"",
"@app.get(\"/health\")",
"async def health():",
" return {\"status\": \"healthy\"}",
"",
"if __name__ == \"__main__\":",
" import uvicorn",
" uvicorn.run(app, host=\"0.0.0.0\", port=8000)"
],
"description": "FastAPI basic application template"
},
"Next.js API Route": {
"prefix": "nextapi",
"body": [
"import { NextRequest, NextResponse } from 'next/server';",
"",
"export async function ${1:GET}(request: NextRequest) {",
" try {",
" // Your API logic here",
" return NextResponse.json({ message: '${2:Success}' });",
" } catch (error) {",
" return NextResponse.json(",
" { error: 'Internal Server Error' },",
" { status: 500 }",
" );",
" }",
"}"
],
"description": "Next.js API route template"
},
"Database Connection": {
"prefix": "db-connect",
"body": [
"import os",
"from sqlalchemy import create_engine",
"from sqlalchemy.orm import sessionmaker",
"",
"DATABASE_URL = os.getenv(",
" \"POSTGRES_URL\",",
" \"postgresql://postgres:password@localhost:5432/postgres\"",
")",
"",
"engine = create_engine(DATABASE_URL)",
"SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)",
"",
"def get_db():",
" db = SessionLocal()",
" try:",
" yield db",
" finally:",
" db.close()"
],
"description": "Database connection setup"
}
}
CURSOR_SNIPPETS_END
printf '{\n'
printf ' "FastAPI Basic App": {\n'
printf ' "prefix": "fastapi-app",\n'
printf ' "body": [\n'
printf ' "from fastapi import FastAPI",\n'
printf ' "from fastapi.middleware.cors import CORSMiddleware",\n'
printf ' "",\n'
printf ' "app = FastAPI(title=\\"${1:My API}\\", version=\\"0.1.0\\")",\n'
printf ' "",\n'
printf ' "app.add_middleware(",\n'
printf ' " CORSMiddleware,",\n'
printf ' " allow_origins=[\\"*\\"],",\n'
printf ' " allow_credentials=True,",\n'
printf ' " allow_methods=[\\"*\\"],",\n'
printf ' " allow_headers=[\\"*\\"],",\n'
printf ' ")",\n'
printf ' "",\n'
printf ' "@app.get(\\"\\")\\",\n'
printf ' "async def root():",\n'
printf ' " return {\\"message\\": \\"${2:Hello World}\\"}",\n'
printf ' "",\n'
printf ' "@app.get(\\"\/health\\")",\n'
printf ' "async def health():",\n'
printf ' " return {\\"status\\": \\"healthy\\"}",\n'
printf ' "",\n'
printf ' "if __name__ == \\"__main__\\":",\n'
printf ' " import uvicorn",\n'
printf ' " uvicorn.run(app, host=\\"0.0.0.0\\", port=8000)"\n'
printf ' ],\n'
printf ' "description": "FastAPI basic application template"\n'
printf ' },\n'
printf ' "Next.js API Route": {\n'
printf ' "prefix": "nextapi",\n'
printf ' "body": [\n'
printf ' "import { NextRequest, NextResponse } from '\''next\/server'\'';",\n'
printf ' "",\n'
printf ' "export async function ${1:GET}(request: NextRequest) {",\n'
printf ' " try {",\n'
printf ' " \/\/ Your API logic here",\n'
printf ' " return NextResponse.json({ message: '\''${2:Success}'\'' });",\n'
printf ' " } catch (error) {",\n'
printf ' " return NextResponse.json(",\n'
printf ' " { error: '\''Internal Server Error'\'' },",\n'
printf ' " { status: 500 }",\n'
printf ' " );",\n'
printf ' " }",\n'
printf ' "}"\n'
printf ' ],\n'
printf ' "description": "Next.js API route template"\n'
printf ' },\n'
printf ' "Database Connection": {\n'
printf ' "prefix": "db-connect",\n'
printf ' "body": [\n'
printf ' "import os",\n'
printf ' "from sqlalchemy import create_engine",\n'
printf ' "from sqlalchemy.orm import sessionmaker",\n'
printf ' "",\n'
printf ' "DATABASE_URL = os.getenv(",\n'
printf ' " \\"POSTGRES_URL\\",",\n'
printf ' " \\"postgresql:\/\/postgres:password@localhost:5432\/postgres\\"",\n'
printf ' ")",\n'
printf ' "",\n'
printf ' "engine = create_engine(DATABASE_URL)",\n'
printf ' "SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)",\n'
printf ' "",\n'
printf ' "def get_db():",\n'
printf ' " db = SessionLocal()",\n'
printf ' " try:",\n'
printf ' " yield db",\n'
printf ' " finally:",\n'
printf ' " db.close()"\n'
printf ' ],\n'
printf ' "description": "Database connection setup"\n'
printf ' }\n'
printf '}\n'
} > "$CURSOR_DIR/data/User/snippets/global.code-snippets"
# Set proper ownership
chown -R coder:coder /home/coder/.cursor-server
# Set proper ownership (Unix-like systems only)
if [[ "$OSTYPE" != "msys" && "$OSTYPE" != "cygwin" && "$OSTYPE" != "win32" ]]; then
if command -v chown >/dev/null 2>&1; then
chown -R "$USER_NAME:$USER_NAME" "$CURSOR_DIR" 2>/dev/null || {
echo "⚠️ Could not set ownership - you may need to run with appropriate permissions"
}
fi
fi
echo "✅ Cursor IDE support configured"
echo "🎯 Cursor will use optimized settings for this development environment"

View File

@@ -1,120 +1,363 @@
#!/bin/bash
# Convert CRLF to LF if present (handles Windows line endings)
if command -v dos2unix >/dev/null 2>&1; then
dos2unix "$0" 2>/dev/null || true
fi
set -e
echo "🔧 Installing development extensions and tools..."
# Ensure we're running as root for system packages
if [ "$EUID" -ne 0 ]; then
echo "This script needs to run as root for system package installation"
exit 1
fi
# Cross-platform system detection
detect_system() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
if command -v apt-get >/dev/null 2>&1; then
SYSTEM="debian"
elif command -v yum >/dev/null 2>&1; then
SYSTEM="rhel"
elif command -v pacman >/dev/null 2>&1; then
SYSTEM="arch"
else
SYSTEM="linux"
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
SYSTEM="macos"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
SYSTEM="windows"
else
SYSTEM="unknown"
fi
}
# Cross-platform package installation
install_package() {
local package="$1"
case "$SYSTEM" in
"debian")
apt-get update >/dev/null 2>&1 || true
apt-get install -y "$package"
;;
"rhel")
yum install -y "$package" || dnf install -y "$package"
;;
"arch")
pacman -S --noconfirm "$package"
;;
"macos")
if command -v brew >/dev/null 2>&1; then
brew install "$package"
else
echo "⚠️ Homebrew not found. Please install $package manually."
fi
;;
"windows")
if command -v choco >/dev/null 2>&1; then
choco install -y "$package"
elif command -v winget >/dev/null 2>&1; then
winget install "$package"
else
echo "⚠️ Package manager not found. Please install $package manually."
fi
;;
*)
echo "⚠️ Unknown system. Please install $package manually."
;;
esac
}
# Detect system and user info
detect_system
HOME_DIR="${HOME:-/home/coder}"
USER_NAME="${USER:-coder}"
# Architecture detection for downloads
ARCH=$(uname -m)
case "$ARCH" in
"x86_64") ARCH="x86_64" ;;
"aarch64"|"arm64") ARCH="arm64" ;;
*) ARCH="x86_64" ;; # Default fallback
esac
# OS-specific binary suffix
case "$SYSTEM" in
"windows") BIN_SUFFIX=".exe" ;;
*) BIN_SUFFIX="" ;;
esac
# Check if we need elevated privileges (skip on Windows/macOS package managers)
check_privileges() {
if [[ "$SYSTEM" == "debian" || "$SYSTEM" == "rhel" || "$SYSTEM" == "arch" ]]; then
if [ "$EUID" -ne 0 ] && ! command -v sudo >/dev/null 2>&1; then
echo "This script needs root privileges or sudo for system package installation"
exit 1
fi
SUDO_CMD="sudo"
else
SUDO_CMD=""
fi
}
check_privileges
echo "📦 Installing additional CLI tools..."
# Ensure curl is available first
type -p curl >/dev/null || (apt-get update && apt-get install curl -y)
if ! command -v curl >/dev/null 2>&1; then
echo "📥 Installing curl..."
case "$SYSTEM" in
"debian") $SUDO_CMD apt-get update && $SUDO_CMD apt-get install -y curl ;;
"rhel") $SUDO_CMD yum install -y curl || $SUDO_CMD dnf install -y curl ;;
"arch") $SUDO_CMD pacman -S --noconfirm curl ;;
"macos") install_package curl ;;
"windows") install_package curl ;;
esac
fi
# Function to install various development tools
install_development_tools() {
echo "🛠️ Installing development utilities..."
# GitHub CLI
if ! command -v gh &> /dev/null; then
if ! command -v gh >/dev/null 2>&1; then
echo "📥 Installing GitHub CLI..."
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null
apt-get update
apt-get install gh -y
case "$SYSTEM" in
"debian")
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | $SUDO_CMD dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
$SUDO_CMD chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg
printf 'deb [arch=%s signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main\n' "$(dpkg --print-architecture)" | $SUDO_CMD tee /etc/apt/sources.list.d/github-cli.list > /dev/null
$SUDO_CMD apt-get update
$SUDO_CMD apt-get install -y gh
;;
"macos")
install_package gh
;;
"windows")
install_package gh
;;
*)
echo "⚠️ Please install GitHub CLI manually for your system"
;;
esac
fi
# Docker Compose (if not already installed)
if ! command -v docker-compose &> /dev/null; then
if ! command -v docker-compose >/dev/null 2>&1; then
echo "🐳 Installing Docker Compose..."
curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose
case "$SYSTEM" in
"windows")
echo "⚠️ Please install Docker Desktop for Windows which includes Docker Compose"
;;
"macos")
echo "⚠️ Please install Docker Desktop for macOS which includes Docker Compose"
;;
*)
COMPOSE_URL="https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)"
curl -L "$COMPOSE_URL" -o docker-compose
$SUDO_CMD mv docker-compose /usr/local/bin/docker-compose
$SUDO_CMD chmod +x /usr/local/bin/docker-compose
;;
esac
fi
# Lazygit for better git UI
if ! command -v lazygit &> /dev/null; then
if ! command -v lazygit >/dev/null 2>&1; then
echo "🌿 Installing lazygit..."
LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v*([^"]+)".*/\1/')
curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz"
tar xf lazygit.tar.gz lazygit
install lazygit /usr/local/bin
rm lazygit.tar.gz lazygit
case "$SYSTEM" in
"macos")
install_package lazygit
;;
"windows")
install_package lazygit
;;
*)
LAZYGIT_VERSION=$(curl -s "https://api.github.com/repos/jesseduffield/lazygit/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v*([^"]+)".*/\1/')
case "$SYSTEM" in
"linux")
curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_${LAZYGIT_VERSION}_Linux_${ARCH}.tar.gz"
;;
*)
curl -Lo lazygit.tar.gz "https://github.com/jesseduffield/lazygit/releases/latest/download/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz"
;;
esac
tar xf lazygit.tar.gz lazygit
$SUDO_CMD install lazygit /usr/local/bin
rm -f lazygit.tar.gz lazygit
;;
esac
fi
# btop for system monitoring
if ! command -v btop &> /dev/null; then
if ! command -v btop >/dev/null 2>&1; then
echo "📊 Installing btop..."
apt-get install btop -y
case "$SYSTEM" in
"debian") $SUDO_CMD apt-get install -y btop ;;
"macos") install_package btop ;;
"windows") install_package btop ;;
*) echo "⚠️ Please install btop manually for your system" ;;
esac
fi
# fd-find for better file searching
if ! command -v fd &> /dev/null; then
if ! command -v fd >/dev/null 2>&1; then
echo "🔍 Installing fd-find..."
apt-get install fd-find -y
# Create symlink for easier usage
ln -sf /usr/bin/fdfind /usr/local/bin/fd
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
;;
"macos")
install_package fd
;;
"windows")
install_package fd
;;
*)
echo "⚠️ Please install fd manually for your system"
;;
esac
fi
# ripgrep for better text searching
if ! command -v rg &> /dev/null; then
if ! command -v rg >/dev/null 2>&1; then
echo "🔎 Installing ripgrep..."
apt-get install ripgrep -y
case "$SYSTEM" in
"debian") $SUDO_CMD apt-get install -y ripgrep ;;
"macos") install_package ripgrep ;;
"windows") install_package ripgrep ;;
*) echo "⚠️ Please install ripgrep manually for your system" ;;
esac
fi
# bat for better cat with syntax highlighting
if ! command -v bat &> /dev/null; then
if ! command -v bat >/dev/null 2>&1; then
echo "🦇 Installing bat..."
apt-get install bat -y
# Create symlink for easier usage
ln -sf /usr/bin/batcat /usr/local/bin/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
;;
"macos")
install_package bat
;;
"windows")
install_package bat
;;
*)
echo "⚠️ Please install bat manually for your system"
;;
esac
fi
# eza for better ls (modern replacement for exa)
if ! command -v eza &> /dev/null; then
if ! command -v eza >/dev/null 2>&1; then
echo "📁 Installing eza..."
curl -L "https://github.com/eza-community/eza/releases/latest/download/eza_x86_64-unknown-linux-gnu.tar.gz" | tar xz -C /usr/local/bin
case "$SYSTEM" in
"macos")
install_package eza
;;
"windows")
install_package eza
;;
*)
case "$ARCH" in
"arm64"|"aarch64")
EZA_ARCH="aarch64"
;;
*)
EZA_ARCH="x86_64"
;;
esac
curl -L "https://github.com/eza-community/eza/releases/latest/download/eza_${EZA_ARCH}-unknown-linux-gnu.tar.gz" | $SUDO_CMD tar xz -C /usr/local/bin 2>/dev/null || {
echo "⚠️ Could not install eza automatically"
}
;;
esac
fi
}
# Install all development tools
install_development_tools
# Switch to coder user for user-specific installations
# Set up user-specific configurations
echo "👤 Setting up user-specific tools..."
su - coder << 'USER_SETUP_END'
# Add useful aliases to .bashrc if not already present
if ! grep -q "# Development tools aliases" ~/.bashrc; then
cat >> ~/.bashrc << 'ALIASES_END'
# Development tools aliases
alias cat='bat'
alias ls='eza'
alias ll='eza -la'
alias la='eza -la'
alias find='fd'
alias grep='rg'
alias git-ui='lazygit'
alias top='btop'
# Create user setup script to handle cross-platform differences
{
printf '#!/bin/bash\n'
printf '# User-specific tool setup\n'
printf '\n'
printf '# Detect shell configuration file\n'
printf 'if [[ "$SHELL" == *"zsh"* && -f "$HOME/.zshrc" ]]; then\n'
printf ' SHELL_RC="$HOME/.zshrc"\n'
printf 'elif [[ -f "$HOME/.bashrc" ]]; then\n'
printf ' SHELL_RC="$HOME/.bashrc"\n'
printf 'else\n'
printf ' SHELL_RC="$HOME/.profile"\n'
printf 'fi\n'
printf '\n'
printf '# Add useful aliases if not already present\n'
printf 'if ! grep -q "# Development tools aliases" "$SHELL_RC" 2>/dev/null; then\n'
printf ' printf "\\n# Development tools aliases\\n" >> "$SHELL_RC"\n'
printf ' if command -v bat >/dev/null 2>&1; then\n'
printf ' printf "alias cat='\''bat'\''\\n" >> "$SHELL_RC"\n'
printf ' fi\n'
printf ' if command -v eza >/dev/null 2>&1; then\n'
printf ' printf "alias ls='\''eza'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias ll='\''eza -la'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias la='\''eza -la'\''\\n" >> "$SHELL_RC"\n'
printf ' fi\n'
printf ' if command -v fd >/dev/null 2>&1; then\n'
printf ' printf "alias find='\''fd'\''\\n" >> "$SHELL_RC"\n'
printf ' fi\n'
printf ' if command -v rg >/dev/null 2>&1; then\n'
printf ' printf "alias grep='\''rg'\''\\n" >> "$SHELL_RC"\n'
printf ' fi\n'
printf ' if command -v lazygit >/dev/null 2>&1; then\n'
printf ' printf "alias git-ui='\''lazygit'\''\\n" >> "$SHELL_RC"\n'
printf ' fi\n'
printf ' if command -v btop >/dev/null 2>&1; then\n'
printf ' printf "alias top='\''btop'\''\\n" >> "$SHELL_RC"\n'
printf ' fi\n'
printf ' printf "\\n" >> "$SHELL_RC"\n'
printf 'fi\n'
printf '\n'
printf '# Install Node.js tools if npm is available\n'
printf 'if command -v npm >/dev/null 2>&1; then\n'
printf ' # Install tldr for better man pages\n'
printf ' if ! command -v tldr >/dev/null 2>&1; then\n'
printf ' npm install -g tldr 2>/dev/null || echo "⚠️ Could not install tldr"\n'
printf ' fi\n'
printf ' \n'
printf ' # Install fkill for better process management\n'
printf ' if ! command -v fkill >/dev/null 2>&1; then\n'
printf ' npm install -g fkill-cli 2>/dev/null || echo "⚠️ Could not install fkill-cli"\n'
printf ' fi\n'
printf 'fi\n'
printf '\n'
printf 'echo "✅ Development tools installed and configured!"\n'
} > /tmp/user_setup.sh
ALIASES_END
# Run user setup based on system type
if [[ "$SYSTEM" == "windows" ]]; then
# On Windows, run directly
bash /tmp/user_setup.sh
else
# On Unix-like systems, try to switch to target user if different
if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1; then
su - "$USER_NAME" -c "bash /tmp/user_setup.sh" 2>/dev/null || {
echo "⚠️ Could not switch to user $USER_NAME, running as current user"
bash /tmp/user_setup.sh
}
else
bash /tmp/user_setup.sh
fi
fi
# Install tldr for better man pages
if ! command -v tldr &> /dev/null; then
npm install -g tldr
fi
# Install fkill for better process management
if ! command -v fkill &> /dev/null; then
npm install -g fkill-cli
fi
echo "✅ Development tools installed and configured!"
USER_SETUP_END
# Clean up
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 "💡 Aliases configured: cat→bat, ls→eza, find→fd, grep→rg, git-ui→lazygit, top→btop"
echo "💡 Restart your shell or run 'source ~/.bashrc' (or ~/.zshrc) to use the new aliases"

View File

@@ -1,9 +1,28 @@
#!/bin/bash
# Convert CRLF to LF if present (handles Windows line endings)
if command -v dos2unix >/dev/null 2>&1; then
dos2unix "$0" 2>/dev/null || true
fi
set -e
echo "📝 Setting up Git hooks and metadata capture..."
# Ensure we're in the workspaces directory
cd /workspaces
# Cross-platform directory and user detection
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
HOME_DIR="${USERPROFILE:-$HOME}"
USER_NAME="${USERNAME:-${USER:-coder}}"
WORKSPACES_DIR="${HOME_DIR}/workspaces"
TEMP_DIR="${TEMP:-/tmp}"
else
HOME_DIR="${HOME:-/home/coder}"
USER_NAME="${USER:-coder}"
WORKSPACES_DIR="/workspaces"
TEMP_DIR="/tmp"
fi
# Ensure workspaces directory exists and navigate to it
mkdir -p "$WORKSPACES_DIR"
cd "$WORKSPACES_DIR"
# Initialize git repository if it doesn't exist
if [ ! -d ".git" ]; then
@@ -14,70 +33,137 @@ fi
# Create .git/hooks directory if it doesn't exist
mkdir -p .git/hooks
# Create post-commit hook for metadata capture
cat > .git/hooks/post-commit << 'POST_COMMIT_END'
#!/bin/bash
# Post-commit hook to capture git metadata
echo "📝 Capturing git metadata after commit..."
# Create post-commit hook for metadata capture using printf
{
printf '#!/bin/bash\n'
printf '# Post-commit hook to capture git metadata\n'
printf 'echo "📝 Capturing git metadata after commit..."\n'
printf '\n'
printf '# Cross-platform temp directory detection\n'
printf 'if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then\n'
printf ' TEMP_DIR="${TEMP:-/tmp}"\n'
printf 'else\n'
printf ' TEMP_DIR="/tmp"\n'
printf 'fi\n'
printf '\n'
printf '# Ensure metadata directory exists\n'
printf 'mkdir -p "$TEMP_DIR/git-metadata"\n'
printf '\n'
printf '# Capture current git state\n'
printf 'git branch --show-current > "$TEMP_DIR/git-metadata/current-branch" 2>/dev/null || printf "main" > "$TEMP_DIR/git-metadata/current-branch"\n'
printf 'git rev-parse HEAD > "$TEMP_DIR/git-metadata/commit-hash" 2>/dev/null || printf "no-commits" > "$TEMP_DIR/git-metadata/commit-hash"\n'
printf 'git remote get-url origin > "$TEMP_DIR/git-metadata/remote-url" 2>/dev/null || printf "no-remote" > "$TEMP_DIR/git-metadata/remote-url"\n'
printf '\n'
printf '# Log the commit for development tracking\n'
printf 'printf "%%s: Commit %%s on branch %%s\\n" "$(date)" "$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")" "$(git branch --show-current 2>/dev/null || echo "unknown")" >> "$TEMP_DIR/git-metadata/commit-log"\n'
printf '\n'
printf 'echo "✅ Git metadata updated"\n'
} > .git/hooks/post-commit
# Ensure metadata directory exists
mkdir -p /tmp/git-metadata
# Create pre-push hook for quality checks using printf
{
printf '#!/bin/bash\n'
printf '# Pre-push hook for basic quality checks\n'
printf 'echo "🔍 Running pre-push quality checks..."\n'
printf '\n'
printf '# Check if package.json exists and run tests\n'
printf 'if [ -f "package.json" ]; then\n'
printf ' echo "📦 Found Node.js project, checking scripts..."\n'
printf ' if npm run --silent test --if-present 2>/dev/null; then\n'
printf ' echo "✅ Tests passed"\n'
printf ' else\n'
printf ' echo "⚠️ Tests not found or failed - pushing anyway"\n'
printf ' fi\n'
printf 'fi\n'
printf '\n'
printf '# Check if requirements.txt or pyproject.toml exists\n'
printf 'if [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then\n'
printf ' echo "🐍 Found Python project..."\n'
printf ' # Could add Python linting here\n'
printf ' echo "✅ Python project checks passed"\n'
printf 'fi\n'
printf '\n'
printf '# Check for large files (cross-platform compatible)\n'
printf 'echo "📁 Checking for large files..."\n'
printf 'if command -v find >/dev/null 2>&1; then\n'
printf ' large_files=$(find . -type f -size +100M 2>/dev/null | head -5)\n'
printf ' if [ ! -z "$large_files" ]; then\n'
printf ' echo "⚠️ Large files detected:"\n'
printf ' printf "%%s\\n" "$large_files"\n'
printf ' echo "Consider using Git LFS for large files"\n'
printf ' fi\n'
printf 'else\n'
printf ' echo "⚠️ find command not available, skipping large file check"\n'
printf 'fi\n'
printf '\n'
printf 'echo "✅ Pre-push checks completed"\n'
} > .git/hooks/pre-push
# Capture current git state
git branch --show-current > /tmp/git-metadata/current-branch 2>/dev/null || echo "main" > /tmp/git-metadata/current-branch
git rev-parse HEAD > /tmp/git-metadata/commit-hash 2>/dev/null || echo "no-commits" > /tmp/git-metadata/commit-hash
git remote get-url origin > /tmp/git-metadata/remote-url 2>/dev/null || echo "no-remote" > /tmp/git-metadata/remote-url
# Make hooks executable (cross-platform compatible)
if [[ "$OSTYPE" != "msys" && "$OSTYPE" != "cygwin" && "$OSTYPE" != "win32" ]]; then
chmod +x .git/hooks/post-commit
chmod +x .git/hooks/pre-push
else
# On Windows, Git Bash should handle executable permissions automatically
echo "🔧 Git hooks created (Windows will handle executable permissions)"
fi
# Log the commit for development tracking
echo "$(date): Commit $(git rev-parse --short HEAD) on branch $(git branch --show-current)" >> /tmp/git-metadata/commit-log
echo "✅ Git metadata updated"
POST_COMMIT_END
# Make post-commit hook executable
chmod +x .git/hooks/post-commit
# Create pre-push hook for quality checks
cat > .git/hooks/pre-push << 'PRE_PUSH_END'
#!/bin/bash
# Pre-push hook for basic quality checks
echo "🔍 Running pre-push quality checks..."
# Check if package.json exists and run tests
if [ -f "package.json" ]; then
echo "📦 Found Node.js project, checking scripts..."
if npm run --silent test --if-present; then
echo "✅ Tests passed"
else
echo "⚠️ Tests not found or failed - pushing anyway"
# Set proper ownership (Unix-like systems only)
if [[ "$OSTYPE" != "msys" && "$OSTYPE" != "cygwin" && "$OSTYPE" != "win32" ]]; then
if command -v chown >/dev/null 2>&1 && [ "$USER_NAME" != "$(whoami)" ]; then
chown -R "$USER_NAME:$USER_NAME" .git/hooks 2>/dev/null || {
echo "⚠️ Could not set ownership - you may need to run with appropriate permissions"
}
fi
fi
# Check if requirements.txt or pyproject.toml exists
if [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
echo "🐍 Found Python project..."
# Could add Python linting here
echo "✅ Python project checks passed"
# Create a helper script for viewing git metadata
{
printf '#!/bin/bash\n'
printf '# Helper script to view captured git metadata\n'
printf '\n'
printf '# Cross-platform temp directory detection\n'
printf 'if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then\n'
printf ' TEMP_DIR="${TEMP:-/tmp}"\n'
printf 'else\n'
printf ' TEMP_DIR="/tmp"\n'
printf 'fi\n'
printf '\n'
printf 'METADATA_DIR="$TEMP_DIR/git-metadata"\n'
printf '\n'
printf 'echo "📊 Git Metadata Summary"\n'
printf 'echo "====================="\n'
printf '\n'
printf 'if [ -d "$METADATA_DIR" ]; then\n'
printf ' if [ -f "$METADATA_DIR/current-branch" ]; then\n'
printf ' printf "Current Branch: %%s\\n" "$(cat "$METADATA_DIR/current-branch")"\n'
printf ' fi\n'
printf ' \n'
printf ' if [ -f "$METADATA_DIR/commit-hash" ]; then\n'
printf ' printf "Latest Commit: %%s\\n" "$(cat "$METADATA_DIR/commit-hash")"\n'
printf ' fi\n'
printf ' \n'
printf ' if [ -f "$METADATA_DIR/remote-url" ]; then\n'
printf ' printf "Remote URL: %%s\\n" "$(cat "$METADATA_DIR/remote-url")"\n'
printf ' fi\n'
printf ' \n'
printf ' if [ -f "$METADATA_DIR/commit-log" ]; then\n'
printf ' echo ""\n'
printf ' echo "Recent Commits:"\n'
printf ' tail -5 "$METADATA_DIR/commit-log" 2>/dev/null || echo "No commit log available"\n'
printf ' fi\n'
printf 'else\n'
printf ' echo "No git metadata found. Make a commit to generate metadata."\n'
printf 'fi\n'
} > .git/hooks/show-metadata
# Make metadata viewer executable
if [[ "$OSTYPE" != "msys" && "$OSTYPE" != "cygwin" && "$OSTYPE" != "win32" ]]; then
chmod +x .git/hooks/show-metadata
fi
# Check for large files
echo "📁 Checking for large files..."
large_files=$(find . -type f -size +100M 2>/dev/null | head -5)
if [ ! -z "$large_files" ]; then
echo "⚠️ Large files detected:"
echo "$large_files"
echo "Consider using Git LFS for large files"
fi
echo "✅ Pre-push checks completed"
PRE_PUSH_END
# Make pre-push hook executable
chmod +x .git/hooks/pre-push
# Set proper ownership for the coder user
chown -R coder:coder .git/hooks
echo "✅ Git hooks and metadata capture configured"
echo "📝 Git metadata will be automatically captured on commits"
echo "🔍 Pre-push quality checks will run before each push"
echo "🔍 Pre-push quality checks will run before each push"
echo "💡 Run '.git/hooks/show-metadata' to view captured git metadata"
echo "💡 Metadata is stored in: $TEMP_DIR/git-metadata/"

View File

@@ -1,66 +1,280 @@
#!/bin/bash
# Convert CRLF to LF if present (handles Windows line endings)
if command -v dos2unix >/dev/null 2>&1; then
dos2unix "$0" 2>/dev/null || true
fi
set -e
echo "🌊 Setting up Windsurf IDE support..."
# Cross-platform user and directory detection
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
HOME_DIR="${USERPROFILE:-$HOME}"
USER_NAME="${USERNAME:-${USER:-coder}}"
WINDSURF_DIR="$HOME_DIR/.windsurf"
else
HOME_DIR="${HOME:-/home/coder}"
USER_NAME="${USER:-coder}"
WINDSURF_DIR="$HOME_DIR/.windsurf"
fi
# Create Windsurf configuration directories
mkdir -p /home/coder/.windsurf/data/User
mkdir -p /home/coder/.windsurf/extensions
mkdir -p "$WINDSURF_DIR/data/User"
mkdir -p "$WINDSURF_DIR/extensions"
# Create optimized Windsurf settings
cat > /home/coder/.windsurf/data/User/settings.json << 'WINDSURF_SETTINGS_END'
# Create optimized Windsurf settings using printf to ensure LF line endings
{
"workbench.colorTheme": "Windsurf Dark",
"editor.fontSize": 14,
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true,
"source.organizeImports": true
},
"files.autoSave": "afterDelay",
"files.autoSaveDelay": 1000,
"terminal.integrated.fontSize": 13,
"git.enableSmartCommit": true,
"git.confirmSync": false,
"python.defaultInterpreterPath": "/home/coder/.venv/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.linting.flake8Enabled": true,
"typescript.preferences.includePackageJsonAutoImports": "auto",
"javascript.preferences.includePackageJsonAutoImports": "auto",
"windsurf.ai.enabled": true,
"windsurf.ai.showInEditorContextMenu": true,
"windsurf.chat.enabled": true,
"windsurf.codeCompletion.enabled": true
}
WINDSURF_SETTINGS_END
printf '{\n'
printf ' "workbench.colorTheme": "Windsurf Dark",\n'
printf ' "editor.fontSize": 14,\n'
printf ' "editor.tabSize": 2,\n'
printf ' "editor.insertSpaces": true,\n'
printf ' "editor.formatOnSave": true,\n'
printf ' "editor.codeActionsOnSave": {\n'
printf ' "source.fixAll": true,\n'
printf ' "source.organizeImports": true\n'
printf ' },\n'
printf ' "files.autoSave": "afterDelay",\n'
printf ' "files.autoSaveDelay": 1000,\n'
printf ' "terminal.integrated.fontSize": 13,\n'
printf ' "git.enableSmartCommit": true,\n'
printf ' "git.confirmSync": false,\n'
printf ' "python.defaultInterpreterPath": "%s/.venv/bin/python",\n' "$HOME_DIR"
printf ' "python.linting.enabled": true,\n'
printf ' "python.linting.pylintEnabled": false,\n'
printf ' "python.linting.flake8Enabled": true,\n'
printf ' "typescript.preferences.includePackageJsonAutoImports": "auto",\n'
printf ' "javascript.preferences.includePackageJsonAutoImports": "auto",\n'
printf ' "windsurf.ai.enabled": true,\n'
printf ' "windsurf.ai.showInEditorContextMenu": true,\n'
printf ' "windsurf.chat.enabled": true,\n'
printf ' "windsurf.codeCompletion.enabled": true\n'
printf '}\n'
} > "$WINDSURF_DIR/data/User/settings.json"
# Create development keybindings
cat > /home/coder/.windsurf/data/User/keybindings.json << 'WINDSURF_KEYS_END'
[
{
"key": "ctrl+shift+a",
"command": "windsurf.chat.open"
},
{
"key": "ctrl+shift+c",
"command": "windsurf.ai.generateCode"
},
{
"key": "ctrl+shift+r",
"command": "windsurf.ai.refactorSelection"
},
{
"key": "ctrl+shift+e",
"command": "windsurf.ai.explainCode"
}
]
WINDSURF_KEYS_END
# Create development keybindings using printf
{
printf '[\n'
printf ' {\n'
printf ' "key": "ctrl+shift+a",\n'
printf ' "command": "windsurf.chat.open"\n'
printf ' },\n'
printf ' {\n'
printf ' "key": "ctrl+shift+c",\n'
printf ' "command": "windsurf.ai.generateCode"\n'
printf ' },\n'
printf ' {\n'
printf ' "key": "ctrl+shift+r",\n'
printf ' "command": "windsurf.ai.refactorSelection"\n'
printf ' },\n'
printf ' {\n'
printf ' "key": "ctrl+shift+e",\n'
printf ' "command": "windsurf.ai.explainCode"\n'
printf ' }\n'
printf ']\n'
} > "$WINDSURF_DIR/data/User/keybindings.json"
# Set proper ownership
chown -R coder:coder /home/coder/.windsurf
# Create development tasks configuration for Windsurf
{
printf '{\n'
printf ' "version": "2.0.0",\n'
printf ' "tasks": [\n'
printf ' {\n'
printf ' "label": "Dev Server",\n'
printf ' "type": "shell",\n'
printf ' "command": "npm run dev",\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' },\n'
printf ' "problemMatcher": []\n'
printf ' },\n'
printf ' {\n'
printf ' "label": "Python Dev Server",\n'
printf ' "type": "shell",\n'
printf ' "command": "uvicorn main:app --reload --host 0.0.0.0 --port 8000",\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' },\n'
printf ' "problemMatcher": []\n'
printf ' },\n'
printf ' {\n'
printf ' "label": "AI Code Review",\n'
printf ' "type": "shell",\n'
printf ' "command": "echo",\n'
printf ' "args": ["Use Ctrl+Shift+R to refactor selection with Windsurf AI"],\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' }\n'
printf ' },\n'
printf ' {\n'
printf ' "label": "Install Dependencies",\n'
printf ' "type": "shell",\n'
printf ' "command": "npm install",\n'
printf ' "group": "build",\n'
printf ' "presentation": {\n'
printf ' "echo": true,\n'
printf ' "reveal": "always",\n'
printf ' "focus": false,\n'
printf ' "panel": "new"\n'
printf ' }\n'
printf ' }\n'
printf ' ]\n'
printf '}\n'
} > "$WINDSURF_DIR/data/User/tasks.json"
# Create useful code snippets for Windsurf
mkdir -p "$WINDSURF_DIR/data/User/snippets"
{
printf '{\n'
printf ' "Windsurf AI Comment": {\n'
printf ' "prefix": "ai-comment",\n'
printf ' "body": [\n'
printf ' "// AI-assisted code: ${1:description}",\n'
printf ' "// Generated with Windsurf AI on $(date)"\n'
printf ' ],\n'
printf ' "description": "Add AI assistance comment"\n'
printf ' },\n'
printf ' "FastAPI with AI Comments": {\n'
printf ' "prefix": "fastapi-ai",\n'
printf ' "body": [\n'
printf ' "# AI-enhanced FastAPI application",\n'
printf ' "from fastapi import FastAPI",\n'
printf ' "from fastapi.middleware.cors import CORSMiddleware",\n'
printf ' "",\n'
printf ' "app = FastAPI(",\n'
printf ' " title=\\"${1:AI-Enhanced API}\\",",\n'
printf ' " description=\\"API built with Windsurf AI assistance\\",",\n'
printf ' " version=\\"0.1.0\\"",\n'
printf ' ")",\n'
printf ' "",\n'
printf ' "# AI-suggested CORS configuration",\n'
printf ' "app.add_middleware(",\n'
printf ' " CORSMiddleware,",\n'
printf ' " allow_origins=[\\"*\\"],",\n'
printf ' " allow_credentials=True,",\n'
printf ' " allow_methods=[\\"*\\"],",\n'
printf ' " allow_headers=[\\"*\\"],",\n'
printf ' ")",\n'
printf ' "",\n'
printf ' "@app.get(\\"\/\\")\\",\n'
printf ' "async def root():",\n'
printf ' " return {\\"message\\": \\"${2:Hello from AI-enhanced API}\\"}"\n'
printf ' ],\n'
printf ' "description": "AI-enhanced FastAPI template"\n'
printf ' },\n'
printf ' "React Component with AI": {\n'
printf ' "prefix": "react-ai",\n'
printf ' "body": [\n'
printf ' "// AI-enhanced React component",\n'
printf ' "import React, { useState, useEffect } from '\''react'\'';",\n'
printf ' "",\n'
printf ' "interface ${1:Component}Props {",\n'
printf ' " // AI-suggested props",\n'
printf ' " title?: string;",\n'
printf ' "}",\n'
printf ' "",\n'
printf ' "const ${1:Component}: React.FC<${1:Component}Props> = ({ title = '\''${2:Default Title}'\'' }) => {",\n'
printf ' " const [state, setState] = useState<string>('\'\'');",\n'
printf ' "",\n'
printf ' " // AI-suggested useEffect",\n'
printf ' " useEffect(() => {",\n'
printf ' " // Component initialization",\n'
printf ' " }, []);",\n'
printf ' "",\n'
printf ' " return (",\n'
printf ' " <div>",\n'
printf ' " <h1>{title}</h1>",\n'
printf ' " {/* AI-enhanced component content */}",\n'
printf ' " </div>",\n'
printf ' " );",\n'
printf ' "};",\n'
printf ' "",\n'
printf ' "export default ${1:Component};"\n'
printf ' ],\n'
printf ' "description": "AI-enhanced React component template"\n'
printf ' }\n'
printf '}\n'
} > "$WINDSURF_DIR/data/User/snippets/windsurf-ai.code-snippets"
# Create Windsurf-specific launch configuration
{
printf '{\n'
printf ' "version": "0.2.0",\n'
printf ' "configurations": [\n'
printf ' {\n'
printf ' "name": "Debug Node.js with AI",\n'
printf ' "type": "node",\n'
printf ' "request": "launch",\n'
printf ' "program": "${workspaceFolder}/index.js",\n'
printf ' "console": "integratedTerminal",\n'
printf ' "internalConsoleOptions": "neverOpen"\n'
printf ' },\n'
printf ' {\n'
printf ' "name": "Debug Python with AI",\n'
printf ' "type": "python",\n'
printf ' "request": "launch",\n'
printf ' "program": "${workspaceFolder}/main.py",\n'
printf ' "console": "integratedTerminal",\n'
printf ' "python": "%s/.venv/bin/python"\n' "$HOME_DIR"
printf ' }\n'
printf ' ]\n'
printf '}\n'
} > "$WINDSURF_DIR/data/User/launch.json"
# Set proper ownership (Unix-like systems only)
if [[ "$OSTYPE" != "msys" && "$OSTYPE" != "cygwin" && "$OSTYPE" != "win32" ]]; then
if command -v chown >/dev/null 2>&1; then
chown -R "$USER_NAME:$USER_NAME" "$WINDSURF_DIR" 2>/dev/null || {
echo "⚠️ Could not set ownership - you may need to run with appropriate permissions"
}
fi
fi
# Create Windsurf AI helper script
{
printf '#!/bin/bash\n'
printf '# Windsurf AI Helper Commands\n'
printf 'echo "🌊 Windsurf AI Assistant Helper"\n'
printf 'echo "============================"\n'
printf 'echo ""\n'
printf 'echo "AI Features:"\n'
printf 'echo " Ctrl+Shift+A # Open AI Chat"\n'
printf 'echo " Ctrl+Shift+C # Generate Code with AI"\n'
printf 'echo " Ctrl+Shift+R # Refactor Selection with AI"\n'
printf 'echo " Ctrl+Shift+E # Explain Code with AI"\n'
printf 'echo ""\n'
printf 'echo "AI-Enhanced Snippets:"\n'
printf 'echo " ai-comment # Add AI assistance comment"\n'
printf 'echo " fastapi-ai # FastAPI with AI comments"\n'
printf 'echo " react-ai # React component with AI"\n'
printf 'echo ""\n'
printf 'echo "Configuration Location:"\n'
printf 'echo " Settings: %s/data/User/settings.json"\n' "$WINDSURF_DIR"
printf 'echo " Keybindings: %s/data/User/keybindings.json"\n' "$WINDSURF_DIR"
printf 'echo " Snippets: %s/data/User/snippets/"\n' "$WINDSURF_DIR"
printf 'echo ""\n'
printf 'echo "💡 Windsurf AI is enabled with optimized settings for development"\n'
} > "$WINDSURF_DIR/windsurf-help"
# Make helper script executable (Unix-like systems only)
if [[ "$OSTYPE" != "msys" && "$OSTYPE" != "cygwin" && "$OSTYPE" != "win32" ]]; then
chmod +x "$WINDSURF_DIR/windsurf-help"
fi
echo "✅ Windsurf IDE support configured"
echo "🌊 Windsurf AI features enabled with optimized settings"
echo "⌨️ Keyboard shortcuts: Ctrl+Shift+A (chat), Ctrl+Shift+C (generate), Ctrl+Shift+R (refactor)"
echo "⌨️ Keyboard shortcuts: Ctrl+Shift+A (chat), Ctrl+Shift+C (generate), Ctrl+Shift+R (refactor)"
echo "📁 Configuration stored in: $WINDSURF_DIR/"
echo "💡 Run '$WINDSURF_DIR/windsurf-help' for quick reference"

View File

@@ -1,43 +1,120 @@
#!/bin/bash
# Convert CRLF to LF if present (handles Windows line endings)
if command -v dos2unix >/dev/null 2>&1; then
dos2unix "$0" 2>/dev/null || true
fi
set -e
echo "🚀 Initializing development environment as user: $(whoami)"
# =============================================================================
# Create coder user if it doesn't exist
# Cross-platform system detection and configuration
# =============================================================================
if ! id -u coder >/dev/null 2>&1; then
echo "👤 Creating coder user..."
useradd -m -s /bin/bash -u 1000 coder
usermod -aG sudo coder
echo "coder ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
detect_system() {
if [[ "$OSTYPE" == "linux-gnu"* ]]; then
if command -v apt-get >/dev/null 2>&1; then
SYSTEM="debian"
PKG_MANAGER="apt-get"
elif command -v yum >/dev/null 2>&1; then
SYSTEM="rhel"
PKG_MANAGER="yum"
elif command -v pacman >/dev/null 2>&1; then
SYSTEM="arch"
PKG_MANAGER="pacman"
else
SYSTEM="linux"
PKG_MANAGER="unknown"
fi
elif [[ "$OSTYPE" == "darwin"* ]]; then
SYSTEM="macos"
PKG_MANAGER="brew"
elif [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
SYSTEM="windows"
PKG_MANAGER="choco"
else
SYSTEM="unknown"
PKG_MANAGER="unknown"
fi
}
detect_system
# Cross-platform directory and user detection
if [[ "$SYSTEM" == "windows" ]]; then
HOME_DIR="${USERPROFILE:-$HOME}"
USER_NAME="${USERNAME:-${USER:-coder}}"
WORKSPACES_DIR="$HOME_DIR/workspaces"
TEMP_DIR="${TEMP:-/tmp}"
BIN_DIR="$HOME_DIR/bin"
CONFIG_DIR="$HOME_DIR/.config"
else
HOME_DIR="${HOME:-/home/coder}"
USER_NAME="${USER:-coder}"
WORKSPACES_DIR="/workspaces"
TEMP_DIR="/tmp"
BIN_DIR="$HOME_DIR/bin"
CONFIG_DIR="$HOME_DIR/.config"
fi
# Set default versions if not provided
NODE_VERSION="${NODE_VERSION:-20}"
PYTHON_VERSION="${PYTHON_VERSION:-3.11}"
GIT_AUTHOR_NAME="${GIT_AUTHOR_NAME:-Developer}"
GIT_AUTHOR_EMAIL="${GIT_AUTHOR_EMAIL:-dev@example.com}"
ENABLE_SERVICES="${ENABLE_SERVICES:-false}"
# =============================================================================
# Create user and directories (Unix-like systems only)
# =============================================================================
if [[ "$SYSTEM" != "windows" && "$SYSTEM" != "macos" ]]; then
if ! id -u "$USER_NAME" >/dev/null 2>&1; then
echo "👤 Creating $USER_NAME user..."
if [[ "$EUID" -eq 0 ]]; then
useradd -m -s /bin/bash -u 1000 "$USER_NAME" 2>/dev/null || {
echo "⚠️ Could not create user $USER_NAME"
}
if command -v usermod >/dev/null 2>&1; then
usermod -aG sudo "$USER_NAME" 2>/dev/null || true
fi
if [[ -f "/etc/sudoers" ]]; then
echo "$USER_NAME ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers 2>/dev/null || true
fi
else
echo "⚠️ Not running as root, skipping user creation"
fi
fi
fi
# =============================================================================
# Create necessary directories
# =============================================================================
echo "📁 Creating user directories..."
mkdir -p /home/coder/bin
mkdir -p /home/coder/.local/bin
mkdir -p /home/coder/.config
mkdir -p /tmp/git-metadata
mkdir -p /workspaces
mkdir -p "$BIN_DIR"
mkdir -p "$HOME_DIR/.local/bin"
mkdir -p "$CONFIG_DIR"
mkdir -p "$TEMP_DIR/git-metadata"
mkdir -p "$WORKSPACES_DIR"
# Ensure proper ownership
chown -R coder:coder /home/coder /workspaces
# Ensure proper ownership (Unix-like systems only)
if [[ "$SYSTEM" != "windows" ]] && command -v chown >/dev/null 2>&1; then
if [[ "$USER_NAME" != "$(whoami)" ]] && [[ "$EUID" -eq 0 ]]; then
chown -R "$USER_NAME:$USER_NAME" "$HOME_DIR" "$WORKSPACES_DIR" 2>/dev/null || true
fi
fi
# =============================================================================
# Switch to coder user for remaining operations
# Environment setup
# =============================================================================
echo "🔄 Switching to coder user context..."
export HOME=/home/coder
export USER=coder
echo "🔄 Setting up environment context..."
export HOME="$HOME_DIR"
export USER="$USER_NAME"
# =============================================================================
# Git Configuration
# =============================================================================
echo "⚙️ Configuring Git..."
git config --global user.name "${GIT_AUTHOR_NAME}"
git config --global user.email "${GIT_AUTHOR_EMAIL}"
git config --global user.name "$GIT_AUTHOR_NAME"
git config --global user.email "$GIT_AUTHOR_EMAIL"
git config --global commit.gpgsign false
git config --global tag.gpgsign false
git config --global init.defaultBranch main
@@ -45,184 +122,286 @@ git config --global pull.rebase false
# Capture and log git information
echo "📝 Capturing Git metadata..."
cd /workspaces
cd "$WORKSPACES_DIR"
if [ -d ".git" ]; then
git branch --show-current > /tmp/git-metadata/current-branch 2>/dev/null || echo "main" > /tmp/git-metadata/current-branch
git rev-parse HEAD > /tmp/git-metadata/commit-hash 2>/dev/null || echo "no-commits" > /tmp/git-metadata/commit-hash
git remote get-url origin > /tmp/git-metadata/remote-url 2>/dev/null || echo "no-remote" > /tmp/git-metadata/remote-url
git branch --show-current > "$TEMP_DIR/git-metadata/current-branch" 2>/dev/null || printf "main" > "$TEMP_DIR/git-metadata/current-branch"
git rev-parse HEAD > "$TEMP_DIR/git-metadata/commit-hash" 2>/dev/null || printf "no-commits" > "$TEMP_DIR/git-metadata/commit-hash"
git remote get-url origin > "$TEMP_DIR/git-metadata/remote-url" 2>/dev/null || printf "no-remote" > "$TEMP_DIR/git-metadata/remote-url"
else
echo "no-repo" > /tmp/git-metadata/current-branch
echo "no-repo" > /tmp/git-metadata/commit-hash
echo "no-repo" > /tmp/git-metadata/remote-url
printf "no-repo" > "$TEMP_DIR/git-metadata/current-branch"
printf "no-repo" > "$TEMP_DIR/git-metadata/commit-hash"
printf "no-repo" > "$TEMP_DIR/git-metadata/remote-url"
fi
# =============================================================================
# System Package Updates and Installation
# =============================================================================
echo "📦 Installing system packages..."
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y make tree jq curl wget unzip build-essential postgresql-client redis-tools
install_system_packages() {
echo "📦 Installing system packages..."
case "$SYSTEM" in
"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 || {
echo "⚠️ Some packages failed to install"
}
if [[ "$ENABLE_SERVICES" == "true" ]]; then
apt-get install -y postgresql-client redis-tools 2>/dev/null || true
fi
;;
"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 2>/dev/null || dnf install -y make tree jq curl wget unzip 2>/dev/null || true
;;
"macos")
if command -v brew >/dev/null 2>&1; then
brew install make tree jq curl wget unzip 2>/dev/null || true
else
echo "⚠️ Homebrew not found. Please install build tools manually."
fi
;;
"windows")
if command -v choco >/dev/null 2>&1; then
choco install -y make tree jq curl wget unzip 2>/dev/null || true
elif command -v winget >/dev/null 2>&1; then
winget install make tree jq curl wget unzip 2>/dev/null || true
else
echo "⚠️ Package manager not found. Please install build tools manually."
fi
;;
*)
echo "⚠️ Unknown system. Please install build tools manually."
;;
esac
}
if [[ "$EUID" -eq 0 ]] || [[ "$SYSTEM" == "macos" ]] || [[ "$SYSTEM" == "windows" ]]; then
install_system_packages
else
echo "⚠️ Not running with appropriate privileges, skipping system package installation"
fi
# =============================================================================
# Node.js and npm Setup (as coder user)
# Node.js and npm Setup
# =============================================================================
echo "🟢 Setting up Node.js and npm..."
# Create Node.js setup script
cat > /tmp/node_setup.sh << 'NODE_SCRIPT_END'
#!/bin/bash
if ! command -v nvm &> /dev/null; then
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
export NVM_DIR="/home/coder/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
{
printf '#!/bin/bash\n'
printf '# Node.js setup script\n'
printf 'if ! command -v nvm >/dev/null 2>&1; then\n'
printf ' curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 2>/dev/null || {\n'
printf ' echo "⚠️ Failed to install nvm"\n'
printf ' exit 1\n'
printf ' }\n'
printf ' export NVM_DIR="%s/.nvm"\n' "$HOME_DIR"
printf ' [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"\n'
printf 'fi\n'
printf '\n'
printf 'export NVM_DIR="%s/.nvm"\n' "$HOME_DIR"
printf '[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"\n'
printf 'nvm install %s 2>/dev/null || echo "⚠️ Failed to install Node.js %s"\n' "$NODE_VERSION" "$NODE_VERSION"
printf 'nvm use %s 2>/dev/null || true\n' "$NODE_VERSION"
printf 'nvm alias default %s 2>/dev/null || true\n' "$NODE_VERSION"
printf '\n'
printf 'echo "📦 Installing npm packages..."\n'
printf 'for package in repomix create-next-app nodemon concurrently @types/node typescript eslint prettier; do\n'
printf ' npm install -g "$package" 2>/dev/null || echo "⚠️ Failed to install $package"\n'
printf 'done\n'
} > "$TEMP_DIR/node_setup.sh"
chmod +x "$TEMP_DIR/node_setup.sh"
if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1; then
su - "$USER_NAME" -c "$TEMP_DIR/node_setup.sh" 2>/dev/null || bash "$TEMP_DIR/node_setup.sh"
else
bash "$TEMP_DIR/node_setup.sh"
fi
export NVM_DIR="/home/coder/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
nvm install ${NODE_VERSION}
nvm use ${NODE_VERSION}
nvm alias default ${NODE_VERSION}
echo "📦 Installing npm packages..."
npm install -g repomix create-next-app nodemon concurrently @types/node typescript eslint prettier
npm install -g create-next-app@latest
NODE_SCRIPT_END
chmod +x /tmp/node_setup.sh
su - coder -c "/tmp/node_setup.sh"
rm /tmp/node_setup.sh
rm -f "$TEMP_DIR/node_setup.sh"
# =============================================================================
# Python Setup with uv (as coder user)
# Python Setup with uv
# =============================================================================
echo "🐍 Setting up Python and uv..."
# Install Python version
apt-get install -y python${PYTHON_VERSION} python${PYTHON_VERSION}-dev python${PYTHON_VERSION}-venv
# Install Python version (Linux only)
if [[ "$SYSTEM" == "debian" ]] && [[ "$EUID" -eq 0 ]]; then
apt-get install -y "python$PYTHON_VERSION" "python$PYTHON_VERSION-dev" "python$PYTHON_VERSION-venv" 2>/dev/null || {
echo "⚠️ Failed to install Python $PYTHON_VERSION"
}
fi
# Create Python setup script
cat > /tmp/python_setup.sh << 'PYTHON_SCRIPT_END'
#!/bin/bash
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="/home/coder/.cargo/bin:$PATH"
{
printf '#!/bin/bash\n'
printf '# Python setup script\n'
printf 'curl -LsSf https://astral.sh/uv/install.sh | sh 2>/dev/null || {\n'
printf ' echo "⚠️ Failed to install uv"\n'
printf ' exit 1\n'
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 '\n'
printf 'uv venv "%s/.venv" --python=%s 2>/dev/null || echo "⚠️ Failed to create venv"\n' "$HOME_DIR" "$PYTHON_VERSION"
printf '\n'
printf '# Add venv activation to shell config\n'
printf 'SHELL_RC="%s/.bashrc"\n' "$HOME_DIR"
printf 'if [[ "$SHELL" == *"zsh"* && -f "%s/.zshrc" ]]; then\n' "$HOME_DIR"
printf ' SHELL_RC="%s/.zshrc"\n' "$HOME_DIR"
printf 'fi\n'
printf 'if ! grep -q "source %s/.venv/bin/activate" "$SHELL_RC" 2>/dev/null; then\n' "$HOME_DIR"
printf ' printf "source %s/.venv/bin/activate\\n" >> "$SHELL_RC"\n' "$HOME_DIR"
printf 'fi\n'
} > "$TEMP_DIR/python_setup.sh"
echo "📦 Installing Python packages with uv..."
for package in fastapi uvicorn requests pandas numpy psycopg2-binary redis qdrant-client python-dotenv; do
uv tool install $package || echo "Failed to install $package"
done
uv venv /home/coder/.venv --python=${PYTHON_VERSION}
echo 'source /home/coder/.venv/bin/activate' >> /home/coder/.bashrc
PYTHON_SCRIPT_END
chmod +x /tmp/python_setup.sh
su - coder -c "/tmp/python_setup.sh"
rm /tmp/python_setup.sh
chmod +x "$TEMP_DIR/python_setup.sh"
if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1; then
su - "$USER_NAME" -c "$TEMP_DIR/python_setup.sh" 2>/dev/null || bash "$TEMP_DIR/python_setup.sh"
else
bash "$TEMP_DIR/python_setup.sh"
fi
rm -f "$TEMP_DIR/python_setup.sh"
# =============================================================================
# Rust and Cargo Setup (as coder user)
# Rust and Cargo Setup
# =============================================================================
echo "🦀 Installing Rust and Cargo..."
# Create Rust setup script
cat > /tmp/rust_setup.sh << 'RUST_SCRIPT_END'
#!/bin/bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
source "/home/coder/.cargo/env"
echo 'export PATH="/home/coder/.cargo/bin:$PATH"' >> /home/coder/.bashrc
cargo install cargo-watch cargo-edit cargo-audit
RUST_SCRIPT_END
{
printf '#!/bin/bash\n'
printf '# Rust setup script\n'
printf 'curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable 2>/dev/null || {\n'
printf ' echo "⚠️ Failed to install Rust"\n'
printf ' exit 1\n'
printf '}\n'
printf 'source "%s/.cargo/env"\n' "$HOME_DIR"
printf '\n'
printf '# Add cargo to shell config\n'
printf 'SHELL_RC="%s/.bashrc"\n' "$HOME_DIR"
printf 'if [[ "$SHELL" == *"zsh"* && -f "%s/.zshrc" ]]; then\n' "$HOME_DIR"
printf ' SHELL_RC="%s/.zshrc"\n' "$HOME_DIR"
printf 'fi\n'
printf 'if ! grep -q "export PATH=.*cargo.*bin" "$SHELL_RC" 2>/dev/null; then\n'
printf ' printf "export PATH=\\"%s/.cargo/bin:$PATH\\"\\n" >> "$SHELL_RC"\n' "$HOME_DIR"
printf 'fi\n'
printf '\n'
printf 'cargo install cargo-watch cargo-edit cargo-audit 2>/dev/null || echo "⚠️ Failed to install some cargo tools"\n'
} > "$TEMP_DIR/rust_setup.sh"
chmod +x /tmp/rust_setup.sh
su - coder -c "/tmp/rust_setup.sh"
rm /tmp/rust_setup.sh
chmod +x "$TEMP_DIR/rust_setup.sh"
if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1; then
su - "$USER_NAME" -c "$TEMP_DIR/rust_setup.sh" 2>/dev/null || bash "$TEMP_DIR/rust_setup.sh"
else
bash "$TEMP_DIR/rust_setup.sh"
fi
rm -f "$TEMP_DIR/rust_setup.sh"
# =============================================================================
# repomix Installation (as coder user)
# repomix Installation
# =============================================================================
echo "📁 Installing repomix..."
su - coder -c "npm install -g repomix"
if command -v npm >/dev/null 2>&1; then
if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/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"
fi
else
echo "⚠️ npm not available, skipping repomix installation"
fi
# =============================================================================
# Shell Configuration (as coder user)
# Shell Configuration
# =============================================================================
echo "🐚 Setting up shell environment..."
# Create devinfo script
cat > /tmp/devinfo_script.sh << 'DEVINFO_SCRIPT_END'
#!/bin/bash
mkdir -p /home/coder/bin
cat > /home/coder/bin/devinfo << 'DEVINFO_END'
#!/bin/bash
echo '🚀 Development Environment Info'
echo '==============================='
echo ''
echo '🔧 Installed Tools:'
echo ' Node.js: '$(node --version 2>/dev/null || echo 'Not found')
echo ' npm: '$(npm --version 2>/dev/null || echo 'Not found')
echo ' Python: '$(python${PYTHON_VERSION} --version 2>/dev/null || echo 'Not found')
echo ' uv: '$(uv --version 2>/dev/null || echo 'Not found')
echo ' Rust: '$(rustc --version 2>/dev/null || echo 'Not found')
echo ' Cargo: '$(cargo --version 2>/dev/null || echo 'Not found')
echo ' repomix: '$(repomix --version 2>/dev/null || echo 'Not found')
echo ''
echo '🗄️ Database Services:'
if [ "${ENABLE_SERVICES}" = "true" ]; then
echo ' PostgreSQL: '${POSTGRES_URL}
echo ' Redis: '${REDIS_URL}
echo ' Qdrant: '${QDRANT_URL}
{
printf '#!/bin/bash\n'
printf '# Development environment info script\n'
printf 'mkdir -p "%s"\n' "$BIN_DIR"
printf '{\n'
printf ' printf "#!/bin/bash\\n"\n'
printf ' printf "echo '\''🚀 Development Environment Info'\''\\n"\n'
printf ' printf "echo '\''==============================='\''\\n"\n'
printf ' printf "echo \\'\\'\\n"\n'
printf ' printf "echo '\''🔧 Installed Tools:'\''\\n"\n'
printf ' printf "echo '\'' Node.js: '\''\\$(node --version 2>/dev/null || echo '\''Not found'\'')\\n"\n'
printf ' printf "echo '\'' npm: '\''\\$(npm --version 2>/dev/null || echo '\''Not found'\'')\\n"\n'
printf ' printf "echo '\'' Python: '\''\\$(python%s --version 2>/dev/null || echo '\''Not found'\'')\\n"\n' "$PYTHON_VERSION"
printf ' printf "echo '\'' uv: '\''\\$(uv --version 2>/dev/null || echo '\''Not found'\'')\\n"\n'
printf ' printf "echo '\'' Rust: '\''\\$(rustc --version 2>/dev/null || echo '\''Not found'\'')\\n"\n'
printf ' printf "echo '\'' Cargo: '\''\\$(cargo --version 2>/dev/null || echo '\''Not found'\'')\\n"\n'
printf ' printf "echo '\'' repomix: '\''\\$(repomix --version 2>/dev/null || echo '\''Not found'\'')\\n"\n'
printf ' printf "echo \\'\\'\\n"\n'
printf ' printf "echo '\''🗄️ Database Services:'\''\\n"\n'
printf ' printf "if [ \\"%s\\" = \\"true\\" ]; then\\n" "%s"\n' "$ENABLE_SERVICES" "$ENABLE_SERVICES"
printf ' printf " echo '\'' PostgreSQL: '\''\\${POSTGRES_URL:-Not configured}\\n"\n'
printf ' printf " echo '\'' Redis: '\''\\${REDIS_URL:-Not configured}\\n"\n'
printf ' printf " echo '\'' Qdrant: '\''\\${QDRANT_URL:-Not configured}\\n"\n'
printf ' printf "else\\n"\n'
printf ' printf " echo '\'' Services disabled'\''\\n"\n'
printf ' printf "fi\\n"\n'
printf ' printf "echo \\'\\'\\n"\n'
printf ' printf "echo '\''📝 Git Metadata:'\''\\n"\n'
printf ' printf "if [ -f %s/git-metadata/current-branch ]; then\\n" "%s"\n' "$TEMP_DIR" "$TEMP_DIR"
printf ' printf " echo '\'' Branch: '\''\\$(cat %s/git-metadata/current-branch)\\n" "%s"\n' "$TEMP_DIR" "$TEMP_DIR"
printf ' printf " echo '\'' Commit: '\''\\$(cat %s/git-metadata/commit-hash)\\n" "%s"\n' "$TEMP_DIR" "$TEMP_DIR"
printf ' printf " echo '\'' Remote: '\''\\$(cat %s/git-metadata/remote-url)\\n" "%s"\n' "$TEMP_DIR" "$TEMP_DIR"
printf ' printf "fi\\n"\n'
printf '} > "%s/devinfo"\n' "$BIN_DIR"
printf 'chmod +x "%s/devinfo"\n' "$BIN_DIR"
} > "$TEMP_DIR/devinfo_script.sh"
chmod +x "$TEMP_DIR/devinfo_script.sh"
if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1; then
su - "$USER_NAME" -c "$TEMP_DIR/devinfo_script.sh" 2>/dev/null || bash "$TEMP_DIR/devinfo_script.sh"
else
echo ' Services disabled'
bash "$TEMP_DIR/devinfo_script.sh"
fi
echo ''
echo '📝 Git Metadata:'
if [ -f /tmp/git-metadata/current-branch ]; then
echo ' Branch: '$(cat /tmp/git-metadata/current-branch)
echo ' Commit: '$(cat /tmp/git-metadata/commit-hash)
echo ' Remote: '$(cat /tmp/git-metadata/remote-url)
rm -f "$TEMP_DIR/devinfo_script.sh"
# Create shell aliases
{
printf '#!/bin/bash\n'
printf '# Shell configuration script\n'
printf 'SHELL_RC="%s/.bashrc"\n' "$HOME_DIR"
printf 'if [[ "$SHELL" == *"zsh"* && -f "%s/.zshrc" ]]; then\n' "$HOME_DIR"
printf ' SHELL_RC="%s/.zshrc"\n' "$HOME_DIR"
printf 'fi\n'
printf '\n'
printf 'if ! grep -q "# Development Environment Aliases" "$SHELL_RC" 2>/dev/null; then\n'
printf ' printf "\\n# Development Environment Aliases\\n" >> "$SHELL_RC"\n'
printf ' printf "alias ll='\''ls -alF'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias la='\''ls -A'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias l='\''ls -CF'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias gs='\''git status'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias gp='\''git push'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias gc='\''git commit'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias gco='\''git checkout'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias gb='\''git branch'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias devinfo='\''%s/devinfo'\''\\n" >> "$SHELL_RC"\n' "$BIN_DIR"
printf ' printf "alias pip='\''uv pip'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias python='\''python%s'\''\\n" >> "$SHELL_RC"\n' "$PYTHON_VERSION"
printf ' if command -v docker >/dev/null 2>&1; then\n'
printf ' printf "alias dps='\''docker ps'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias dimg='\''docker images'\''\\n" >> "$SHELL_RC"\n'
printf ' printf "alias dlog='\''docker logs'\''\\n" >> "$SHELL_RC"\n'
printf ' fi\n'
printf ' printf "\\n" >> "$SHELL_RC"\n'
printf 'fi\n'
} > "$TEMP_DIR/bashrc_setup.sh"
chmod +x "$TEMP_DIR/bashrc_setup.sh"
if [[ "$USER_NAME" != "$(whoami)" ]] && command -v su >/dev/null 2>&1; then
su - "$USER_NAME" -c "$TEMP_DIR/bashrc_setup.sh" 2>/dev/null || bash "$TEMP_DIR/bashrc_setup.sh"
else
bash "$TEMP_DIR/bashrc_setup.sh"
fi
DEVINFO_END
chmod +x /home/coder/bin/devinfo
DEVINFO_SCRIPT_END
chmod +x /tmp/devinfo_script.sh
su - coder -c "/tmp/devinfo_script.sh"
rm /tmp/devinfo_script.sh
# Create bashrc aliases script
cat > /tmp/bashrc_setup.sh << 'BASHRC_SCRIPT_END'
#!/bin/bash
cat >> /home/coder/.bashrc << 'BASHRC_END'
# Development Environment Aliases
alias ll='ls -alF'
alias la='ls -A'
alias l='ls -CF'
alias gs='git status'
alias gp='git push'
alias gc='git commit'
alias gco='git checkout'
alias gb='git branch'
# Development workflow shortcuts
alias devinfo='/home/coder/bin/devinfo'
# Package managers
alias pip='uv pip'
alias python='python${PYTHON_VERSION}'
# Docker shortcuts
alias dps='docker ps'
alias dimg='docker images'
alias dlog='docker logs'
BASHRC_END
BASHRC_SCRIPT_END
chmod +x /tmp/bashrc_setup.sh
su - coder -c "/tmp/bashrc_setup.sh"
rm /tmp/bashrc_setup.sh
rm -f "$TEMP_DIR/bashrc_setup.sh"
# =============================================================================
# Final Environment Setup
@@ -230,14 +409,15 @@ rm /tmp/bashrc_setup.sh
echo "✅ Development environment initialization complete!"
echo ""
echo "🎉 Available tools:"
echo " - Node.js ${NODE_VERSION} with npm packages"
echo " - Python ${PYTHON_VERSION} with uv package manager"
printf " - Node.js %s with npm packages\n" "$NODE_VERSION"
printf " - Python %s with uv package manager\n" "$PYTHON_VERSION"
echo " - Rust with Cargo"
echo " - repomix for repository packaging"
echo " - make, tree, and other build tools"
if [ "${ENABLE_SERVICES}" = "true" ]; then
echo " - PostgreSQL, Redis, Qdrant databases"
if [ "$ENABLE_SERVICES" = "true" ]; then
echo " - PostgreSQL, Redis, Qdrant databases"
fi
echo ""
echo "🔧 Run 'devinfo' for detailed environment information"
echo "🚀 Ready for development!"
echo "🚀 Ready for development!"
echo "💡 Restart your shell or source your shell config to use new aliases"

View File

@@ -12,8 +12,10 @@ environment = "dev"
# =============================================================================
# Docker Configuration
# =============================================================================
docker_socket = ""
devcontainer_image = "mcr.microsoft.com/devcontainers/universal:2-linux"
docker_socket = ""
# devcontainer_image is deprecated, using devcontainer_repo_url instead
devcontainer_repo_url = "http://git.lab/vasceannie/code-tools.git"
envbuilder_cache_repo = "local" # Set to your registry URL for faster builds, e.g. "ghcr.io/username/cache"
# =============================================================================
# Development Tool Versions

View File

@@ -40,11 +40,23 @@ variable "docker_socket" {
}
variable "devcontainer_image" {
description = "Development container image with all required tools pre-installed"
description = "Development container image with all required tools pre-installed (deprecated - use devcontainer_repo_url)"
type = string
default = "mcr.microsoft.com/devcontainers/universal:2-linux"
}
variable "devcontainer_repo_url" {
description = "Git repository URL containing the devcontainer configuration"
type = string
default = "http://git.lab/vasceannie/code-tools.git"
}
variable "envbuilder_cache_repo" {
description = "Docker registry to use for caching envbuilder layers (e.g., 'ghcr.io/username/cache')"
type = string
default = "local"
}
# =============================================================================
# Development Tool Versions
# =============================================================================

View File

@@ -31,17 +31,12 @@ resource "coder_agent" "main" {
"ENABLE_SERVICES" = tostring(data.coder_parameter.enable_services.value)
# Security: Block file transfer commands to prevent data exfiltration
"CODER_AGENT_BLOCK_FILE_TRANSFER" = var.block_file_transfer ? "1" : ""
# Repository to clone on startup
"CODER_WORKSPACE_REPO" = local.repo_url != "custom" ? local.repo_url : ""
}
# Reference bind-mounted startup script plus service port forwarding
startup_script = <<-EOT
bash /home/coder/resources/tf/scripts/workspace-setup.sh
# Register JetBrains Gateway backend location if enabled
if [ "${data.coder_parameter.enable_jetbrains.value}" = "true" ] && [ -d ~/JetBrains ]; then
~/JetBrains/*/bin/remote-dev-server.sh registerBackendLocationForGateway 2>/dev/null || echo "JetBrains Gateway registration skipped"
fi
EOT
startup_script = "#!/bin/sh\necho 'Starting workspace...'"
# Performance and resource monitoring
metadata {