Functions/Docker-Operations.ps1

# Docker Operations Functions
# Docker-Build, Docker-Publish, Docker-StartIfNeeded

function Docker-Build {
    <#
    .SYNOPSIS
    Builds Docker images with specified configurations.
     
    .DESCRIPTION
    Builds Docker images using Dockerfile with various build options and configurations.
     
    .PARAMETER ProjectPath
    Path to the project directory containing Dockerfile.
     
    .PARAMETER Dockerfile
    Path to the Dockerfile (default: Dockerfile).
     
    .PARAMETER Tag
    Tag for the Docker image.
     
    .PARAMETER BuildArgs
    Build arguments to pass to Docker build.
     
    .PARAMETER Platform
    Target platform for the build.
     
    .PARAMETER NoCache
    Build without using cache.
     
    .PARAMETER Pull
    Always attempt to pull a newer version of the base image.
     
    .PARAMETER Verbose
    Enable verbose output.
     
    .EXAMPLE
    Docker-Build -ProjectPath "C:\MyProject" -Tag "myapp:latest"
     
    .EXAMPLE
    Docker-Build -ProjectPath "C:\MyProject" -Dockerfile "Dockerfile.prod" -Tag "myapp:1.0" -NoCache
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProjectPath,
        
        [string]$Dockerfile = "Dockerfile",
        
        [Parameter(Mandatory = $true)]
        [string]$Tag,
        
        [hashtable]$BuildArgs = @{},
        
        [string]$Platform,
        
        [switch]$NoCache,
        
        [switch]$Pull,
        
        [switch]$VerboseOutput
    )
    
    if (-not (Test-Path $ProjectPath)) {
        Write-Error "Project directory not found: $ProjectPath"
        return $false
    }
    
    $dockerfilePath = Join-Path $ProjectPath $Dockerfile
    if (-not (Test-Path $dockerfilePath)) {
        Write-Error "Dockerfile not found: $dockerfilePath"
        return $false
    }
    
    # Ensure Docker is running
    if (-not (Docker-StartIfNeeded)) {
        Write-Error "Docker is not available"
        return $false
    }
    
    $originalLocation = Get-Location
    
    try {
        Set-Location $ProjectPath
        
        Write-Host "Building Docker image..." -ForegroundColor Green
        Write-Host "Project: $ProjectPath" -ForegroundColor Cyan
        Write-Host "Dockerfile: $Dockerfile" -ForegroundColor Cyan
        Write-Host "Tag: $Tag" -ForegroundColor Cyan
        
        $buildArgs = @("build", "-f", $Dockerfile, "-t", $Tag)
        
        if ($Platform) {
            $buildArgs += @("--platform", $Platform)
            Write-Host "Platform: $Platform" -ForegroundColor Cyan
        }
        
        if ($NoCache) {
            $buildArgs += "--no-cache"
            Write-Host "No cache: true" -ForegroundColor Cyan
        }
        
        if ($Pull) {
            $buildArgs += "--pull"
            Write-Host "Pull: true" -ForegroundColor Cyan
        }
        
        if ($VerboseOutput) {
            $buildArgs += "--progress=plain"
        }
        
        # Add build arguments
        foreach ($key in $BuildArgs.Keys) {
            $buildArgs += @("--build-arg", "${key}=$($BuildArgs[$key])")
        }
        
        # Add context
        $buildArgs += "."
        
        $buildOutput = docker $buildArgs 2>&1 | Out-String
        $buildExitCode = $LASTEXITCODE
        
        if ($buildExitCode -eq 0) {
            Write-Host "Docker image built successfully: $Tag" -ForegroundColor Green
            return $true
        }
        else {
            Write-Host "Docker build failed with exit code: $buildExitCode" -ForegroundColor Red
            if ($buildOutput) {
                Write-Host "Build output:" -ForegroundColor Yellow
                Write-Host $buildOutput
            }
            return $false
        }
    }
    catch {
        Write-Error "Docker build failed: $_"
        return $false
    }
    finally {
        Set-Location $originalLocation
    }
}

