Publish-Website.psm1


function Get-Project-Libraries
{
    Param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage="Project file")]
        [string]
        $Project,
        [Parameter(HelpMessage="Build configuration")]
        [string]
        $Configuration = 'Release',
        #Level
        [int]
        $Level = 0
    )
    Begin{
    }
    Process{
        # Log with Level
        Write-Verbose "$(''.PadLeft($Level,' ')) Reading project $([IO.Path]::GetFullPath($Project))"
        # Get content

        $xmlContent = [xml] (Get-Content -LiteralPath $Project)
        $directory = Split-Path $Project -Parent

        # Referenced libraries with HintPath
        $xmlContent.Project.ItemGroup.Reference.HintPath  | ForEach-Object {
            if($_){
                Write-Output ([IO.Path]::GetFullPath([IO.Path]::Combine($directory, $_)))
            }
        }
        # Project assembly name

        $assemblyName = $null

        $xmlContent.Project.PropertyGroup.AssemblyName | ForEach-Object {
            if($_){
                $assemblyName = $_ + ".dll"

                if($xmlContent.Project.Sdk.Length){
                    Write-Output ([IO.Path]::GetFullPath([IO.Path]::Combine($directory, 'bin', $Configuration, 'netstandard2.0', $assemblyName)))
                }
                else{
                    Write-Output ([IO.Path]::GetFullPath([IO.Path]::Combine($directory, 'bin', $Configuration, $assemblyName)))
                }
            }
        }
        #
        if($null -eq $assemblyName){
            $assemblyName = [IO.Path]::GetFileNameWithoutExtension($Project) + ".dll"
            Write-Output ([IO.Path]::GetFullPath([IO.Path]::Combine($directory, 'bin', $Configuration, $assemblyName)))
        }
        $Level += 4
        # Subproject
        Get-Content -LiteralPath $Project | Where-Object { $_ -match '<ProjectReference Include="(.+)"' } | ForEach-Object {
            $anotherProjectPath = Join-Path -Path $directory -ChildPath $matches[1]
            Get-Project-Libraries -Project $anotherProjectPath -Configuration $Configuration $Level
        }
    }
    End{
    }
}

<#
.SYNOPSIS
    Automati Webform Publish Tool (AWT)
.DESCRIPTION
    Compile and publish website projects
.PARAMETER Solution
    Path to solution file
.PARAMETER Configuration
    Build configuration.
 
    Valid values are Release and Debug.
     
    Default is Release.
.EXAMPLE
    Publish-Website -Solution .\cisss.sln -Configuration 'Release'
#>

