Commit 418e37887be1

Vincent Demeester <vincent@sbr.pm>
2025-12-19 23:10:44
feat(claude): add runtime selection to Docker skill
- Enable explicit Docker or Podman choice via natural language prompts - Support forced runtime detection when both engines are installed - Allow users to leverage runtime-specific features on demand Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 5f19f21
Changed files (3)
dots
.config
claude
dots/.config/claude/skills/Docker/tools/DetectRuntime.sh
@@ -3,61 +3,140 @@
 # shellcheck shell=bash
 
 # DetectRuntime.sh - Detect Docker or Podman runtime and return information
-# Usage: DetectRuntime.sh [--json]
+# Usage: DetectRuntime.sh [--runtime docker|podman] [--json]
 
 set -euo pipefail
 
+show_usage() {
+    cat <<EOF
+Usage: DetectRuntime.sh [OPTIONS]
+
+Detect container runtime (Docker or Podman) and return information.
+
+OPTIONS:
+    --runtime RUNTIME    Force specific runtime (docker|podman)
+    --json              Output in JSON format
+    -h, --help          Show this help message
+
+EXAMPLES:
+    DetectRuntime.sh                    # Auto-detect available runtime
+    DetectRuntime.sh --runtime docker   # Force Docker runtime
+    DetectRuntime.sh --json             # Output as JSON
+EOF
+}
+
 detect_runtime() {
+    local force_runtime=""
+    local output_json=false
+
+    # Parse arguments
+    while [[ $# -gt 0 ]]; do
+        case $1 in
+            --runtime)
+                force_runtime="$2"
+                shift 2
+                ;;
+            --json)
+                output_json=true
+                shift
+                ;;
+            -h|--help)
+                show_usage
+                exit 0
+                ;;
+            *)
+                echo "ERROR: Unknown option: $1" >&2
+                show_usage >&2
+                exit 1
+                ;;
+        esac
+    done
+
     local runtime=""
     local compose=""
     local version=""
     local rootless="false"
     local socket=""
 
-    # Check for Podman first (preference for rootless)
-    if command -v podman &>/dev/null; then
-        runtime="podman"
-        version=$(podman --version | awk '{print $3}')
-
-        # Check if running rootless
-        if podman info --format '{{.Host.Security.Rootless}}' 2>/dev/null | grep -q "true"; then
-            rootless="true"
-            socket="unix://$XDG_RUNTIME_DIR/podman/podman.sock"
-        else
-            rootless="false"
-            socket="unix:///run/podman/podman.sock"
-        fi
-
-        # Check for podman-compose
-        if command -v podman-compose &>/dev/null; then
-            compose="podman-compose"
-        elif command -v docker-compose &>/dev/null; then
-            compose="docker-compose"
-        else
-            compose="none"
-        fi
-    # Fall back to Docker
-    elif command -v docker &>/dev/null; then
-        runtime="docker"
-        version=$(docker --version | awk '{print $3}' | tr -d ',')
-        rootless="false"
-        socket="unix:///var/run/docker.sock"
-
-        # Check for docker-compose
-        if docker compose version &>/dev/null; then
-            compose="docker-compose-plugin"
-        elif command -v docker-compose &>/dev/null; then
-            compose="docker-compose"
-        else
-            compose="none"
-        fi
-    else
-        echo "ERROR: Neither Docker nor Podman found in PATH" >&2
-        exit 1
+    # If specific runtime requested, check only that one
+    if [[ -n "$force_runtime" ]]; then
+        case "$force_runtime" in
+            podman)
+                if ! command -v podman &>/dev/null; then
+                    echo "ERROR: Podman requested but not found in PATH" >&2
+                    exit 1
+                fi
+                runtime="podman"
+                ;;
+            docker)
+                if ! command -v docker &>/dev/null; then
+                    echo "ERROR: Docker requested but not found in PATH" >&2
+                    exit 1
+                fi
+                runtime="docker"
+                ;;
+            *)
+                echo "ERROR: Invalid runtime '$force_runtime'. Must be 'docker' or 'podman'" >&2
+                exit 1
+                ;;
+        esac
     fi
 
