PSKeepAChangeLog.psm1

function New-ChangeLog {
    [CmdletBinding()]
    param(
        [parameter(Mandatory)]
        [string]
        $Path,

        [parameter()]
        [string]
        $TagSortPattern = "^[Vv]"
    )

    begin {
        $template = Get-Content $PSScriptRoot\template.md
    }

    process {
        $CommitInfo = Get-ChangeLogCommitInfo -TagSortPattern $TagSortPattern

        $release = $template -replace '{{commit_info}}', $CommitInfo

        $links = Get-GitComparisonLinks -TagSortPattern $TagSortPattern

        $release = $release -replace '{{links}}', ($links -join [System.Environment]::NewLine)

        New-Item $Path -ItemType File -Force | Out-Null
        Set-Content -Path $Path -Value $release -Force
    }
}
function Get-ChangeLogCommitInfo {
    [CmdletBinding()]
    param(
        [parameter()]
        [string]
        $TagSortPattern = "^[Vv]"
    )

    begin {
        $tags = Get-OrderedGitTags -VersionPattern $TagSortPattern

        $searchUntil = "..HEAD"

        $Output = @()
    }

    process {
        foreach ($tag in $tags) {
            $currentTagIndex = $tags.IndexOf($tag)

            if ($currentTagIndex -eq $tags.Count - 1) {
                $searchUntil = ""
            }

            $commits = Get-SerializedCommits -Filter "refs/tags/$tag$searchUntil"

            if ($searchUntil -eq "..HEAD") {
                $title = "## [Unreleased]"
            } else {
                $title = "## [$($tags[$currentTagIndex-1])] - $(get-date $commits[0].Date -Format "dd-MM-yyyy")"
            }

            $Output += $title

            $commits | ForEach-Object -Process {
                $Output += " - $($_.Subject)"
            }

            $Output += ""
            $searchUntil = "..refs/tags/$tag"
        }

        return $Output -join [System.Environment]::NewLine
    }
}
function Get-GitComparisonLinks {
    [cmdletbinding()]
    param(
        [parameter()]
        [string]
        $TagSortPattern = "^[Vv]"
    )

    begin {
        $gitUrl = (git remote get-url origin) -replace '\.git$'
        $tags = @("HEAD") + (Get-OrderedGitTags -VersionPattern $TagSortPattern)
    }

    process {
        $i = -1
        while (++$i -lt $tags.count - 2) {
            $versionName = if($tags[$i] -eq "HEAD") {
                "Unreleased"
            } else {
                $tags[$i]
            }

            "[{0}]: {1}/compare/{2}...{3}" -f $versionName, $gitUrl, $tags[$i+1], $tags[$i]
        }
    }
}
function Get-OrderedGitTags {
    [cmdletbinding(DefaultParameterSetName = "desc")]
    param(
        [parameter(ParameterSetName = "aesc")]
        [switch]
        $Ascending,

        [parameter(ParameterSetName = "desc")]
        [switch]
        $Descending,

        [parameter()]
        [string]
        $VersionPattern = '^[Vv]'
    )

    process {
        $Tags = ((git show-ref --tags) -match 'refs/tags') -replace '.+refs/tags/'

        $Tags | Sort-Object { [system.version]($_ -creplace $VersionPattern)} -Descending:$($PSCmdlet.ParameterSetName -eq "desc")
    }
}

function Get-SerializedCommits {
    [cmdletbinding()]
    param(
        [parameter(Mandatory)]
        [string]
        $Filter
    )

    process {
        Write-Verbose "finding commits using filter $filter"
        $committext = (git log $filter --format="%ai`t%H`t%an`t%ae`t%s`t%b")
        return $committext | ConvertFrom-Csv -Delimiter "`t" -Header ("Date", "CommitId", "Author", "Email", "Subject", "Body")
    }
}
Export-ModuleMember -Function New-ChangeLog