# Runtime abstraction module for Docker, Podman, OrbStack, Colima, nerdctl # # Provides unified interface for container runtime operations across platforms. # Follows NUSHELL_GUIDELINES.md: single purpose, explicit types, early return, atomic operations # Detect available container runtime on the system # # Returns: record with runtime info # Errors: propagates if no runtime found export def runtime-detect [] { let runtimes = [ { name: "docker", command: "docker", priority: 1 } { name: "podman", command: "podman", priority: 2 } { name: "orbstack", command: "orbctl", priority: 3 } { name: "colima", command: "colima", priority: 4 } { name: "nerdctl", command: "nerdctl", priority: 5 } ] let available = ( $runtimes | each {|rt| let exists = (^which $rt.command 2>/dev/null | length) > 0 { name: $rt.name command: $rt.command priority: $rt.priority available: $exists } } | where available | sort-by priority ) if ($available | length) == 0 { error "No container runtime detected. Install Docker, Podman, or another supported runtime." } $available | first } # Execute command in detected runtime # # Parameters: # command: string - Command to execute # --check: bool - Dry-run mode (no execution) # # Returns: string - Command output # Errors: propagates from command execution export def runtime-exec [command: string, --check = false] { # Validate inputs early if ($command | str trim) == "" { error "Command cannot be empty" } let runtime = (runtime-detect) let full_command = ($"($runtime.command) ($command)") if $check { return $"Would execute: [$full_command]" } # Execute atomically - succeed or fail completely let result = ( do { ^sh -c $full_command } | complete ) if $result.exit_code == 0 { $result.stdout } else { error $"Runtime execution failed: [$runtime.name]\nCommand: [$command]\nError: [$result.stderr]" } } # Adapt docker-compose file for detected runtime # # Parameters: # file_path: string - Path to compose file # # Returns: string - Compose command for this runtime # Errors: propagates if file not found or runtime not available export def runtime-compose [file_path: string] { # Validate input early if (not ($file_path | path exists)) { error $"Compose file not found: [$file_path]" } let runtime = (runtime-detect) # Generate appropriate compose command based on runtime match $runtime.name { "docker" => { $"docker compose -f [$file_path]" } "podman" => { $"podman compose -f [$file_path]" } "orbstack" | "colima" => { $"docker compose -f [$file_path]" } "nerdctl" => { $"nerdctl compose -f [$file_path]" } _ => { error $"Unknown runtime: [$runtime.name]" } } } # Get runtime information # # Returns: record - Runtime details # Errors: propagates if no runtime available export def runtime-info [] { let rt = (runtime-detect) { name: $rt.name command: $rt.command available: true version: ( let result = (do { let ver_output = (^sh -c $"($rt.command) --version" 2>&1) $ver_output | str trim | str substring [0..<40] } | complete) if $result.exit_code != 0 { "unknown" } else { $result.stdout } ) } } # List all available runtimes on the system # # Returns: table - All available runtimes # Errors: none (returns empty if none available) export def runtime-list [] { let runtimes = [ { name: "docker", command: "docker" } { name: "podman", command: "podman" } { name: "orbstack", command: "orbctl" } { name: "colima", command: "colima" } { name: "nerdctl", command: "nerdctl" } ] $runtimes | each {|rt| let exists = (^which $rt.command 2>/dev/null | length) > 0 { name: $rt.name command: $rt.command available: $exists } } | where available } #[cfg(test)] # Tests for runtime module def test-runtime-detect [] { # Note: Tests require runtime to be installed let result = (do { runtime-detect } | complete) let rt = if $result.exit_code != 0 { null } else { $result.stdout } if ($rt != null) { assert ($rt.name != "") } } def test-runtime-info [] { let result = (do { runtime-info } | complete) let info = if $result.exit_code != 0 { null } else { $result.stdout } if ($info != null) { assert ($info.name != "") } } def test-runtime-list [] { let list = (runtime-list) # Should return list of available runtimes (0 or more) assert (($list | length) >= 0) }