Plugins/GitReleases.ps1

# Author: Kim Nordmo <kim.nordmo@gmail.com>
# Last Change: 29-Oct-2017.

<#
.SYNOPSIS
    Creates Github release for updated packages
#>

param(
    $Info,

    # Github API token to use when creating/checking releases and uploading artifacts
    [string]$ApiToken,

    # What kind of release should be created, either 1 release per date, or 1 release per package and version is supported.
    [ValidateSet('date', 'package')]
    [string]$releaseType,

    # The text that should be used in the header of the release.
    [string]$releaseHeader = $null,

    # The text that should be used in the description of the release.
    [string]$releaseDescription = $null,

    # The formatting to use when replacing <date> in release header/description and on date based releases.
    [string]$dateFormat = '{0:yyyy-MM-dd}',

    # Force creating a release when a package have been updated and not just been pushed.
    [switch]$Force,

    # The name of the branch, to create the release at
    [string]$Branch = 'master'
)

function GetOrCreateRelease() {
    param(
        [string]$tagName,
        [string]$releaseName,
        [string]$releaseDescription,
        [string]$repository,
        $headers)

    try {
        Write-Verbose "Checking for a release using the tag: $tagName..."
        $response = Invoke-RestMethod -UseBasicParsing -Uri "https://api.github.com/repos/$repository/releases/tags/$tagName" -Headers $headers | Where-Object tag_name -eq $tagName
        if ($response) {
            return $response
        }
    }
    catch {
    }

    $json = @{
        "tag_name"         = $tagName
        "target_commitish" = $Branch
        "name"             = $releaseName
        "body"             = $releaseDescription
        "draft"            = $false
        "prerelease"       = $false
    } | ConvertTo-Json -Compress

    Write-Host "Creating the new release $tagName..."
    return Invoke-RestMethod -UseBasicParsing -Method Post -Uri "https://api.github.com/repos/$repository/releases" -Body $json -Headers $headers
}

[array]$packages = if ($Force) { $Info.result.updated } else { $Info.result.pushed }

if ($packages.Length -eq 0) { Write-Host "No package updated, skipping"; return }

$packagesToRelease = New-Object 'System.Collections.Generic.List[hashtable]'

$packages | ForEach-Object {
    if ($_.Streams) {
        $_.Streams.Values | Where-Object { $_.Updated } | ForEach-Object {
            $packagesToRelease.Add(@{
                    Name          = $_.Name
                    NuspecVersion = $_.NuspecVersion
                    RemoteVersion = $_.RemoteVersion
                    NuFile        = Resolve-Path ("$($_.Path)/$($_.Name).$($_.RemoteVersion).nupkg")
                })
        }
    }
    else {
        $packagesToRelease.Add(@{
                Name          = $_.Name
                NuspecVersion = $_.NuspecVersion
                RemoteVersion = $_.RemoteVersion
                NuFile        = Resolve-Path ("$($_.Path)/$($_.Name).$($_.RemoteVersion).nupkg")
            })
    }
}

$origin = git config --get remote.origin.url

if (!($origin -match "github.com\/([^\/]+\/[^\/\.]+)")) {
    Write-Warning "Unable to parse the repository information, skipping..."
    return;
}
$repository = $Matches[1]

$headers = @{
    Authorization = "token $ApiToken"
}

if ($releaseType -eq 'date' -and !$releaseHeader) {
    $releaseHeader = 'Packages updated on <date>'
}
elseif (!$releaseHeader) {
    $releaseHeader = '<PackageName> <RemoteVersion>'
}

if ($releaseType -eq 'date' -and !$releaseDescription) {
    $releaseDescription = 'We had packages that were updated on <date>'
}
elseif (!$releaseDescription) {
    $releaseDescription = '<PackageName> was updated from version <NuspecVersion> to <RemoteVersion>'
}

$date = Get-Date -UFormat $dateFormat

if ($releaseType -eq 'date') {
    $release = GetOrCreateRelease `
        -tagName $date `
        -releaseName ($releaseHeader -replace '<date>', $date) `
        -releaseDescription ($releaseDescription -replace '<date>', $date) `
        -repository $repository `
        -headers $headers

    if (!$release) {
        Write-Error "Unable to create a new release, please check your permissions..."
        return
    }
}

$uploadHeaders = $headers.Clone()
$uploadHeaders['Content-Type'] = 'application/zip'

$packagesToRelease | ForEach-Object {
    # Because we grab all streams previously, we need to ignore
    # cases when a stream haven't been updated (no nupkg file created)
    if (!$_.NuFile) { return }

    if ($releaseType -eq 'package') {
        $releaseName = $releaseHeader -replace '<PackageName>', $_.Name -replace '<RemoteVersion>', $_.RemoteVersion -replace '<NuspecVersion>', $_.NuspecVersion -replace '<date>', $date
        $packageDesc = $releaseDescription -replace '<PackageName>', $_.Name -replace '<RemoteVersion>', $_.RemoteVersion -replace '<NuspecVersion>', $_.NuspecVersion -replace '<date>', $date

        $release = GetOrCreateRelease `
            -tagName "$($_.Name)-$($_.RemoteVersion)" `
            -releaseName $releaseName `
            -releaseDescription $packageDesc `
            -repository $repository `
            -headers $headers
    }

    $fileName = [System.IO.Path]::GetFileName($_.NuFile)

    $existing = $release.assets | Where-Object name -eq $fileName
    if ($existing) {
        Write-Verbose "Removing existing $fileName asset..."
        Invoke-RestMethod -UseBasicParsing -Uri $existing.url -method Delete -Headers $headers | Out-Null
    }

    $uploadUrl = $release.upload_url -replace '\{.*\}$', ''
    $rawContent = [System.IO.File]::ReadAllBytes($_.NuFile)
    Write-Host "Uploading $fileName asset..."
    Invoke-RestMethod -UseBasicParsing -Uri "${uploadUrl}?name=${fileName}&label=$($_.Name) v$($_.RemoteVersion)" -Body $rawContent -Headers $uploadHeaders -Method Post | Out-Null
}