Functions/GenXdev.AI/EnsureDockerDesktop.ps1

################################################################################
<#
.SYNOPSIS
Ensures Docker Desktop is installed and available for containerization operations.
 
.DESCRIPTION
Verifies if Docker Desktop is installed and properly configured on the system.
If not found, installs Docker Desktop using WinGet and handles the complete
installation process automatically.
 
.EXAMPLE
EnsureDockerDesktop
Ensures Docker Desktop is installed and properly configured.
#>

function EnsureDockerDesktop {

    [CmdletBinding()]
    param()

    begin {

        ########################################################################
        <#
        .SYNOPSIS
        Checks if the WinGet PowerShell module is installed.
 
        .DESCRIPTION
        Attempts to import the Microsoft.WinGet.Client module and verifies its
        presence.
 
        .EXAMPLE
        IsWinGetInstalled
        #>

        function IsWinGetInstalled {

            # attempt to load the winget module silently
            Microsoft.PowerShell.Core\Import-Module "Microsoft.WinGet.Client" `
                -ErrorAction SilentlyContinue

            # verify if module was loaded successfully
            $module = Microsoft.PowerShell.Core\Get-Module "Microsoft.WinGet.Client" `
                -ErrorAction SilentlyContinue

            return $null -ne $module
        }

        ########################################################################
        <#
        .SYNOPSIS
        Installs the WinGet PowerShell module.
 
        .DESCRIPTION
        Installs and imports the Microsoft.WinGet.Client module for package
        management.
 
        .EXAMPLE
        InstallWinGet
        #>

        function InstallWinGet {

            # install and import winget module with force to ensure success
            Microsoft.PowerShell.Utility\Write-Verbose "Installing WinGet PowerShell client..."
            $null = PowerShellGet\Install-Module "Microsoft.WinGet.Client" -Force -AllowClobber

            # load the newly installed module
            Microsoft.PowerShell.Core\Import-Module "Microsoft.WinGet.Client"
        }
    }

    process {

        # verify if docker desktop is available in current session
        if (@(Microsoft.PowerShell.Core\Get-Command 'docker.exe' -ErrorAction SilentlyContinue).Length -eq 0) {

            # check if docker desktop is installed but not in PATH
            $dockerPaths = @(
                "${env:ProgramFiles}\Docker\Docker\resources\bin",
                "${env:LOCALAPPDATA}\Programs\Docker\Docker\resources\bin"
            )

            $dockerFound = $false
            foreach ($path in $dockerPaths) {
                if (Microsoft.PowerShell.Management\Test-Path (Microsoft.PowerShell.Management\Join-Path $path "docker.exe")) {
                    $currentPath = [Environment]::GetEnvironmentVariable('PATH', 'User')
                    if ($currentPath -notlike "*$path*") {
                        Microsoft.PowerShell.Utility\Write-Verbose "Adding Docker to system PATH..."
                        [Environment]::SetEnvironmentVariable(
                            'PATH',
                            "$currentPath;$path",
                            'User')
                        # update current session's path
                        $env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'User')
                    }
                    $dockerFound = $true
                    break
                }
            }

            # check if docker is now accessible
            if (@(Microsoft.PowerShell.Core\Get-Command 'docker.exe' -ErrorAction SilentlyContinue).Length -eq 0) {

                Microsoft.PowerShell.Utility\Write-Host "Docker Desktop not found. Installing Docker Desktop..."

                # ensure winget is available for installation
                if (-not (IsWinGetInstalled)) {
                    InstallWinGet
                }

                # install docker desktop using winget package manager
                $null = Microsoft.WinGet.Client\Install-WinGetPackage -Id 'Docker.DockerDesktop' -Force

                # re-check docker paths after installation
                foreach ($path in $dockerPaths) {
                    if (Microsoft.PowerShell.Management\Test-Path (Microsoft.PowerShell.Management\Join-Path $path "docker.exe")) {
                        $currentPath = [Environment]::GetEnvironmentVariable('PATH', 'User')
                        if ($currentPath -notlike "*$path*") {
                            Microsoft.PowerShell.Utility\Write-Verbose "Adding Docker to system PATH..."
                            [Environment]::SetEnvironmentVariable(
                                'PATH',
                                "$currentPath;$path",
                                'User')
                            # update current session's path
                            $env:PATH = [Environment]::GetEnvironmentVariable('PATH', 'User')
                        }
                        break
                    }
                }

                # verify successful installation
                if (-not (Microsoft.PowerShell.Core\Get-Command 'docker.exe' -ErrorAction SilentlyContinue)) {
                    throw "Docker Desktop installation failed."
                }
            }
        }

        # ensure docker desktop service is running
        $dockerDesktopProcess = Microsoft.PowerShell.Management\Get-Process "Docker Desktop" -ErrorAction SilentlyContinue
        if (-not $dockerDesktopProcess) {
            Microsoft.PowerShell.Utility\Write-Host "Starting Docker Desktop..."
            $dockerExePath = Microsoft.PowerShell.Core\Get-Command "Docker Desktop.exe" -ErrorAction SilentlyContinue
            if ($dockerExePath) {
                Microsoft.PowerShell.Management\Start-Process $dockerExePath.Source
                Microsoft.PowerShell.Utility\Start-Sleep 30
            }
            else {
                # try common installation paths
                $dockerDesktopPaths = @(
                    "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe",
                    "${env:LOCALAPPDATA}\Programs\Docker\Docker\Docker Desktop.exe"
                )
                foreach ($path in $dockerDesktopPaths) {
                    if (Microsoft.PowerShell.Management\Test-Path $path) {
                        Microsoft.PowerShell.Management\Start-Process $path
                        Microsoft.PowerShell.Utility\Start-Sleep 30
                        break
                    }
                }
            }
        }

        # wait for docker daemon to be ready
        Microsoft.PowerShell.Utility\Write-Verbose "Waiting for Docker daemon to be ready..."
        $timeout = 60
        $elapsed = 0
        do {
            Microsoft.PowerShell.Utility\Start-Sleep -Seconds 2
            $elapsed += 2
            $dockerInfo = docker info 2>$null
        } while (-not $dockerInfo -and $elapsed -lt $timeout)

        if ($elapsed -ge $timeout) {
            throw "Docker daemon failed to start within $timeout seconds."
        }

        Microsoft.PowerShell.Utility\Write-Verbose "✅ Docker Desktop is ready."
    }

    end {
    }
}
################################################################################