ReCrutch.GitLab.psm1

[System.Reflection.Assembly]::LoadWithPartialName("System.Web")

function Set-ReCrGitLabSettings($apiUrl, $prj, $privTokenEnvVarName, $privTokenScripts, $privTokenValue)
{
    $ReCr.GitLab = @{
        ApiUrl = $apiUrl
        Prj = $prj
        PrivateToken = @{
            EnvVarName = $privTokenEnvVarName
            Scripts = $privTokenScripts
            PrivateToken = $privTokenValue
        }
    }
}

function Get-ReCrGitLabPrivateToken()
{
    try
    {
        $settings = $ReCr.GitLab.PrivateToken
        if($settings.PrivateToken -ne $null)
        {
            return $settings.PrivateToken
        }
        
        if($settings.EnvVarName -ne $null)
        {
            $pt = [System.Environment]::GetEnvironmentVariable($settings.EnvVarName)
            if($pt -ne $null)
            {
                return $pt
            }
        }

        if($settings.Scripts -ne $null)
        {
            foreach($script in $settings.Scripts)
            {
                if((Test-Path $script) -eq $true)
                {
                    . $script
                    if($settings.PrivateToken -ne $null)
                    {
                        return $settings.PrivateToken
                    }
                }
            }
        }

        if($settings.PrivateToken -eq $null)
        {
            $settings.PrivateToken = Read-Host -Prompt 'Input private token for accsess GitLab'
        }

        return $settings.PrivateToken
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function Invoke-ReCrGitLabRestApi($method, [string]$url, $urlParameters, $if404ToNull)
{
    $oldSecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol;
    try
    {
        $settings = $ReCr.GitLab

        $pt = Get-ReCrGitLabPrivateToken

        [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12;
        $urlParametersStr = ""
        if($urlParameters -ne $null)
        {
            $urlParametersStr = ($urlParameters.Keys | ForEach-Object {
                "{0}={1}" -f [System.Uri]::EscapeDataString($_), [System.Uri]::EscapeDataString($urlParameters[$_])
                }) -join "&"
        }

        $con = ""
        if([string]::IsNullOrEmpty($urlParametersStr) -ne $true -and $url.Contains("?") -ne $true -and $url.Contains("&") -ne $true)
        {
            $con = "?"
        }
        elseif([string]::IsNullOrEmpty($urlParametersStr) -ne $true -and $url.EndsWith("?") -eq $true)
        {
            $con = ""
        }
        elseif([string]::IsNullOrEmpty($urlParametersStr) -ne $true -and $url.EndsWith("&") -eq $true)
        {
            $con = ""
        }
        elseif([string]::IsNullOrEmpty($urlParametersStr) -ne $true -and $url.Contains("?") -eq $true)
        {
            $con = "&"
        }

        $fullUrl = "$($settings.ApiUrl)/projects/$([System.Web.HttpUtility]::UrlEncode($settings.Prj))$($url)$con$($urlParametersStr)"

        $uri = New-Object System.Uri -ArgumentList ($fullUrl)
        Repair-ReCrUri $uri
        $resp = Invoke-WebRequest -Uri $uri -Method $method -Headers @{
            "PRIVATE-TOKEN" = $pt
            "Content-Type" = "application/json;charset=utf-8"
        }
        $str=[system.Text.Encoding]::UTF8.GetString($resp.RawContentStream.ToArray());

        return ConvertFrom-Json $str
    }
    catch
    {
        if($if404ToNull -eq $true -and $_.Exception.GetType() -eq [System.Net.WebException])
        {
            if($_.Exception.Response.StatusCode -eq 404)
            {
                return $null
            }
        }

        throw new-object System.Exception ("$fullUrl", $_.Exception)
    }
    finally
    {
        [System.Net.ServicePointManager]::SecurityProtocol = $oldSecurityProtocol
    }
}

function Invoke-ReCrGitLabRestApiGet([string]$url, $urlParameters, $if404ToNull)
{
    return Invoke-ReCrGitLabRestApi Get $url $urlParameters $if404ToNull
}

function Invoke-ReCrGitLabRestApiPost([string]$url, $urlParameters, $if404ToNull)
{
    return Invoke-ReCrGitLabRestApi Post $url $urlParameters $if404ToNull
}

function Invoke-ReCrGitLabRestApiPut([string]$url, $urlParameters, $if404ToNull)
{
    return Invoke-ReCrGitLabRestApi Put $url $urlParameters $if404ToNull
}



# tags b

function Get-ReCrGitLabTag($tagName, $if404ToNull)
{
    try
    {
        $tag = Invoke-ReCrGitLabRestApiGet "/repository/tags/$tagName" -if404ToNull:$if404ToNull
        $tag
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function New-ReCrGitLabTag($branch, $tagName, $buildPath)
{
    <#if([string]::IsNullOrEmpty($tagName) -eq $true)
    {
        throw [System.Exception] "`$tagName requery"
    }#>


    #$tagName = "1"
    #$r = BuildGLRestGet "/repository/tags/" @{ per_page = 1000 } #?per_page=3&page=2&sort=commit.committed_date
    <#
    $r = $r | Sort-Object { $_.commit.committed_date } -Descending
    $r | Select-Object { $_.name; $_.commit.committed_date } | Format-Table
    $lastTag = $r[0]
    [Regex]::Matches($lastTag.name, "(?<stend>/)")
    $g = [Regex]::Matches($lastTag.name, "(?<stend>^\S[^/]+)/v(?<v1>\d+).(?<v2>\d+).(?<v3>\d+)")
    $g[0].Groups
    $nextVer = [int]::Parse($g[0].Groups["v2"].Value) + 1
    $newTagName = "$($g[0].Groups["stend"].Value)/v$($g[0].Groups["v1"].Value).$nextVer.$($g[0].Groups["v3"].Value)"
    #>


    <#$tag = BuildGLRestGet "/repository/tags/$tagName" -if404ToNull:true
 
    if($tag -ne $null)
    {
        throw [System.Exception] "Tag $tagName уже существует"
    }
 
 
    $issues = BuildGLRestGet "/issues" @{ labels = "$tagName"; state = "opened" }
    $issues = $issues | sort iid
    $issues.Count
    $issues | Format-Table iid, title
 
    $release_description = ""
    foreach ($issue in $issues)
    {
        $release_description += "* #$($issue.iid)
"
    }
 
    $newtag = BuildGLRestPost "/repository/tags/" @{
        tag_name = $tagName
        ref = $branch
        message = "$buildPath"
        release_description = $release_description
    }
    #>

}

function New-ReCrGitLabTagRelease($tagName, $description)
{
    try
    {
        if([string]::IsNullOrEmpty($tagName) -eq $true)
        {
             throw new-object System.Exception ("tagName require")
        }
        if([string]::IsNullOrEmpty($description) -eq $true)
        {
             throw new-object System.Exception ("description require")
        }

        $r = Invoke-ReCrGitLabRestApiPost "/repository/tags/$tagName/release/" @{ description = $description }
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function Update-ReCrGitLabTagRelease($tagName, $description)
{
    try
    {
        if([string]::IsNullOrEmpty($tagName) -eq $true)
        {
             throw new-object System.Exception ("tagName require")
        }
        if([string]::IsNullOrEmpty($description) -eq $true)
        {
             throw new-object System.Exception ("description require")
        }

        $r = Invoke-ReCrGitLabRestApiPut "/repository/tags/$tagName/release/" @{ description = $description }
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function Update-ReCrGitLabTagReleaseBuildPathAndIssues($tagName, $buildPath, $issueIds)
{
    # $tagName = "mrgrp-suip.v0.0-build.1-revision.1"
    #$buildPath = $buildDevServerPath
    try
    {
        if([string]::IsNullOrEmpty($tagName) -eq $true)
        {
             throw new-object System.Exception ("tagName require")
        }
        
        $tag = Get-ReCrGitLabTag -tagName:$tagName -if404ToNull:$true

        $release_description = ""
        if($tag.release -ne $null -and $tag.release.description -ne $null)
        {
            $release_description = $tag.release.description
        }

        $releaseDescription = "`r`n>>>`r`n"
        $releaseDescription += "Билд тут: "
        # \\aakozlov-vm4.test.local\C$\_MRGRP-SUIP\Builds\mrgrp-suip.v0.0-build.1-revision.1
        #Билд тут: \\aakozlov-vm4.test.local\C$_MRGRP-SUIP\Builds\mrgrp-suip.v0.0-build.1-revision.1
        #$releaseDescription += $buildPath.Replace("\\", "\\\\").Replace("$\", "$\\")
        $releaseDescription += "file://" + $buildPath.Replace("\\", "").Replace("\", "/")
        $releaseDescription += "`r`n ``(если не открывается, то нужно ссылку скопировать и вставить в Explorer, IE или файловый менеджер или новую вкладку браузера)``"

        $releaseDescription += "`r`n`r`n"
        $releaseDescription += "[issues](../issues?label_name%5B%5D=$tagName):"
        $releaseDescription += "`r`n"
        foreach ($id in $issueIds)
        {
            $releaseDescription += "* #$id `r`n"
        }
        $releaseDescription += ">>>"

        if($release_description -match "Билд тут:")
        {
            $pat = "[\r\n]+>>>[\r\n]+Билд тут: (.*[\r\n])+>>>"
            [System.Text.RegularExpressions.Regex]::Match($release_description, $pat, [System.Text.RegularExpressions.RegexOptions]::Multiline)
            $release_description = [System.Text.RegularExpressions.Regex]::Replace($release_description, $pat, "", [System.Text.RegularExpressions.RegexOptions]::Multiline)
        }


        $release_description += $releaseDescription + "`r`n`r`n"

        if($tag.release -eq $null)
        {
            New-ReCrGitLabTagRelease -tagName:$tagName -description:$release_description
        }
        else
        {
            Update-ReCrGitLabTagRelease -tagName:$tagName -description:$release_description
        }
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function Get-ReCrGitLabTagCommits($tagName, $regExpMaskTagName)
{
    #$tagName = "mrgrp-suip.v0.0-build.0-revision.1"
    #$regExpMaskTagName = "mrgrp-suip.v[0-9].[0-9]-build.[0-9]-revision.[0-9]"
    try
    {
        $tag = Get-ReCrGitLabTag -tagName:$tagName -if404ToNull:$false

        $tagCommits = Invoke-ReCrGitLabRestApiGet "/repository/commits/" @{
            ref_name = "$tagName"
        }

        $tags = Invoke-ReCrGitLabRestApiGet "/repository/tags/" @{
            order_by = "updated"
            sort = "desc"
        }

        $tags = $tags | Where-Object { 
            [System.Text.RegularExpressions.Regex]::IsMatch($_.name, $regExpMaskTagName) -eq $true `
            -and $_.commit.committed_date -lt $tag.commit.committed_date }

        #$tags | Format-List

        $tagCommitsCurrent = @()

        foreach($commit in $tagCommits)
        {
            #$commit = $tagCommits[0]
            #$commitRefs = Invoke-ReCrGitLabRestApiGet "/repository/commits/$($commit.short_id)/refs/"

            foreach($t in $tags)
            {
                if($t.name -eq $tag.name)
                {
                    continue
                }

                if($t.commit.id -eq $commit.id)
                {
                    $flag = $true
                    break
                }
            }

            if($flag -eq $true)
            {
                break
            }

            $tagCommitsCurrent += $commit
        }

        $tagCommitsCurrent
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function Get-ReCrGitLabTagCommitsIssueIds($tagName, $regExpMaskTagName)
{
    #$tagName = "mrgrp-suip.v0.0-build.0-revision.1"
    #$regExpMaskTagName = "mrgrp-suip.v[0-9].[0-9]-build.[0-9]-revision.[0-9]"
    try
    {
        $commits = Get-ReCrGitLabTagCommits -tagName:$tagName -regExpMaskTagName:$regExpMaskTagName

        $issueIds = @()

        [array]::Reverse($commits)

        foreach($commit in $commits)
        {
            $msg = $commit.message
            <#
            $msg = "[*] #1177 Структура решения
[*] #2 Структура решения #3"
 
            $msg = "Merge branch 'task/1-structure-solution' into 'master'
 
#1 Структура решения 2
 
See merge request MRGRP/SUIP!2"
#>


            $mathes = [System.Text.RegularExpressions.Regex]::Matches($msg, "(?<id>#[0-9]+)")

            foreach($math in $mathes)
            {
                $id = $math.Value.Replace("#", "")
                if($issueIds.Contains($id) -eq $false)
                {
                    $issueIds += $id
                }
            }
        }

        $issueIds
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

# tags e



# label b

function New-ReCrGitLabLabel($labelName, $color)
{
    try
    {
        if([string]::IsNullOrEmpty($labelName) -eq $true)
        {
            throw [System.Exception] "`$labelName requery"
        }

        $labels = Invoke-ReCrGitLabRestApiGet "/labels" @{ per_page = 1000 }
        $label = $labels | where name -eq $labelName
        if($label -ne $null)
        {
            return $label
        }

        $label = Invoke-ReCrGitLabRestApiPost "/labels/" @{
            name = $labelName
            color = $color
        }

        return $label
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

# label e



# Issue b

function Get-ReCrGitLabIssue($id, $if404ToNull)
{
    try
    {
        $issue = Invoke-ReCrGitLabRestApiGet "/issues/$id/" -if404ToNull:$if404ToNull
        $issue
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function Add-ReCrGitLabCommentToIssue($id, $body)
{
    try
    {
        $newNote = Invoke-ReCrGitLabRestApiPost "/issues/$id/notes" @{
                body = $body
            }
        $newNote
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

function Set-ReCrGitLabLabelToIssue($labelName, $labelBuildColor, $issueIds, $needToDeployLabel, $msg)
{
    try
    {
        $labelBuild = New-ReCrGitLabLabel -labelName:$labelName -color:$labelBuildColor

        $needToDeployLabelCmd = ""
        if($needToDeployLabel -ne $null)
        {
            $needToDeployLabelCmd = " ~`"$needToDeployLabel`""
        }

        foreach($id in $issueIds)
        {
            $issue = Get-ReCrGitLabIssue -id:$id -if404ToNull:$false
            if($issue -eq $null)
            {
                Write-Host-Indent 1 "issue not found by id '$id'" -foregroundColor:$ReCr.ConsoleColors.error
                continue
            }

            $body = "$msg`r`n`r`n/label ~`"$labelName`"$needToDeployLabelCmd`r`n"
            $newNote = Add-ReCrGitLabCommentToIssue -id:$id -body:$body
        }
    }
    catch
    {
        Show-ReCrError "$($MyInvocation.MyCommand)" $_.Exception
        throw new-object System.Exception ("$($MyInvocation.MyCommand) error", $_.Exception)
    }
}

# Issue e