Skip to content

Instantly share code, notes, and snippets.

@mandel-macaque
Created December 10, 2020 18:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mandel-macaque/5586b8dc0e041e305d33617388a7a272 to your computer and use it in GitHub Desktop.
Save mandel-macaque/5586b8dc0e041e305d33617388a7a272 to your computer and use it in GitHub Desktop.
Test
<#
.SYNOPSIS
Returns the target url to be used when setting the status. The target url allows users to get back to the CI event that updated the status.
#>
function Get-TargetUrl {
$targetUrl = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI + "$Env:SYSTEM_TEAMPROJECT/_build/index?buildId=$Env:BUILD_BUILDID&view=ms.vss-test-web.test-result-details"
return $targetUrl
}
<#
.SYNOPSIS
Returns the url to the Html Report index page stored in xamarin-storage.
#>
function Get-XamarinStorageIndexUrl {
param (
[Parameter(Mandatory)]
[String]
$Path
)
return "http://xamarin-storage/$Path/jenkins-results/tests/index.html"
}
<#
.SYNOPSIS
Sets a new status in github for the current build.
.DESCRIPTION
.PARAMETER Status
The status value to be set in GitHub. The available values are:
* error
* failure
* pending
* success
If the wrong value is passed a validation error with be thrown.
.PARAMETER Description
The description that will be added with the status update. This allows us to add a human readable string
to understand why the status was updated.
.PARAMETER Context
The context to be used. A status can contain several contexts. The context must be passed to associate
the status with a specific event.
.EXAMPLE
Set-GitHubStatus -Status "error" -Description "Not enough free space in the host." -Context "VSTS iOS device tests."
.NOTES
This cmdlet depends on the following environment variables. If one or more of the variables is missing an
InvalidOperationException will be thrown:
* SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url.
* SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url.
* BUILD_BUILDID: The current build id. Needed to be able to calculate the target url.
* BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change.
* GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API.
#>
function Set-GitHubStatus {
param
(
[Parameter(Mandatory)]
[String]
[ValidateScript({
$("error", "failure", "pending", "success").Contains($_) #validate that the status is in the range of valid values
})]
$Status,
[Parameter(Mandatory)]
[String]
$Description,
[Parameter(Mandatory)]
[String]
$Context,
[String]
$TargetUrl
)
# assert that all the env vars that are needed are present, else we do have an error
$envVars = @{
"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI;
"SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT;
"BUILD_BUILDID" = $Env:BUILD_BUILDID;
"BUILD_REVISION" = $Env:BUILD_REVISION;
"GITHUB_TOKEN" = $Env:GITHUB_TOKEN;
}
foreach ($key in $envVars.Keys) {
if (-not($envVars[$key])) {
Write-Debug "Enviroment varible missing $key"
throw [System.InvalidOperationException]::new("Environment variable missing: $key")
}
}
# use the GitHub API to set the status for the given commit
$detailsUrl = ""
if ($TargetUrl) {
$detailsUrl = $TargetUrl
} else {
$detailsUrl = Get-TargetUrl
}
$payload= @{
state = $Status
target_url = $detailsUrl
description = $Description
context = $Context
}
$url = "https://api.github.com/repos/xamarin/xamarin-macios/statuses/$Env:BUILD_REVISION"
$headers = @{
Authorization = ("token {0}" -f $Env:GITHUB_TOKEN)
}
return Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-json) -ContentType 'application/json'
}
<#
.SYNOPSIS
Add a new comment for the commit on GitHub.
.PARAMETER Header
The header to be used in the comment.
.PARAMETER Description
A show description to be added in the comment, this will show as a short version of the comment on GitHub.
.PARAMETER Message
A longer string that contains the full comment message. Will be shown when the comment is expanded.
.PARAMETER Emoji
Optional string representing and emoji to be used in the comments header.
.EXAMPLE
New-GitHubComment -Header "Tests failed catastrophically" -Emoji ":fire:" -Description "Not enough free space in the host."
.NOTES
This cmdlet depends on the following environment variables. If one or more of the variables is missing an
InvalidOperationException will be thrown:
* SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url.
* SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url.
* BUILD_BUILDID: The current build id. Needed to be able to calculate the target url.
* BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change.
* GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API.
#>
function New-GitHubComment {
param
(
[Parameter(Mandatory)]
[String]
$Header,
[Parameter(Mandatory)]
[String]
$Description,
[String]
$Message,
[String]
$Emoji #optionally use an emoji
)
# assert that all the env vars that are needed are present, else we do have an error
$envVars = @{
"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI;
"SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT;
"BUILD_DEFINITIONNAME" = $Env:BUILD_DEFINITIONNAME;
"BUILD_REVISION" = $Env:BUILD_REVISION;
"GITHUB_TOKEN" = $Env:GITHUB_TOKEN;
}
foreach ($key in $envVars.Keys) {
if (-not($envVars[$key])) {
Write-Debug "Enviroment varible missing $key"
throw [System.InvalidOperationException]::new("Environment variable missing: $key")
}
}
$targetUrl = Get-TargetUrl
# build the message, which will be sent to github, users can use markdown
$msg = [System.Text.StringBuilder]::new()
$msg.AppendLine("### $Emoji $Header $Emoji")
$msg.AppendLine()
$msg.AppendLine($Description)
if ($Message) { # only if message is not null or empty
$msg.AppendLine()
$msg.AppendLine($Message)
}
$msg.AppendLine()
$msg.AppendLine("[Pipeline]($targetUrl) on Agent $Env:TESTS_BOT") # Env:TESTS_BOT is added by the pipeline as a variable coming from the execute tests job
$url = "https://api.github.com/repos/xamarin/xamarin-macios/commits/$Env:BUILD_REVISION/comments"
$payload = @{
body = $msg.ToString()
}
$headers = @{
Authorization = ("token {0}" -f $Env:GITHUB_TOKEN)
}
$request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-Json) -ContentType 'application/json'
Write-Host $request
return $request
}
<#
.SYNOPSIS
Add a new comment that contains the result summaries of the test run.
.PARAMETER Header
The header to be used in the comment.
.PARAMETER Description
A show description to be added in the comment, this will show as a short version of the comment on GitHub.
.PARAMETER Message
A longer string that contains the full comment message. Will be shown when the comment is expanded.
.PARAMETER Emoji
Optional string representing and emoji to be used in the comments header.
.EXAMPLE
New-GitHubComment -Header "Tests failed catastrophically" -Emoji ":fire:" -Description "Not enough free space in the host."
.NOTES
This cmdlet depends on the following environment variables. If one or more of the variables is missing an
InvalidOperationException will be thrown:
* SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url.
* SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url.
* BUILD_BUILDID: The current build id. Needed to be able to calculate the target url.
* BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change.
* GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API.
#>
function New-GitHubCommentFromFile {
param (
[Parameter(Mandatory)]
[String]
$Header,
[Parameter(Mandatory)]
[String]
$Description,
[Parameter(Mandatory)]
[String]
[ValidateScript({
Test-Path -Path $_ -PathType Leaf
})]
$Path,
[String]
$Emoji #optionally use an emoji
)
# read the file, create a message and use the New-GithubComment function
$msg = [System.Text.StringBuilder]::new()
foreach ($line in Get-Content -Path $Path)
{
$msg.AppendLine($line)
}
return New-GithubComment -Header $Header -Description $Description -Message $msg.ToString() -Emoji $Emoji
}
<#
.SYNOPSIS
Test if the current job is successful or not.
#>
function Test-JobSuccess {
param (
[Parameter(Mandatory)]
[String]
$Status
)
# return if the status is one of the failure ones
return $Status -eq "Succeeded"
}
<#
.SYNOPSIS
Add a new comment that contains the summaries to the Html Report as well as set the status accordingly.
.PARAMETER Context
The context to be used to link the status and the device test run in the GitHub status API.
.PARAMETER XamarinStoragePath
The xamarin-storage path to which the html report was uploaded.
.PARAMETER TestSummaryPath
The path to the generated test summary.
.EXAMPLE
New-GitHubSummaryComment -Context "$Env:CONTEXT" -XamarinStoragePath "$Env:XAMARIN_STORAGE_PATH" -TestSummaryPath "$Env:SYSTEM_DEFAULTWORKINGDIRECTORY/xamarin/xamarin-macios/tests/TestSummary.md"
.NOTES
This cmdlet depends on the following environment variables. If one or more of the variables is missing an
InvalidOperationException will be thrown:
* SYSTEM_TEAMFOUNDATIONCOLLECTIONURI: The uri of the vsts collection. Needed to be able to calculate the target url.
* SYSTEM_TEAMPROJECT: The team project executing the build. Needed to be able to calculate the target url.
* BUILD_BUILDID: The current build id. Needed to be able to calculate the target url.
* BUILD_REVISION: The revision of the current build. Needed to know the commit whose status to change.
* GITHUB_TOKEN: OAuth or PAT token to interact with the GitHub API.
#>
function New-GitHubSummaryComment {
param (
[Parameter(Mandatory)]
[String]
$Context,
[Parameter(Mandatory)]
[String]
$TestSummaryPath,
[String]
[AllowEmptyString()]
$XamarinStoragePath
)
$envVars = @{
"SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI;
"SYSTEM_TEAMPROJECT" = $Env:SYSTEM_TEAMPROJECT;
"BUILD_DEFINITIONNAME" = $Env:BUILD_DEFINITIONNAME;
"BUILD_REVISION" = $Env:BUILD_REVISION;
"GITHUB_TOKEN" = $Env:GITHUB_TOKEN;
}
foreach ($key in $envVars.Keys) {
if (-not($envVars[$key])) {
Write-Debug "Environment variable missing: $key"
throw [System.InvalidOperationException]::new("Environment variable missing: $key")
}
}
$vstsTargetUrl = Get-TargetUrl
# build the links to provide extra info to the monitoring person, we need to make sure of a few things
# 1. We do have the xamarin-storage path
# 2. We did reach the xamarin-storage, stored in the env var XAMARIN_STORAGE_REACHED
$headerSb = [System.Text.StringBuilder]::new()
$headerSb.AppendLine(); # new line to start the list
$headerSb.AppendLine("* [Azure DevOps]($vstsTargetUrl)")
if ([string]::IsNullOrEmpty($XamarinStoragePath)) { # if we do have the storage path but we failed. first part of the -and check string is not null or empty, second check presence of the env var
$headerSb.AppendLine("* :warning: xamarin-storage could not be reached :warning:")
} else {
$xamarinStorageUrl = Get-XamarinStorageIndexUrl -Path $XamarinStoragePath
$headerSb.AppendLine("* [Html Report]($xamarinStorageUrl)")
}
if ($Env:VSDROPS_INDEX) {
# we did generate an index with the files in vsdrops
$headerSb.AppendLine("* [Html Report (VSDrops)]($Env:VSDROPS_INDEX)")
}
$headerLinks = $headerSb.ToString()
$request = $null
if (-not (Test-Path $TestSummaryPath -PathType Leaf)) {
Write-Host "No test summary found"
Set-GitHubStatus -Status "failure" -Description "Tests failed catastrophically on $Context (no summary found)." -Context "$Context"
$request = New-GitHubComment -Header "Tests failed catastrophically on $Context (no summary found)." -Emoji ":fire:" -Description "Result file $TestSummaryPath not found. $headerLinks"
} else {
if (Test-JobSuccess -Status $Env:TESTS_JOBSTATUS) {
Set-GitHubStatus -Status "success" -Description "Device tests passed on $Context." -Context "$Context"
$request = New-GitHubCommentFromFile -Header "Device tests passed on $Context." -Description "Device tests passed on $Context. $headerLinks" -Emoji ":white_check_mark:" -Path $TestSummaryPath
} else {
Set-GitHubStatus -Status "failure" -Description "Device tests failed on $Context." -Context "$Context"
$request = New-GitHubCommentFromFile -Header "Device tests failed on $Context" -Description "Device tests failed on $Context. $headerLinks" -Emoji ":x:" -Path $TestSummaryPath
}
}
return $request
}
<#
.SYNOPSIS
Get the information of a PR in GitHub.
.PARAMETER ChangeId
The Id whose labels we want to retrieve.
#>
function Get-GitHubPRInfo {
param (
[Parameter(Mandatory)]
[String]
$ChangeId
)
$envVars = @{
"GITHUB_TOKEN" = $Env:GITHUB_TOKEN;
}
foreach ($key in $envVars.Keys) {
if (-not($envVars[$key])) {
Write-Debug "Environment variable missing: $key"
throw [System.InvalidOperationException]::new("Environment variable missing: $key")
}
}
$url = "https://api.github.com/repos/xamarin/xamarin-macios/pulls/$ChangeId"
$headers = @{
Authorization = ("token {0}" -f $Env:GITHUB_TOKEN)
}
$request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -ContentType 'application/json'
Write-Host $request
return $request
}
<#
.SYNOPSIS
Class used to represent a single file to be added to a gist.
#>
class GistFile
{
[ValidateNotNullOrEmpty ()]
[string]
$Name
[ValidateNotNullOrEmpty ()]
[string]
$Path
[ValidateNotNullOrEmpty ()]
[string]
$Type
GistFile ($Name, $Path, $Type) {
# validate that the path does exist
if (Test-Path -Path $Path -PathType Leaf) {
$this.Path = $Path
} else {
throw [System.InvalidOperationException]::new("Path could not be found: $Path")
}
$this.Name = $Name
$this.Type = $Type
}
[hashtable] ConvertToHashTable () {
# ugly workaround to get decent new lines
$file= [System.Text.StringBuilder]::new()
foreach ($line in Get-Content -Path $this.Path)
{
$file.AppendLine($line)
}
return @{
content = $file.ToString()
filename = $this.Name;
language = $this.Type;
}
}
}
<#
.SYNOPSIS
Creates a new gist that will contain the given collection of files and returns the url
#>
function New-GistWithFiles {
param (
[ValidateNotNullOrEmpty ()]
[string]
$Description,
[Parameter(Mandatory)]
[GistFile[]]
$Files,
[switch]
$IsPublic=$false # default to false, better save than sorry
)
$envVars = @{
"GITHUB_TOKEN" = $Env:GITHUB_TOKEN;
}
foreach ($key in $envVars.Keys) {
if (-not($envVars[$key])) {
Write-Debug "Environment variable missing: $key"
throw [System.InvalidOperationException]::new("Environment variable missing: $key")
}
}
# create the hashtable that will contain all the information of all types
$payload = @{
description = $Description;
public = $IsPublic ? $true : $false; # switchs are converted to {\"IsPresent\"=>true} in json :/
files = @{}; # each file is the name of the file + the hashtable of the data to be used
}
Write-Host $Files
foreach ($g in $Files) {
Write-Host "File name is " + $g.Name
# add the file using its name + the hashtable that is used by GitHub
$payload["files"].Add($g.Name, $g.ConvertToHashTable())
}
$url = "https://api.github.com/gists"
$headers = @{
Accept = "application/vnd.github.v3+json";
Authorization = ("token {0}" -f $Env:GITHUB_TOKEN);
}
Write-Host ($payload | ConvertTo-Json)
$request = Invoke-RestMethod -Uri $url -Headers $headers -Method "POST" -Body ($payload | ConvertTo-Json) -ContentType 'application/json'
Write-Host $request
return $request.html_url
}
# module exports, any other functions are private and should not be used outside the module.
Export-ModuleMember -Function Set-GitHubStatus
Export-ModuleMember -Function New-GitHubComment
Export-ModuleMember -Function New-GitHubCommentFromFile
Export-ModuleMember -Function New-GitHubSummaryComment
Export-ModuleMember -Function Test-JobSuccess
Export-ModuleMember -Function Get-GitHubPRInfo
Export-ModuleMember -Function New-GistWithFiles
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment