Skip to content

Commit

Permalink
Automatic release (#375)
Browse files Browse the repository at this point in the history
marvinpinto replacement
  • Loading branch information
rf-halfspace authored May 30, 2024
1 parent 0b076cc commit f18ba72
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 1 deletion.
172 changes: 172 additions & 0 deletions .github/actions/github-create-release/Create-GitHubRelease.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
<#
.SYNOPSIS
Github Action script creating automated releases
.DESCRIPTION
The functionality is executed as an action on a github runner and creates automated releases for pull requests.
#>

if ([string]::IsNullOrEmpty($env:GH_TOKEN)) {
throw "Error: GH_TOKEN environment variable is not set, see https://cli.github.com/manual/gh_auth_login for details"
}

if ([string]::IsNullOrEmpty($env:GH_CONTEXT)) {
throw "Error: GH_CONTEXT environment variable is not set. Functionality is depending on github actions context variables."
}

$GithubRepository = $env:GH_CONTEXT | ConvertFrom-Json | Select-Object -ExpandProperty repository
$PullRequstNumber = $env:GH_CONTEXT | ConvertFrom-Json | Select-Object -ExpandProperty event | Select-Object -ExpandProperty number

<#
.SYNOPSIS
Class representing a Github Release using github CLI (gh)
.DESCRIPTION
Simple class mapping the output from a gh release list json response to a typed object
#>
class GithubRelease {
[string]$name
[string]$tagName
[string]$publishedAt
[bool]$isPrerelease
[bool]$isLatest
[bool]$isDraft
[string]$notes
[string[]]$files
}

<#
.SYNOPSIS
Creates a github release
.DESCRIPTION
Creates a github release. Makes sure to delete any prior releases with similar tag.
#>
function Create-GitHubRelease {
param (
[Parameter(Mandatory)]
[string]$TagName,
[Parameter(Mandatory)]
[string]$Title,
[Parameter(Mandatory)]
[string[]]$Files,
[string]$PreRelease = "false",
[string]$Draft = "false"
)

# Input parsing
$isDraft = [bool]::Parse($Draft)
$isPrerelease = [bool]::Parse($PreRelease)

# Step 1: Get Previous Release
[GithubRelease]$release = Invoke-GithubReleaseList -TagName $TagName

# Step 2: Delete Previous Release
$release | Invoke-GithubReleaseDelete

# Step 3: Create new release
$newrelease = [GithubRelease]@{
name = $Title
tagName = $TagName
isPrerelease = $isPrerelease
isDraft = $isDraft
notes = Get-ChangeNotes
files = $Files
}

$newrelease | Invoke-GithubReleaseCreate
}

<#
.SYNOPSIS
Uses github CLI (gh) to retrieves a list of releases
.DESCRIPTION
Wrapping a "gh release list" call
#>
function Invoke-GithubReleaseList {
param (
[string]$TagName
)
gh release list -L 10000 -R $GithubRepository --json "name,tagName,publishedAt,isPrerelease,isLatest,isDraft" `
| ConvertFrom-Json
| Where-Object { $_.name -eq $TagName }
}

<#
.SYNOPSIS
Uses github CLI (gh) to delete a release
.DESCRIPTION
Wrapping a "gh release delete" call
#>
function Invoke-GithubReleaseDelete {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
[GithubRelease]$release
)

if ($null -eq $release) {
Write-Warning "No release to delete."
return $release
}

Write-Host "Deleting $($release.Name)"
gh release delete $release.Name -y --cleanup-tag -R $GithubRepository
}

<#
.SYNOPSIS
Uses github CLI (gh) to create a release
.DESCRIPTION
Wrapping a "gh release create" call
#>
function Invoke-GithubReleaseCreate {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
[GithubRelease]$release
)

if ($null -eq $release) {
Write-Warning "No release to create."
return $release
}

Write-Verbose "Creating release: $($release.tagName)"

$ArgNotes = if ($release.notes) { "-n `"$($release.notes)`"" } else { "--generate-notes" }
$ArgPreRelease = if ($release.isPrerelease) { "--prerelease" } else { "" }
$ArgDraft = if ($release.isDraft) { "--draft" } else { "" }

$cmd = "gh release create $($release.tagName) -t $($release.name) -R $GithubRepository ${ArgPreRelease} ${ArgDraft} ${ArgNotes} $($release.Files)"
Invoke-Expression $cmd
}

<#
.SYNOPSIS
Construct a change note
.DESCRIPTION
Creates changes notes for github release
#>
function Get-ChangeNotes {
$commits = Invoke-GithubPrCommitHistory
$notes = @("## Commits")
$commits | ForEach-Object { $notes += "- $($_.sha.Substring(0,8)): $($_.commit.message) ($($_.committer.login))" }

return $notes -join "`n"
}

<#
.SYNOPSIS
Uses github CLI (gh api) to retrieve commit history
.DESCRIPTION
Wrapping a "gh api /repos/{repo}/pulls/{pr_number}/commits" call
#>
function Invoke-GithubPrCommitHistory {
gh api -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" "/repos/$GithubRepository/pulls/$PullRequstNumber/commits" | ConvertFrom-Json
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
BeforeAll {
$env:GH_TOKEN = "test"
$env:GH_CONTEXT = @"
{
"sha": "7fc6c8ad63a35313621bade00ddd2e91f756e093",
"repository": "owner/reponame",
"event": {
"number": "1234"
}
}
"@
. $PSScriptRoot/Create-GitHubRelease.ps1
}

Describe "Create-GithubRelease" {

Context "Invoke-GithubReleaseList" {
It "Returns a release when release exist" -ForEach @(
@{ Releases = @("v1"); TagName = "v1"; Expected = "v1" },
@{ Releases = @("v1", "v2"); TagName = "v1"; Expected = "v1" },
@{ Releases = @("not_v1"); TagName = "v1"; Expected = $null }
) {
## Mock gh release list
Mock gh {
$Releases | ForEach-Object { [GithubRelease]@{ Name = $_ } } | ConvertTo-Json -AsArray
}
$previousRelease = Invoke-GithubReleaseList -TagName $TagName
$previousRelease.Name | Should -Be $Expected
}
}

Context "Invoke-GithubReleaseDelete" {
It "Deletes a piped release" -ForEach @(
@{ Release = @{ Name = "noop" }; Expected = 1 },
@{ Release = $null; Expected = 0 }
) {
Mock gh {}
Mock Invoke-GithubReleaseList {
[GithubRelease] $Release
}

Invoke-GithubReleaseList | Invoke-GithubReleaseDelete | Should -Invoke -CommandName gh -Exactly -Times $Expected
}
}

Context "Invoke-GithubReleaseCreate" {
It "Should have <expected> as parameter" -ForEach @(
@{ Release = @{ name = "xyz" }; Expected = "xyz" }
@{ Release = @{ name = "xyz" }; Expected = "-t" }
@{ Release = @{ tagName = "tagxyz" }; Expected = "tagxyz" }
@{ Release = @{ notes = "" }; Expected = "--generate-notes" }
@{ Release = @{ isPrerelease = $true }; Expected = "--prerelease" }
@{ Release = @{ isDraft = $true }; Expected = "--draft" }
@{ Release = @{ notes = "notes" }; Expected = "-n" }
@{ Release = @{ notes = "notes" }; Expected = "notes" }
) {
Mock gh {}
$rel = [GithubRelease]$Release
$rel | Invoke-GithubReleaseCreate
Should -Invoke -CommandName gh -Exactly 1 -ParameterFilter {
$args -contains $Expected
}
}
}
}

48 changes: 48 additions & 0 deletions .github/actions/github-create-release/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Automatic Releases
description: Automate the GitHub release process with assets, changelogs, pre-releases, and more
branding:
icon: package
color: blue
inputs:
repo_token:
description: GitHub secret token
required: true
automatic_release_tag:
description: Git tag (for automatic releases)
required: false
draft:
description: Should this release be marked as a draft?
required: false
default: false
prerelease:
description: Should this release be marked as a pre-release?
required: false
default: true
title:
description: Release title (for automatic releases)
required: false
files:
description: Assets to upload to the release
required: false
outputs:
automatic_releases_tag:
description: The release tag this action just processed
upload_url:
description: The URL for uploading additional assets to the release
runs:
using: composite
steps:
- name: Create automatic release
shell: pwsh
env:
GH_TOKEN: ${{ inputs.repo_token }}
GH_CONTEXT: ${{ toJSON(github) }}
run: |
. ${{ github.action_path }}/Create-GitHubRelease.ps1
Create-GitHubRelease `
-TagName "${{ inputs.automatic_release_tag }}" `
-Draft "${{ inputs.draft }}" `
-PreRelease "${{ inputs.prerelease }}" `
-title "${{ inputs.title }}" `
-files "${{ inputs.files }}"
2 changes: 1 addition & 1 deletion .github/workflows/create-release-tag.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
# BE AWARE --> Updating to a new MAJOR version will delete deprecated versions on a nightly schedule.
# See https://github.com/Energinet-DataHub/.github#release-procedure for details
major_version: 14
minor_version: 3
minor_version: 4
patch_version: 0
repository_path: Energinet-DataHub/.github
usage_patterns: \s*uses:\s*Energinet-DataHub/\.github(.*)@v?(?<version>\d+)
Expand Down

0 comments on commit f18ba72

Please sign in to comment.