function Publish-Website
{
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, HelpMessage="Solution file")]
        [string]
        $Solution,

        [Parameter(HelpMessage="Build configuration")]
        [string]
        $Configuration = "Release",

        [ValidateSet('quiet','minimal','normal',' detailed', 'diagnostic')]
        $Verbosity = "quiet"
    )
    Begin{
        if ($null -eq (Get-Command dotnet -ErrorAction SilentlyContinue)) { throw "Please install dotnet SDK. [ Download https://dotnet.microsoft.com/en-us/download ]" }
        if ($null -eq (Get-Command nuget  -ErrorAction SilentlyContinue)) { throw "Please install nuget CLI. [ Download https://www.nuget.org/downloads & add copy nuget.exe to %userprofile%\.dotnet\tools ]" }
        if( $null -eq (Get-MsBuildCommand -ErrorAction SilentlyContinue)) { throw "Please install Visual Studio. [ Download https://visualstudio.microsoft.com/downloads/ ]" }
    }
    Process{
        if(!(Test-Path -LiteralPath $Solution -PathType Leaf)){ throw "$Solution is not valid" }
        #
        $solutionFile = ( Resolve-Path $Solution ).Path
        $solutionPath = Split-Path $solutionFile -Parent
        #
        Write-Verbose "Remove bin & obj [$solutionPath]"
        Get-ChildItem $solutionPath -Recurse -Directory -Filter obj | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
        Get-ChildItem $solutionPath -Recurse -Directory -Filter bin | Where-Object -Property FullName -NotMatch node_modules | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue
        #restore nugets
        Write-Output "#### AWT RESTORE nugets ####"
        nuget restore $solutionFile -verbosity quiet
        if($LastExitCode) {    throw "NUGET RESTORE failed" }            
        #prepare publish
        $projectSections = Get-Content -LiteralPath $solutionFile -Delimiter "EndProject"
        $projectSections | ForEach-Object {
            # Spracuj iba WebSite projekty
            if($_ -match "Project\(`"{E24C65DC-7377-472B-9ABA-BC803B73C61A}`"\) = `"([^`"]*)`", `"([^`"]*)`""){
                $site = $Matches[1]
                $sitePath = $Matches[2]
                Write-Verbose "Found website project $site with $sitePath"

                $binPath = [IO.Path]::Combine($solutionPath, $sitePath, 'bin')
                if(!(Test-Path -LiteralPath $binPath -PathType 'Container')){
                    New-Item -Path $binPath -Type 'Directory' | Out-Null
                }

                $publishPath = $null
                if($_ -match "$Configuration.AspNetCompiler.TargetPath = `"([^`"]*)`""){
                    $publishPath = ([IO.Path]::Combine($solutionPath, $Matches[1])) 
                }    

                Write-Verbose "Remove publish [$publishPath] & bin [$binPath] folder "
                #
                if($null -ne $publishPath) { Remove-Item -Path $publishPath -Force -Recurse -ErrorAction SilentlyContinue }
                Get-ChildItem -Path $binPath -Recurse | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue

                #
                if($_ -match "ProjectReferences = `"([^`"]*)`""){
                    $childProjects = @()
                    $Matches[1] -split ';' | ForEach-Object {
                        $projectReference, $library = $_.Split('|')

                        if($library){
                            $projectSections | Where-Object { $_ -match "Project\(`"{.*}`"\) = `"[^`"]*`", `"([^`"]*)`", `"$projectReference`"" } | Select-Object -First 1 | Out-Null
                            Write-Verbose "Found required library $library from project $($Matches[1])"
                            $projectFile = [IO.Path]::Combine($solutionPath, $Matches[1])
                            $projectPath = Split-Path $projectFile -Parent

                            $fileName = [IO.Path]::GetFileName($projectFile)

                            if($childProjects -notcontains $projectFile){
                                $childProjects+= $projectFile
                            }
                            #
                            if((Get-Content -LiteralPath $projectFile) -match "<Project Sdk=`"Microsoft.NET.Sdk`">"){
                                $libraryPath = [IO.Path]::Combine($projectPath, 'bin', $Configuration, 'netstandard2.0', $library)
                            }
                            else{
                                $libraryPath = [IO.Path]::Combine($projectPath, 'bin', $Configuration, $library)
                            }                            
                            #
                            Write-Output "#### AWT BUILD .netstandard/.net48 library ($fileName) [msbuild] ####"
                            $buildResult = Invoke-MsBuild $projectFile  -ShowBuildOutputInCurrentWindow -MsBuildParameters "/restore /target:Restore;Build /p:RestoreLockedMode=true /p:Configuration=$Configuration /p:BuildInParallel=true /verbosity:$Verbosity /maxCpuCount /nologo /noWarn:CS0618;CS0169;CS0168;CS1685"
                            if($buildResult.BuildSucceeded -eq $false){ throw ("Build failed after {0:N1} seconds." -f $buildResult.BuildDuration.TotalSeconds)    }
                            #
                            if(!(Test-Path -LiteralPath $libraryPath -PathType 'Leaf')){ throw("Missing library $libraryPath") }    
                        }
                    }

                    Write-Output "#### AWT PREPARE website: $site ####"

                    # Copy child dependencies
                    $childProjects | Get-Project-Libraries -Configuration $Configuration | Select-Object -Unique | ForEach-Object {
                        if(Test-Path -LiteralPath $_ -PathType 'Leaf'){
                            Write-Verbose "Copying $_ to $binPath"
                            Copy-Item -LiteralPath $_ -Destination $binPath -Force -ErrorAction SilentlyContinue
                        }
                        else{
                            Write-Warning "Missing $_!"
                        }
                    }
                }
            }
        }
        #publish
        $projectSections | ForEach-Object {
            # Spracuj iba WebSite projekty
            if($_ -match "Project\(`"{E24C65DC-7377-472B-9ABA-BC803B73C61A}`"\) = `"([^`"]*)`", `"([^`"]*)`""){
                $site = $Matches[1]

                $publishPath = $null
                if($_ -match "$Configuration.AspNetCompiler.TargetPath = `"([^`"]*)`""){
                    $publishPath = ([IO.Path]::Combine($solutionPath, $Matches[1])) 
                }    

                Write-Output "#### AWT PUBLISH website: $site ####"

                # Build Website
                $siteName = $site.Replace(".","_")
                #
                $buildResult = Invoke-MsBuild $solutionFile -ShowBuildOutputInCurrentWindow -MsBuildParameters "/restore /target:$siteName;Restore;Build;Publish /p:RestoreLockedMode=true /p:Configuration=$Configuration /p:BuildInParallel=true /verbosity:$Verbosity /maxCpuCount /nologo /noWarn:CS0618;CS0169;CS0168;CS1685"
                if($buildResult.BuildSucceeded -eq $false){    throw ("Publish failed after {0:N1} seconds." -f $buildResult.BuildDuration.TotalSeconds) }

                # Cleanup
                if($null -ne $publishPath) {
                    Write-Verbose "Publish cleanup [$publishPath]"
                    Get-ChildItem -Path $publishPath -Recurse -Filter *.pdb -File -ErrorAction SilentlyContinue | Remove-Item -Force
                }    
            }
        }
    }
    End{
    }
}
Export-ModuleMember -Function Publish-WebSite
# SIG # Begin signature block
# MIIIEwYJKoZIhvcNAQcCoIIIBDCCCAACAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBXp7dVsPl+9OtG
# lOv0HBfbIlqh9TBH/IPyN+SoxUq4eqCCBQ8wggULMIIC86ADAgECAgIApTANBgkq
# hkiG9w0BAQsFADCBoDELMAkGA1UEBhMCU0sxETAPBgNVBAgTCFNsb3Zha2lhMRMw
# EQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpMb210ZWMuY29tMSswKQYDVQQD
# EyJMb210ZWMuY29tIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MScwJQYJKoZIhvcN
# AQkBFhhJbXJpY2guU3pvbGlrQGxvbXRlYy5jb20wHhcNMTkwMzI2MTUwNDQzWhcN
# MzAwMTAxMDAwMDAwWjCBkzELMAkGA1UEBhMCU0sxETAPBgNVBAgTCFNsb3Zha2lh
# MRMwEQYDVQQHEwpCcmF0aXNsYXZhMRMwEQYDVQQKEwpMb210ZWMuY29tMR4wHAYD
# VQQDExVMb210ZWMuY29tIFBvd2Vyc2hlbGwxJzAlBgkqhkiG9w0BCQEWGEltcmlj
# aC5Tem9saWtAbG9tdGVjLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBAL4n4nCHbkdrf09IHNFQ2P/z6I43GKScsFJOQHUMkRD3ALoUFL/URC9sW2fY
# rqG5VkFAKhnM0VxeiICR53cAshNShjFf58PhaS973jtCoJcaugIVBVoFIuQ+gnNY
# Jp2VdBbIPKMW4JjjZOxBEkHpMWmfitXKWGKeU68Qcn3oI6PO+YSztfXLe1NU+1GI
# O3fA7E0vHVPUf/qWZXMYU5ElLQVm9AXbfX79mTgl76A57+OC6j+Aehkd2OPfUl4w
# snox3fOyUAUA8iojeWh97PpXd/s+RkuxWdgsC4YSWDUjhZSzBkml9uerYqo9a+XA
# b39dvkpK9TPl3q5HNBQMkCfp8bkCAwEAAaNaMFgwCQYDVR0TBAIwADALBgNVHQ8E
# BAMCB4AwKwYDVR0lBCQwIgYIKwYBBQUHAwMGCisGAQQBgjcCARUGCisGAQQBgjcC
# ARYwEQYJYIZIAYb4QgEBBAQDAgSwMA0GCSqGSIb3DQEBCwUAA4ICAQAJJapv9skY
# jh5HTsJnqDdtqh7YOOXuA8g+DKBj+5gDEZE5V9VhAFVp8UJ9RoITGGIRTVId0lqc
# LJiVSTHx305VW9aID8vo77kfrTyXvPXNIsTtHnPkkwH47+CoiY3IpPQLjUA/Q6Qd
# qwINvvwom7/Wc+OoIqlPdJH5DbBrIy85dr6M/Lm3Rw2BolcTRwXTB3xAhweth78B
# P6pbcAd32FdymkRopLIihuNs7g7ZR/Q/5803G+OiQIMRGyTvaQ+aQjJgFpkzp7NI
# whzougfCOV47Sc89jEpUqw16i2UFfz2ywOlWUyYtue1S1PjM1ljgJfRo+e0wUnFp
# gFQzXGF1bTYVaQ4e3nJleADfvqeXoH2AYeBTbz9BcogkkfURAC3iiob1bNs5jE1C
# brEDCw6m/03k0oOmm3xQksXyAhBYuUkRwu9jd4y3FwZ/syDGLz3b6cY8o1YyINOO
# A3B2r92shNt0rWhJu3v+qcIVmFQ0aKlhNNRoiVlQgJ7NgO0UV+vU2lgiscpUSxEt
# xKN+450r49su06NA6zsyn3CELUmVkPyjx5fyizwt9KxVuYOUSEb32Y7QCffHJ1qt
# F6SuQrKbgb/24y7cCDW6PDVRvPOySUlKu9sPykICDjvzXBvjYEILM7AmtYCIBGQg
# imBsQvTEoXJXiwAOi1XNz3LxcjQHQ/ZM4zGCAlowggJWAgEBMIGnMIGgMQswCQYD
# VQQGEwJTSzERMA8GA1UECBMIU2xvdmFraWExEzARBgNVBAcTCkJyYXRpc2xhdmEx
# EzARBgNVBAoTCkxvbXRlYy5jb20xKzApBgNVBAMTIkxvbXRlYy5jb20gQ2VydGlm
# aWNhdGlvbiBBdXRob3JpdHkxJzAlBgkqhkiG9w0BCQEWGEltcmljaC5Tem9saWtA
# bG9tdGVjLmNvbQICAKUwDQYJYIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEK
# MAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3
# AgELMQ4wDAYKKwYBBAGCNwIBFjAvBgkqhkiG9w0BCQQxIgQgOqNIJTGA5hnmJh5W
# WisUTZMYtwi4CFFLjBRn1Ac2CkcwDQYJKoZIhvcNAQEBBQAEggEAAeS+DqBxclWF
# E4E1nW1i5h4hmxQtLt23qLc5p2VTEpo162+f95+aZNC+1dqy94QJSUQ0U0wfy+7A
# 3c5IX9BJqYgcPT8AiwJlUvHUWO50FWWz/9yQjQkg9nmRSY2XBLpB6UR1NHjPQ5Kw
# NUWf5603sNPgiU0j2Bq+drUV7zs2uOx59CKhfThd4cewTMFN65ueheX+5I1nSMnB
# mu74il4e7FehUf/EjZbjrXVNsIRqGKjfm+49JIAv0ymk75qtcidC6/usjWRZ/N7y
# foQCXFqCPahb11pXD7Vt40oZ1/Z2RCWHTcghyXc7U5mek9zY4EClJa20Qu9EHTrs
# b4n+VXe1CA==
# SIG # End signature block