function Docker-Publish {
    <#
    .SYNOPSIS
    Publishes Docker images to registries.
     
    .DESCRIPTION
    Publishes Docker images to Docker Hub, GitHub Container Registry, or other registries.
     
    .PARAMETER ImageName
    Name of the Docker image to publish.
     
    .PARAMETER Registry
    Target registry (dockerhub, ghcr, custom).
     
    .PARAMETER Username
    Username for the registry.
     
    .PARAMETER Password
    Password or token for the registry.
     
    .PARAMETER Tags
    Array of tags to publish.
     
    .PARAMETER Latest
    Also tag as latest.
     
    .PARAMETER Verbose
    Enable verbose output.
     
    .EXAMPLE
    Docker-Publish -ImageName "myapp" -Registry "dockerhub" -Username "myuser" -Tags @("1.0", "latest")
     
    .EXAMPLE
    Docker-Publish -ImageName "myapp" -Registry "ghcr" -Username "myuser" -Tags @("1.0") -Latest
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$ImageName,
        
        [ValidateSet("dockerhub", "ghcr", "custom")]
        [string]$Registry = "dockerhub",
        
        [string]$Username,
        
        [SecureString]$Password,
        
        [string[]]$Tags = @("latest"),
        
        [switch]$Latest,
        
        [switch]$VerboseOutput
    )
    
    # Ensure Docker is running
    if (-not (Docker-StartIfNeeded)) {
        Write-Error "Docker is not available"
        return $false
    }
    
    try {
        # Get username if not provided
        if (-not $Username) {
            $Username = Get-Username -Service "docker"
            if (-not $Username) {
                Write-Error "Could not determine Docker username"
                return $false
            }
        }
        
        # Determine registry URL
        $registryUrl = switch ($Registry) {
            "dockerhub" { "$Username/$ImageName" }
            "ghcr" { "ghcr.io/$Username/$ImageName" }
            "custom" { $ImageName }
        }
        
        Write-Host "Publishing Docker image..." -ForegroundColor Green
        Write-Host "Image: $ImageName" -ForegroundColor Cyan
        Write-Host "Registry: $Registry" -ForegroundColor Cyan
        Write-Host "Full name: $registryUrl" -ForegroundColor Cyan
        
        $success = $true
        
        # Tag and push each tag
        foreach ($tag in $Tags) {
            $fullTag = "${registryUrl}:$tag"
            
            Write-Host "Tagging image as $fullTag..." -ForegroundColor Yellow
            docker tag $ImageName $fullTag
            
            if ($LASTEXITCODE -eq 0) {
                Write-Host "Pushing $fullTag..." -ForegroundColor Yellow
                docker push $fullTag
                
                if ($LASTEXITCODE -eq 0) {
                    Write-Host "Successfully pushed $fullTag" -ForegroundColor Green
                }
                else {
                    Write-Host "Failed to push $fullTag" -ForegroundColor Red
                    $success = $false
                }
            }
            else {
                Write-Host "Failed to tag $fullTag" -ForegroundColor Red
                $success = $false
            }
        }
        
        # Tag as latest if requested
        if ($Latest -and $Tags -notcontains "latest") {
            $latestTag = "${registryUrl}:latest"
            
            Write-Host "Tagging image as $latestTag..." -ForegroundColor Yellow
            docker tag $ImageName $latestTag
            
            if ($LASTEXITCODE -eq 0) {
                Write-Host "Pushing $latestTag..." -ForegroundColor Yellow
                docker push $latestTag
                
                if ($LASTEXITCODE -eq 0) {
                    Write-Host "Successfully pushed $latestTag" -ForegroundColor Green
                }
                else {
                    Write-Host "Failed to push $latestTag" -ForegroundColor Red
                    $success = $false
                }
            }
            else {
                Write-Host "Failed to tag $latestTag" -ForegroundColor Red
                $success = $false
            }
        }
        
        if ($success) {
            Write-Host "Docker image published successfully" -ForegroundColor Green
            return $true
        }
        else {
            Write-Host "Some operations failed" -ForegroundColor Red
            return $false
        }
    }
    catch {
        Write-Error "Docker publish failed: $_"
        return $false
    }
}

