Public/Publish-GithubRelease.ps1

function Publish-GithubRelease
{
    <#
    .SYNOPSIS
        Publish a new release on a Github repository
 
    .FUNCTIONALITY
        CI/CD
 
    .EXAMPLE
        Publish-GithubRelease -AccessToken $mySecretToken -TagName "v1.0"
 
        Create a new release for the tag "v1.0".
        The name of the repository is assumed to be the same as the BHProjectName.
 
    .EXAMPLE
        Publish-GithubRelease -AccessToken $mySecretToken -TagName "v0.1" -Name "Beta Version 0.1" -PreRelease
 
        Create a new pre-release for the tag "v0.1".
 
    .EXAMPLE
        Publish-GithubRelease -AccessToken $mySecretToken -TagName "v1.0" -Draft
 
        Create a draft for a release on tag "v1.0".
 
    .EXAMPLE
        $release = @{
            AccessToken = "00000000000000000000000"
            TagName = "v1.0"
            Name = "Version 1.0"
            ReleaseText = "First version of my cool thing"
            Draft = $true
            PreRelease = $false
            RepositoryName = "MyGithubRepository"
        }
        Publish-GithubRelease @release
 
        Create a new draft release by using splatting (more info at "about_splatting").
 
    .LINK
        https://developer.github.com/v3/repos/releases/
 
    .LINK
        https://blog.github.com/2013-05-16-personal-api-tokens/
    #>

    [CmdletBinding()]
    param(
        # Personal API Token for authentication
        #
        # This sha string must be generated by a user that has push access to
        # the repository.
        # More information can be found at:
        # https://blog.github.com/2013-05-16-personal-api-tokens/
        [Parameter( Mandatory )]
        [ValidateNotNullOrEmpty()]
        [String]
        $AccessToken,

        # Name of the Github user or organization hosting the repository
        [Parameter( Mandatory )]
        [ValidateNotNullOrEmpty()]
        [Alias('Owner')]
        [String]
        $RepositoryOwner,

        # Name of the Github repository
        #
        # Default: $env:BHProjectName
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [String]
        $RepositoryName = (Get-ProjectName),

        # Name of the tag
        [Parameter( Mandatory )]
        [ValidateNotNullOrEmpty()]
        [String]
        $TagName,

        # Specifies the commitish value that determines where the Git tag is
        # created from. Can be any branch or commit SHA.
        #
        # Unused if the Git tag already exists.
        # Default: the repository's default branch (usually master).
        [Alias("Commit")]
        [String]
        $TargetCommit,

        # Name of the release
        [String]
        $Name,

        # Text describing the contents of the tag.
        [Alias('Body')]
        [String]
        $ReleaseText,

        # Create a draft (unpublished) release
        [Switch]
        $Draft,

        # Identify the release as a prerelease
        [Switch]
        $PreRelease,

        # Path to the artifact to upload to the release
        [Parameter( ValueFromPipeline, ValueFromPipelineByPropertyName )]
        [ValidateScript(
            {
                if (-not (Test-Path $_ -PathType Leaf))
                {
                    $exception = ([System.ArgumentException]"File not found")
                    $errorId = 'ParameterValue.FileNotFound'
                    $errorCategory = 'ObjectNotFound'
                    $errorTarget = $_
                    $errorItem = New-Object -TypeName System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $errorTarget
                    $errorItem.ErrorDetails = "No file could be found with the provided path '$_'."
                    $PSCmdlet.ThrowTerminatingError($errorItem)
                }
                return $true
            }
        )]
        [Alias('File', 'FullName', 'Path')]
        [String[]]
        $Artifact
    )

    begin
    {
        $body = @{ "tag_name" = $TagName }
        if ($PSBoundParameters.ContainsKey("TargetCommit"))
        {
            $body["target_commitish"] = $TargetCommit
        }
        if ($PSBoundParameters.ContainsKey("Name"))
        {
            $body["name"] = $Name
        }
        if ($PSBoundParameters.ContainsKey("ReleaseText"))
        {
            $body["body"] = $ReleaseText
        }
        if ($PSBoundParameters.ContainsKey("Draft"))
        {
            $body["draft"] = $true
        }
        if ($PSBoundParameters.ContainsKey("PreRelease"))
        {
            $body["prerelease"] = $true
        }

        $releaseParams = @{
            Uri         = "https://api.github.com/repos/{0}/{1}/releases" -f $RepositoryOwner, $RepositoryName
            Method      = 'POST'
            Headers     = @{
                Authorization = 'Basic ' + [Convert]::ToBase64String(
                    [Text.Encoding]::ASCII.GetBytes($AccessToken + ":x-oauth-basic")
                )
            }
            ContentType = 'application/json'
            Body        = $body | ConvertTo-Json
            ErrorAction = "Stop"
        }
        try {
            Set-TlsLevel -Tls12
            $release = Invoke-RestMethod @releaseParams
            $release
        }
        catch {
            throw $_
        }
        finally {
            Set-TlsLevel -Revert
        }
    }

    process
    {
        if ($Artifact)
        {
            foreach ($file in (Get-Item $Artifact))
            {
                $body = [System.IO.File]::ReadAllBytes($file.FullName)
                $uri = $release.upload_url -replace "\{\?name,label\}", "?name=$($file.Name)"

                $assetParams = @{
                    Uri         = $uri
                    Method      = 'POST'
                    Headers     = @{
                        Authorization = 'Basic ' + [Convert]::ToBase64String(
                            [Text.Encoding]::ASCII.GetBytes($AccessToken + ":x-oauth-basic")
                        )
                    }
                    ContentType = "application/octet-stream"
                    Body        = $body
                }

                try {
                    Set-TlsLevel -Tls12
                    Invoke-RestMethod @assetParams
                }
                catch {
                    throw $_
                }
                finally {
                    Set-TlsLevel -Revert
                }
            }
        }
    }
}