+    # Auto-detect if no runtime forced
+    if [[ -z "$runtime" ]]; then
+        # Check for Podman first (preference for rootless)
+        if command -v podman &>/dev/null; then
+            runtime="podman"
+        # Fall back to Docker
+        elif command -v docker &>/dev/null; then
+            runtime="docker"
+        else
+            echo "ERROR: Neither Docker nor Podman found in PATH" >&2
+            exit 1
+        fi
+    fi
+
+    # Get runtime-specific information
+    case "$runtime" in
+        podman)
+            version=$(podman --version | awk '{print $3}')
+
+            # Check if running rootless
+            if podman info --format '{{.Host.Security.Rootless}}' 2>/dev/null | grep -q "true"; then
+                rootless="true"
+                socket="unix://$XDG_RUNTIME_DIR/podman/podman.sock"
+            else
+                rootless="false"
+                socket="unix:///run/podman/podman.sock"
+            fi
+
+            # Check for podman-compose
+            if command -v podman-compose &>/dev/null; then
+                compose="podman-compose"
+            elif command -v docker-compose &>/dev/null; then
+                compose="docker-compose"
+            else
+                compose="none"
+            fi
+            ;;
+        docker)
+            version=$(docker --version | awk '{print $3}' | tr -d ',')
+            rootless="false"
+            socket="unix:///var/run/docker.sock"
+
+            # Check for docker-compose
+            if docker compose version &>/dev/null 2>&1; then
+                compose="docker-compose-plugin"
+            elif command -v docker-compose &>/dev/null; then
+                compose="docker-compose"
+            else
+                compose="none"
+            fi
+            ;;
+    esac
+
     # Output format
-    if [[ "${1:-}" == "--json" ]]; then
+    if [[ "$output_json" == true ]]; then
         jq -n \
             --arg runtime "$runtime" \
             --arg version "$version" \
dots/.config/claude/skills/Docker/workflows/BuildImage.md
@@ -5,7 +5,9 @@ Build container images with best practices for Docker or Podman.
 ## Process
 
 1. **Detect Runtime**
-   - Run `DetectRuntime.sh` to determine Docker/Podman
+   - Parse user prompt for explicit runtime mention ("use Docker", "with Podman")
+   - If explicit runtime mentioned: Use `DetectRuntime.sh --runtime <docker|podman>`
+   - If auto-detect: Run `DetectRuntime.sh` (prefers Podman, falls back to Docker)
    - Identify available build tools (buildx, buildah)
 
 2. **Analyze Context**
dots/.config/claude/skills/Docker/SKILL.md
@@ -51,6 +51,27 @@ This skill works seamlessly with both runtimes:
 
 The skill automatically detects which runtime is available and adjusts commands accordingly.
 
+## Runtime Selection
+
+By default, the skill auto-detects available runtimes (prefers Podman for rootless). You can explicitly choose a runtime in your prompts:
+
+**Explicit mentions** (natural language):
+- "**Use Docker to** build this image"
+- "Build this image **with Podman**"
+- "**Using Docker**, start the container"
+- "**With Podman**, create a volume"
+
+When you mention a specific runtime, the skill will use that runtime exclusively for the operation.
+
+**Auto-detection behavior**:
+1. Checks for Podman first (rootless preference)
+2. Falls back to Docker if Podman not found
+3. Errors if neither is available
+
+**When both are installed**: You have both runtimes available, so you can choose based on your needs:
+- **Podman**: Rootless, daemonless, pods support
+- **Docker**: Mature ecosystem, wider adoption, better tooling
+
 ## Examples
 
 **Example 1: Build a container image**
@@ -87,6 +108,19 @@ User: "Build this image for both AMD64 and ARM64"
 → Pushes to registry with manifest
 ```
 
+**Example 5: Explicit runtime selection**
+```
+User: "Use Docker to build this image"
+→ Invokes BuildImage workflow
+→ Forces Docker runtime (--runtime docker)
+→ Uses Docker-specific features if needed
+
+User: "With Podman, start a rootless container"
+→ Invokes ManageContainers workflow
+→ Forces Podman runtime (--runtime podman)
+→ Uses Podman rootless mode
+```
+
 ## Best Practices
 
 ### Dockerfile