public/Get-TagNext.ps1

function Get-TagNext {
    [CmdletBinding()]
    param (
        [Parameter()]
        [ValidateSet('calver', 'semver')]
        [string]$TagConvention
    )

    try {
        if ($PSCmdlet.ShouldProcess("<tag> of Convention '$TagConvention'", 'get')) {
            $tagMostRecent = Execute-Command { git describe --tags --abbrev=0 } -ErrorAction Continue | Select-Object -Last 1
            if ($TagConvention) {
                if ($tagMostRecent) {
                    if ($TagConvention -eq 'calver' -and $tagMostRecent -notmatch '^\d{8}\.\d+\.\d+$') {
                        throw "-TagConvention is calver but most recent tag is not calver"
                    }
                    if ($TagConvention -eq 'semver' -and ($tagMostRecent -notmatch '^v?\d+\.\d+\.\d+$' -or $tagMostRecent -match '^\d{8}\.\d+\.\d+$')) {
                        throw "-TagConvention is semver but most recent tag is not semver"
                    }
                }else {
                    "No tags found in this repo. Using specified -TagConvention" | Write-Verbose
                }
            }else {
                if ($tagMostRecent) {
                    "Tag convention will be determined based on previous tags: $TagConvention" | Write-Verbose
                    $TagConvention = if ($tagMostRecent -match '^\d{8}\.\d+\.\d+$') {
                        'calver'
                    }elseif ($tagMostRecent -match '^v?\d+\.\d+\.\d+$') {
                        'semver'
                    }else {
                        throw "Most recent tag is not in calver or semver format"
                    }
                }else {
                    throw "No tags found in this repo. Please specify a -TagConvention"
                }
            }
            "Tag convention: $TagConvention" | Write-Verbose

            $commitTitles = if (!$tagMostRecent ) {
                Execute-Command { git log master --format=%s } -ErrorAction Stop
            }else {
                Execute-Command { git log "$tagMostRecent..master" --format=%s } -ErrorAction Stop
            }
            if (!$commitTitles) {
                throw "No commits found between 'master' and '$tagMostRecent'"
            }
            $major = 0
            $minor = 0
            $patch = 0
            foreach ($t in $commitTitles) {
                if ($t -match '^(breaking)') {
                    $major++
                }elseif ($t -match '^(enhancement|feature|refactor)') {
                    $minor++
                }elseif ($t -match '^(chore|docs|fix|hotfix|style)') {
                    $patch++
                }
            }
            "Major commits or PRs: $major, Minor commits or PRs: $minor, Patch commits or PRs: $patch" | Write-Verbose
            if ($TagConvention -eq 'calver') {
                # Calver. E.g. 20230910.0.0
                if ($tagMostRecent) {
                    $tagMostRecentV = [version]($tagMostRecent -replace '^v', '')
                    if ("$( $tagMostRecentV.Major )" -eq (Get-Date -Format 'yyyyMMdd')) {
                        if ($major) {
                            "$( Get-Date -Format 'yyyyMMdd' ).$( $tagMostRecentV.Minor + 1).0" # E.g. 20230910.0.0 -> 20230910.1.0
                        }elseif ($minor) {
                            "$( Get-Date -Format 'yyyyMMdd' ).$( $tagMostRecentV.Minor + 1).0" # E.g. 20230910.0.0 -> 20230910.1.0
                        }elseif ($patch) {
                            "$( Get-Date -Format 'yyyyMMdd' ).$( $tagMostRecentV.Minor ).$( $tagMostRecentV.Build + 1 )" # E.g. 20230910.0.0 -> 20230910.0.1
                        }else {
                            # Should not arrive here
                            throw "Couldn't determine Calver tag, because the commit messages do not follow Conventional Commits."
                        }
                    }else {
                        "$( Get-Date -Format 'yyyyMMdd' ).0.0" # E.g. 20230910.0.0
                    }
                }else {
                    "$( Get-Date -Format 'yyyyMMdd' ).0.0" # E.g. 20230910.0.0
                }
            }
            if ($TagConvention -eq 'semver') {
                # Semver. E.g. v0.0.1 or 0.0.1
                if ($tagMostRecent) {
                    $v = if ($tagMostRecent -match '^v') { 'v' } else { '' }
                    $tagMostRecentV = [version]($tagMostRecent -replace '^v', '')
                    if ($major) {
                        "$v$( $tagMostRecentV.Major + 1).0.0" # E.g. v0.0.1 -> v1.0.0
                    }elseif ($minor) {
                        "$v$( $tagMostRecentV.Major).$( $tagMostRecentV.Minor + 1).0" # E.g. v0.0.1 -> v0.1.0
                    }elseif ($patch) {
                        "$v$( $tagMostRecentV.Major).$( $tagMostRecentV.Minor ).$( $tagMostRecentV.Build + 1 )" # E.g. v0.0.1 -> v0.0.2
                    }else {
                        # Should not arrive here
                        throw "Couldn't determine Semver tag, because the commit messages do not follow Conventional Commits."
                    }
                }else {
                    $v = 'v'
                    if ($major) {
                        "$( $v )1.0.0"
                    }elseif ($minor) {
                        "$( $v )0.1.0"
                    }elseif ($patch) {
                        "$( $v )0.0.1"
                    }else {
                        # Should not arrive here
                        throw "Couldn't determine Semver tag"
                    }
                }
            }
        }
    }catch {
        if ($ErrorActionPreference -eq 'Stop') {
            throw
        }
        if ($ErrorActionPreference -eq 'Continue') {
            $_ | Write-Error -ErrorAction Continue
        }
    }
}