Update-VersionInfo.psm1

# GIT Revision
function Get-GitRepository-Version([string] $file){
    try
    {
        $dir = Split-Path $file -Parent
        $rev = git -C "$dir" rev-list --all --count 2> $null
        $hash = git -C "$dir" rev-parse --short HEAD 2> $null

        if ([string]::IsNullOrEmpty($rev) -or [string]::IsNullOrEmpty($hash)) {
            Write-Warning "Not valid repository, or GIT command was not found"
            return $null
        }
        else{
            return new-object psobject -property @{ Revision = $rev; Hash = $hash}
        }
    }
    catch 
    {
        Write-Warning "Not valid repository, or GIT command was not found"
    }

    return $null;
}
# Set/Reset AssemblyInfo.cs
function Set-Csharp-Project-AssemblyInfo([string] $projectFile, $version, [bool] $useRevision){
    try{
        #vars
        $majorVersion = "1"
        $minorVersion = "0"
        $buildNumber = "0"
        
        #version
        if($null -eq $version){
            $revisionNumber = "0"
            $revisionHash = "0"
        }
        else{
            $revisionNumber = $version.Revision
            $revisionHash = $version.Hash
        }
        #useRevision
        if($null -eq $useRevision){
            $useRevision = $true
        }
        
        #validate assemblyFileName
        $dir = Split-Path $projectFile -Parent
        $assemblyFileName =  Join-Path -Path $dir  -ChildPath "Properties" | Join-Path -ChildPath "Assemblyinfo.cs" 
        #
        if( !(Test-Path $assemblyFileName -PathType Leaf ) ) {
            $msg = "Assembly info is missing [$assemblyFileName]."
            
            Write-Warning $msg
            throw $msg
        }
        
        # find version info
        $versionPattern='^\[assembly: AssemblyVersion\(\s*"(?<major>[1-9]+[0-9]*)\.(?<minor>[0-9]+)\.(?<build>[0-9]+)(\.[0-9]+)?"\s*\)\]'
        (Get-Content $assemblyFileName) | Foreach-Object { if($_ -match $versionPattern) { $majorVersion=$matches["major"]; $minorVersion=$matches["minor"]; $buildNumber=$matches["build"] } }

        if($useRevision){
            Write-Verbose "Version number was determined as $majorVersion.$minorVersion.$buildNumber.$revisionNumber"
            $versionReplace= "[assembly: AssemblyVersion(`"$majorVersion.$minorVersion.$buildNumber.$revisionNumber`")]"
        }
        else{
            Write-Verbose "Version number was used as $majorVersion.$minorVersion.$buildNumber.0"
            $versionReplace= "[assembly: AssemblyVersion(`"$majorVersion.$minorVersion.$buildNumber.0`")]"
        }
        #
        $versionInfoPattern='^\[assembly: AssemblyInformationalVersion\(\s*"(?<major>[1-9]+[0-9]*)\.(?<minor>[0-9]+)\.(?<build>[0-9]+)(\.[a-z0-9]+)?"\s*\)\]'
        (Get-Content $assemblyFileName) | Foreach-Object { if($_ -match $versionPattern) { $majorVersion=$matches["major"]; $minorVersion=$matches["minor"]; $buildNumber=$matches["build"] } }
        $versionInfoReplace = "[assembly: AssemblyInformationalVersion(`"$majorVersion.$minorVersion.$buildNumber.$revisionHash`")]"
        
        # replace version info
        (Get-Content $assemblyFileName) | ForEach-Object { $_ -replace $versionPattern,$versionReplace} | ForEach-Object { $_ -replace $versionInfoPattern,$versionInfoReplace} | Set-Content -Path $assemblyFileName -Force
    
        # report
        $file = Split-Path $projectFile -Leaf
        if($null -eq $version){
           Write-Host "#### AVT RESET $majorVersion.$minorVersion.$buildNumber.$revisionNumber [$file] ####"
        }
        else{
           if($useRevision){
               Write-Host "#### AVT SET $majorVersion.$minorVersion.$buildNumber.$revisionNumber for $revisionHash [$file] ####"
           }
           else{
               Write-Host "#### AVT SET $majorVersion.$minorVersion.$buildNumber.0 for $revisionHash [$file] ####"
           }
        }
        return "$majorVersion.$minorVersion.$buildNumber"
    }
    catch{
        Write-Warning "Can't set assembly info [$projectFile]."
        return $null
    }
}
# Set/Reset c# project VersionInfo
function Set-Csharp-Project-VersionInfo([string] $projectFile, $version, [bool] $useRevision){
    try{
        #vars
        $majorVersion = "1"
        $minorVersion = "0"
        $buildNumber = "0"
        
        #version
        if($null -eq $version){
            $revisionNumber = "0"
            $revisionHash = "0"
        }
        else{
            $revisionNumber = $version.Revision
            $revisionHash = $version.Hash
        }
        #useRevision
        if($null -eq $useRevision){
            $useRevision = $true
        }        
        
        # find version info
        $versionPattern='<Version>((?<major>[1-9]+[0-9]*)\.(?<minor>[0-9]+)\.(?<build>[0-9]+)(\.[0-9]+)?)</Version>'
        (Get-Content $projectFile) | Foreach-Object { if($_ -match $versionPattern) { $majorVersion=$matches["major"]; $minorVersion=$matches["minor"]; $buildNumber=$matches["build"] } }
        
        if($useRevision){
            Write-Verbose "Version number was determined as $majorVersion.$minorVersion.$buildNumber.$revisionNumber"
            $versionReplace= "<Version>$majorVersion.$minorVersion.$buildNumber.$revisionNumber</Version>"
        }
        else{
            Write-Verbose "Version number was used as $majorVersion.$minorVersion.$buildNumber.0"
            $versionReplace= "<Version>$majorVersion.$minorVersion.$buildNumber.0</Version>"
        }
        #
        $versionInfoPattern='<InformationalVersion>((?<major>[1-9]+[0-9]*)\.(?<minor>[0-9]+)\.(?<build>[0-9]+)(\.[a-z0-9]+)?)</InformationalVersion>'
        (Get-Content $projectFile) | Foreach-Object { if($_ -match $versionPattern) { $majorVersion=$matches["major"]; $minorVersion=$matches["minor"]; $buildNumber=$matches["build"] } }
        $versionInfoReplace = "<InformationalVersion>$majorVersion.$minorVersion.$buildNumber.$revisionHash</InformationalVersion>"
        
        # replace version info
        (Get-Content $projectFile) | ForEach-Object { $_ -replace $versionPattern,$versionReplace} | ForEach-Object { $_ -replace $versionInfoPattern,$versionInfoReplace} | Set-Content -Path $projectFile -Force
    
        # report
        $file = Split-Path $projectFile -Leaf
        if($null -eq $version){
           Write-Host "#### AVT RESET $majorVersion.$minorVersion.$buildNumber.$revisionNumber [$file] ####"
        }
        else{
           if($useRevision){
               Write-Host "#### AVT SET $majorVersion.$minorVersion.$buildNumber.$revisionNumber for $revisionHash [$file] ####"
           }
           else{
               Write-Host "#### AVT SET $majorVersion.$minorVersion.$buildNumber.0 for $revisionHash [$file] (no-revision) ####"
           }
        }
        return "$majorVersion.$minorVersion.$buildNumber"
    }
    catch{
        Write-Warning "Can't set version info [$projectFile]."
        return $null
    }
}
# Set/Reset nuspec VersionInfo
function Set-Nuspec-VersionInfo([string] $nugetFile, [string] $textversion){
    try{
        if( ![string]::IsNullOrEmpty($textversion) ){
            Write-Verbose "Update nuspec to $textversion [$(Split-Path -Leaf $nugetFile)]"

            $versionPattern='<version>(([1-9]+[0-9]*)\.([0-9]+)\.([0-9]+)(?<name>(-[\.0-9a-z]+)?))</version>'
            $wrongVersionPattern = '<version>(?<version>([1-9]+[0-9]*)\.([0-9]+)\.([0-9])+)(\.[0-9a-z-]+)?</version>'

            $content = (Get-Content $nugetFile) | ForEach-Object { 
                if ( $_ -match $versionPattern){
                    if([string]::IsNullOrEmpty($Matches["name"])){
                        $versionReplace= "<version>$textversion</version>"
                    }
                    else{
                        $versionReplace= "<version>$($textversion)$($matches[""name""])</version>"
                    }
                    $_ -replace $versionPattern,$versionReplace
                }
                elseif( $_ -match $wrongVersionPattern ){ 
                    $versionReplace= "<version>$textversion</version>"
                    $_ -replace $wrongVersionPattern,$versionReplace
                }
                else{
                    $_
                }
            } 
            Set-Content -Path $nugetFile -Value $content -Force
        }
    }
    catch{
        Write-Warning "Can't set nuspec version info [$nugetFile]."
    }
}
# process c# proj
function Set-Csharp-Project([string] $projectFile, $version){
    #
    $textVersion = $null
    #
    $nugetFile = $projectFile.Replace(".csproj",".nuspec")
    if(Test-Path $nugetFile -PathType Leaf){
        $hasNugetFile = $true
    }
    else{
        $hasNugetFile = $false
    }
    $useRevision = -not $hasNugetFile
    #
    try{
        [xml] $project = Get-Content $projectFile
        if ($project.Project.Sdk.Length -eq 0){
            Write-Verbose "Using AssemblyInfo.cs [$(Split-Path $projectFile -Leaf)]"
            $textVersion = Set-Csharp-Project-AssemblyInfo $projectFile $version $useRevision
        }
        else{
            $textVersion = Set-Csharp-Project-VersionInfo $projectFile $version $useRevision
        }
        
        if ($hasNugetFile -and !($null -eq $version)){
            Set-Nuspec-VersionInfo $nugetFile $textVersion
        }
    }
    catch{
        Write-Warning "Not valid project file [$projectFile]."
    }
}
# process sln file
function Set-VisualStudio-Solution([string] $slnFile, $version){
    try{
        # get all projects
        $projects = Get-Content $slnFile |
                     Where-Object { $_ -match "^Project" } | 
                     ForEach-Object { $_ -match ".*=(.*)$" | out-null ; $matches[1] } | 
                     ForEach-Object { $_.Split(",")[1].Trim().Trim('"') } |
                     Where-Object { $_ -match ".*\.csproj$" }
        
        #
        $dir = Split-Path $slnFile -Parent

        #process
        foreach($p in $projects){
            Write-Verbose "Processing project: $p"
            $prj =  Join-Path -Path $dir -ChildPath $p
            Set-Csharp-Project $prj $version
        }
    }
    catch{
        Write-Warning "Not valid solution file [$slnFile]."
    }
}
<#
.Synopsis
   Automatic Version Tool (AVT)
.DESCRIPTION
   Update VersionInfo from GIT
.EXAMPLE
   Update-VersionInfo set D:\Projects\project.csproj
.EXAMPLE
   Update-VersionInfo reset D:\Projects\project.csproj
.EXAMPLE
   Update-VersionInfo set D:\Projects\project.sln
.EXAMPLE
   Update-VersionInfo reset D:\Projects\project.sln
#>

function Update-VersionInfo
{
    [CmdletBinding()]
    [Alias()]
    [OutputType([void])]
    Param
    (
        # Mode set or reset
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$false, HelpMessage="Command type.")]
        [ValidateSet('set','reset')]
        [string]
        $Mode,
        # Project/Solution file (csproj,sln)
        [string]
        [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$false, HelpMessage="Project file.")]
        $File
    )
    Begin
    {
        if( !(Test-Path $File -PathType Leaf ) ) {
            throw "File is missing [$File]."
        }
    }
    Process
    {
         switch ($Mode){
            'set' {
                #
                $version = Get-GitRepository-Version $File
                #
                if ($File -like '*.csproj') {
                    Set-Csharp-Project $File $version
                }
                elseif ($File -like '*.sln') {
                    Set-VisualStudio-Solution $File $version
                }
                else{
                    throw "Not supported [$File]."
                }
            }
            Default {
                if ($File -like '*.csproj') {
                    Set-Csharp-Project $File
                }
                elseif ($File -like '*.sln') {
                    Set-VisualStudio-Solution $File 
                }
                else{
                    throw "Not supported [$File]."
                }
            }
         }
    }
    End
    {
    }
}
Export-ModuleMember -Function Update-VersionInfo
# SIG # Begin signature block
# MIIIEwYJKoZIhvcNAQcCoIIIBDCCCAACAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDaYXRrk/3bnv3S
# 0Tw6VNTUDZlRY4wMJiLlvxygPGrSvqCCBQ8wggULMIIC86ADAgECAgIApTANBgkq
# 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
# AgELMQ4wDAYKKwYBBAGCNwIBFjAvBgkqhkiG9w0BCQQxIgQgVqEuiPhGV2k8Xgjh
# fVdHpTMqIrC5VK8pcvE5JHkoCawwDQYJKoZIhvcNAQEBBQAEggEAS/bmpP+Ye9Tq
# vnYQ6abi5yDblijP8m3RsoaFJFLo5PQ0nY1l3AVSzYOu3nPMI5zGscMrlZlLq1JO
# qD5YSALnAgnWPtX6tRYxSq34Z/KliDiMNMQz+EkemCyqrweATKd0cxa9Mbk1Wf8V
# NORKBg98vrQmpyqmk8pndTwW0jv891rD/GBhY/H+cpTSKsKQfNI0hHJ0zvZf9Dr6
# 4vp7DYpivOQxwWnjHN5QkrR6DBMAB49MuyGrLB/xD/tVfdRjRdCPmXdaw5L4Qf/q
# JoKoQ8HA8qRED6xTR2WbxGGxAcLPRF95c27992RIa6dye039Xm41VkcF/PbnOjlQ
# PgjWJSS5FQ==
# SIG # End signature block