diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 753c996..737d35b 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,12 +1,20 @@ -// For format details, see https://aka.ms/devcontainer.json. For config options, see the -// README at: https://github.com/devcontainers/templates/tree/main/src/rust { - "name": "Rust", - // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/rust:2-1-trixie", + "name": "Development environments on your infrastructure", + "image": "codercom/oss-dogfood:latest", "features": { - "ghcr.io/devcontainers/features/rust:1": {} + "ghcr.io/devcontainers/features/docker-in-docker:2": { + "moby": "false" + }, + "ghcr.io/coder/devcontainer-features/code-server:1": { + "auth": "none", + "port": 13337 + }, + "./filebrowser": { + "folder": "${containerWorkspaceFolder}" + } }, + // SYS_PTRACE to enable go debugging + "runArgs": ["--cap-add=SYS_PTRACE"], "customizations": { "vscode": { "extensions": [ @@ -15,30 +23,65 @@ "tamasfe.even-better-toml", "fill-labs.dependi" ] + }, + "coder": { + "apps": [ + { + "slug": "cursor", + "displayName": "Cursor Desktop", + "url": "cursor://coder.coder-remote/openDevContainer?owner=${localEnv:CODER_WORKSPACE_OWNER_NAME}&workspace=${localEnv:CODER_WORKSPACE_NAME}&agent=${localEnv:CODER_WORKSPACE_PARENT_AGENT_NAME}&url=${localEnv:CODER_URL}&token=$SESSION_TOKEN&devContainerName=${localEnv:CONTAINER_ID}&devContainerFolder=${containerWorkspaceFolder}&localWorkspaceFolder=${localWorkspaceFolder}", + "external": true, + "icon": "/icon/cursor.svg", + "order": 1 + }, + { + "slug": "windsurf", + "displayName": "Windsurf Editor", + "url": "windsurf://coder.coder-remote/openDevContainer?owner=${localEnv:CODER_WORKSPACE_OWNER_NAME}&workspace=${localEnv:CODER_WORKSPACE_NAME}&agent=${localEnv:CODER_WORKSPACE_PARENT_AGENT_NAME}&url=${localEnv:CODER_URL}&token=$SESSION_TOKEN&devContainerName=${localEnv:CONTAINER_ID}&devContainerFolder=${containerWorkspaceFolder}&localWorkspaceFolder=${localWorkspaceFolder}", + "external": true, + "icon": "/icon/windsurf.svg", + "order": 4 + }, + { + "slug": "zed", + "displayName": "Zed Editor", + "url": "zed://ssh/${localEnv:CODER_WORKSPACE_AGENT_NAME}.${localEnv:CODER_WORKSPACE_NAME}.${localEnv:CODER_WORKSPACE_OWNER_NAME}.coder${containerWorkspaceFolder}", + "external": true, + "icon": "/icon/zed.svg", + "order": 5 + }, + // Reproduce `code-server` app here from the code-server + // feature so that we can set the correct folder and order. + // Currently, the order cannot be specified via option because + // we parse it as a number whereas variable interpolation + // results in a string. Additionally we set health check which + // is not yet set in the feature. + { + "slug": "code-server", + "displayName": "code-server", + "url": "http://${localEnv:FEATURE_CODE_SERVER_OPTION_HOST:127.0.0.1}:${localEnv:FEATURE_CODE_SERVER_OPTION_PORT:8080}/?folder=${containerWorkspaceFolder}", + "openIn": "${localEnv:FEATURE_CODE_SERVER_OPTION_APPOPENIN:slim-window}", + "share": "${localEnv:FEATURE_CODE_SERVER_OPTION_APPSHARE:owner}", + "icon": "/icon/code.svg", + "group": "${localEnv:FEATURE_CODE_SERVER_OPTION_APPGROUP:Web Editors}", + "order": 3, + "healthCheck": { + "url": "http://${localEnv:FEATURE_CODE_SERVER_OPTION_HOST:127.0.0.1}:${localEnv:FEATURE_CODE_SERVER_OPTION_PORT:8080}/healthz", + "interval": 5, + "threshold": 2 + } + } + ] } - } - - // Use 'mounts' to make the cargo cache persistent in a Docker Volume. - // "mounts": [ - // { - // "source": "devcontainer-cargo-cache-${devcontainerId}", - // "target": "/usr/local/cargo", - // "type": "volume" - // } - // ] - - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, - - // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [], - - // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "rustc --version", - - // Configure tool-specific properties. - // "customizations": {}, - - // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" + }, + "mounts": [ + // Add a volume for the Coder home directory to persist shell history, + // and speed up dotfiles init and/or personalization. + "source=coder-coder-devcontainer-home,target=/home/coder,type=volume", + // Mount the entire home because conditional mounts are not supported. + // See: https://github.com/devcontainers/spec/issues/132 + "source=${localEnv:HOME},target=/mnt/home/coder,type=bind,readonly" + ], + "postCreateCommand": ["./.devcontainer/scripts/post_create.sh"], + "postStartCommand": ["./.devcontainer/scripts/post_start.sh"] } diff --git a/.devcontainer/filebrowser/devcontainer-feature.json b/.devcontainer/filebrowser/devcontainer-feature.json new file mode 100644 index 0000000..c7a55a0 --- /dev/null +++ b/.devcontainer/filebrowser/devcontainer-feature.json @@ -0,0 +1,46 @@ +{ + "id": "filebrowser", + "version": "0.0.1", + "name": "File Browser", + "description": "A web-based file browser for your development container", + "options": { + "port": { + "type": "string", + "default": "13339", + "description": "The port to run filebrowser on" + }, + "folder": { + "type": "string", + "default": "", + "description": "The root directory for filebrowser to serve" + }, + "baseUrl": { + "type": "string", + "default": "", + "description": "The base URL for filebrowser (e.g., /filebrowser)" + } + }, + "entrypoint": "/usr/local/bin/filebrowser-entrypoint", + "dependsOn": { + "ghcr.io/devcontainers/features/common-utils:2": {} + }, + "customizations": { + "coder": { + "apps": [ + { + "slug": "filebrowser", + "displayName": "File Browser", + "url": "http://localhost:${localEnv:FEATURE_FILEBROWSER_OPTION_PORT:13339}", + "icon": "/icon/filebrowser.svg", + "order": 3, + "subdomain": true, + "healthcheck": { + "url": "http://localhost:${localEnv:FEATURE_FILEBROWSER_OPTION_PORT:13339}/health", + "interval": 5, + "threshold": 2 + } + } + ] + } + } +} diff --git a/.devcontainer/filebrowser/install.sh b/.devcontainer/filebrowser/install.sh new file mode 100755 index 0000000..6e8d58a --- /dev/null +++ b/.devcontainer/filebrowser/install.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash + +set -euo pipefail + +BOLD='\033[0;1m' + +printf "%sInstalling filebrowser\n\n" "${BOLD}" + +# Check if filebrowser is installed. +if ! command -v filebrowser &>/dev/null; then + VERSION="v2.42.1" + EXPECTED_HASH="7d83c0f077df10a8ec9bfd9bf6e745da5d172c3c768a322b0e50583a6bc1d3cc" + + curl -fsSL "https://github.com/filebrowser/filebrowser/releases/download/${VERSION}/linux-amd64-filebrowser.tar.gz" -o /tmp/filebrowser.tar.gz + echo "${EXPECTED_HASH} /tmp/filebrowser.tar.gz" | sha256sum -c + tar -xzf /tmp/filebrowser.tar.gz -C /tmp + sudo mv /tmp/filebrowser /usr/local/bin/ + sudo chmod +x /usr/local/bin/filebrowser + rm /tmp/filebrowser.tar.gz +fi + +# Create entrypoint. +cat >/usr/local/bin/filebrowser-entrypoint <>\${LOG_PATH} 2>&1 + filebrowser users add admin "" --perm.admin=true --viewMode=mosaic >>\${LOG_PATH} 2>&1 +fi + +filebrowser config set --baseurl=\${BASEURL} --port=\${PORT} --auth.method=noauth --root=\${FOLDER} >>\${LOG_PATH} 2>&1 + +printf "👷 Starting filebrowser...\n\n" + +printf "📂 Serving \${FOLDER} at http://localhost:\${PORT}\n\n" + +filebrowser >>\${LOG_PATH} 2>&1 & + +printf "📝 Logs at \${LOG_PATH}\n\n" +EOF + +chmod +x /usr/local/bin/filebrowser-entrypoint + +printf "🥳 Installation complete!\n\n" diff --git a/.devcontainer/scripts/post_create.sh b/.devcontainer/scripts/post_create.sh new file mode 100755 index 0000000..ab5be4b --- /dev/null +++ b/.devcontainer/scripts/post_create.sh @@ -0,0 +1,67 @@ +#!/bin/sh + +install_devcontainer_cli() { + set -e + echo "🔧 Installing DevContainer CLI..." + cd "$(dirname "$0")/../tools/devcontainer-cli" + npm ci --omit=dev + ln -sf "$(pwd)/node_modules/.bin/devcontainer" "$(npm config get prefix)/bin/devcontainer" +} + +install_ssh_config() { + echo "🔑 Installing SSH configuration..." + if [ -d /mnt/home/coder/.ssh ]; then + rsync -a /mnt/home/coder/.ssh/ ~/.ssh/ + chmod 0700 ~/.ssh + else + echo "⚠️ SSH directory not found." + fi +} + +install_git_config() { + echo "📂 Installing Git configuration..." + if [ -f /mnt/home/coder/git/config ]; then + rsync -a /mnt/home/coder/git/ ~/.config/git/ + elif [ -d /mnt/home/coder/.gitconfig ]; then + rsync -a /mnt/home/coder/.gitconfig ~/.gitconfig + else + echo "⚠️ Git configuration directory not found." + fi +} + +install_dotfiles() { + if [ ! -d /mnt/home/coder/.config/coderv2/dotfiles ]; then + echo "⚠️ Dotfiles directory not found." + return + fi + + cd /mnt/home/coder/.config/coderv2/dotfiles || return + for script in install.sh install bootstrap.sh bootstrap script/bootstrap setup.sh setup script/setup; do + if [ -x $script ]; then + echo "📦 Installing dotfiles..." + ./$script || { + echo "❌ Error running $script. Please check the script for issues." + return + } + echo "✅ Dotfiles installed successfully." + return + fi + done + echo "⚠️ No install script found in dotfiles directory." +} + +personalize() { + # Allow script to continue as Coder dogfood utilizes a hack to + # synchronize startup script execution. + touch /tmp/.coder-startup-script.done + + if [ -x /mnt/home/coder/personalize ]; then + echo "🎨 Personalizing environment..." + /mnt/home/coder/personalize + fi +} + +install_devcontainer_cli +install_ssh_config +install_dotfiles +personalize diff --git a/.devcontainer/scripts/post_start.sh b/.devcontainer/scripts/post_start.sh new file mode 100755 index 0000000..c986740 --- /dev/null +++ b/.devcontainer/scripts/post_start.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +# Start Docker service if not already running. +sudo service docker start diff --git a/.devcontainer/tools/devcontainer-cli/package-lock.json b/.devcontainer/tools/devcontainer-cli/package-lock.json new file mode 100644 index 0000000..2fee536 --- /dev/null +++ b/.devcontainer/tools/devcontainer-cli/package-lock.json @@ -0,0 +1,26 @@ +{ + "name": "devcontainer-cli", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "devcontainer-cli", + "version": "1.0.0", + "dependencies": { + "@devcontainers/cli": "^0.80.0" + } + }, + "node_modules/@devcontainers/cli": { + "version": "0.80.0", + "resolved": "https://registry.npmjs.org/@devcontainers/cli/-/cli-0.80.0.tgz", + "integrity": "sha512-w2EaxgjyeVGyzfA/KUEZBhyXqu/5PyWNXcnrXsZOBrt3aN2zyGiHrXoG54TF6K0b5DSCF01Rt5fnIyrCeFzFKw==", + "bin": { + "devcontainer": "devcontainer.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + } + } +} diff --git a/.devcontainer/tools/devcontainer-cli/package.json b/.devcontainer/tools/devcontainer-cli/package.json new file mode 100644 index 0000000..b474c86 --- /dev/null +++ b/.devcontainer/tools/devcontainer-cli/package.json @@ -0,0 +1,8 @@ +{ + "name": "devcontainer-cli", + "private": true, + "version": "1.0.0", + "dependencies": { + "@devcontainers/cli": "^0.80.0" + } +}