function Docker-StartIfNeeded {
    <#
    .SYNOPSIS
    Ensures Docker is running and starts it if needed.
     
    .DESCRIPTION
    Checks if Docker daemon is running and attempts to start it if not available.
     
    .EXAMPLE
    Docker-StartIfNeeded
    #>

    [CmdletBinding()]
    param()
    
    Write-Host "Checking Docker status..." -ForegroundColor Yellow
    
    # Check if Docker daemon is accessible
    try {
        docker info --format "{{.ServerVersion}}" 2>$null | Out-Null
        if ($LASTEXITCODE -eq 0) {
            Write-Host "Docker daemon is running" -ForegroundColor Green
            return $true
        }
    }
    catch {
        Write-Host "Docker daemon not accessible" -ForegroundColor Yellow
    }
    
    Write-Host "Docker daemon not running. Checking Docker service..." -ForegroundColor Yellow
    
    # Check and start Docker Windows service if needed
    try {
        $dockerService = Get-Service -Name "com.docker.service" -ErrorAction SilentlyContinue
        if ($dockerService) {
            if ($dockerService.Status -ne "Running") {
                Write-Host "Starting Docker service (com.docker.service)..." -ForegroundColor Yellow
                Start-Service -Name "com.docker.service" -ErrorAction Stop
                Write-Host "Docker service started successfully" -ForegroundColor Green
                
                # Wait a moment for the service to fully start
                Start-Sleep -Seconds 3
                
                # Check if Docker daemon is now accessible
                try {
                    docker info --format "{{.ServerVersion}}" 2>$null | Out-Null
                    if ($LASTEXITCODE -eq 0) {
                        Write-Host "Docker daemon is now running after service start" -ForegroundColor Green
                        return $true
                    }
                }
                catch {
                    Write-Host "Docker daemon still not accessible after service start" -ForegroundColor Yellow
                }
            }
            else {
                Write-Host "Docker service is already running" -ForegroundColor Green
            }
        }
        else {
            Write-Host "Docker service (com.docker.service) not found" -ForegroundColor Yellow
        }
    }
    catch {
        Write-Host "Failed to manage Docker service: $_" -ForegroundColor Yellow
    }
    
    Write-Host "Attempting to start Docker Desktop..." -ForegroundColor Yellow
    
    # Try to start Docker Desktop application
    $dockerDesktopPaths = @(
        "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe",
        "${env:ProgramFiles(x86)}\Docker\Docker\Docker Desktop.exe",
        "${env:LOCALAPPDATA}\Programs\Docker\Docker\Docker Desktop.exe"
    )
    
    $dockerStarted = $false
    foreach ($path in $dockerDesktopPaths) {
        if (Test-Path $path) {
            Write-Host "Starting Docker Desktop from: $path"
            try {
                Start-Process -FilePath $path -ErrorAction Stop
                $dockerStarted = $true
                break
            }
            catch {
                Write-Host "Failed to start Docker Desktop from $($path): $_" -ForegroundColor Yellow
            }
        }
    }
    
    if (-not $dockerStarted) {
        Write-Host "Could not find Docker Desktop executable. Trying to start via Start-Process..." -ForegroundColor Yellow
        try {
            Start-Process "Docker Desktop" -ErrorAction Stop
            $dockerStarted = $true
        }
        catch {
            Write-Host "Failed to start Docker Desktop: $_" -ForegroundColor Yellow
        }
    }
    
    if ($dockerStarted) {
        Write-Host "Docker Desktop starting... waiting for daemon to be ready..." -ForegroundColor Yellow
        
        # Wait for Docker daemon to be ready (up to 60 seconds)
        $maxWaitTime = 60
        $waitTime = 0
        $interval = 2
        
        while ($waitTime -lt $maxWaitTime) {
            Start-Sleep -Seconds $interval
            $waitTime += $interval
            
            try {
                docker info --format "{{.ServerVersion}}" 2>$null | Out-Null
                if ($LASTEXITCODE -eq 0) {
                    Write-Host "Docker daemon is now ready!" -ForegroundColor Green
                    return $true
                }
            }
            catch {
                # Continue waiting
            }
            
            Write-Host "Still waiting for Docker daemon... ($waitTime/$maxWaitTime seconds)" -ForegroundColor Yellow
        }
        
        Write-Host "Docker daemon did not start within $maxWaitTime seconds" -ForegroundColor Red
        return $false
    }
    
    Write-Host "Failed to start Docker Desktop" -ForegroundColor Red
    return $false
}