Merge pull request #13 from actions/v-mazhuk/migrate-tools-ci-to-github-actions

Migrate tools CI to GitHub Actions
This commit is contained in:
Maxim Lobanov
2020-08-25 11:56:23 +03:00
committed by GitHub
7 changed files with 189 additions and 59 deletions

View File

@@ -1,29 +1,15 @@
steps:
- checkout: self
- task: PowerShell@2
displayName: 'Get source version'
inputs:
TargetType: inline
script: |
$url = "https://api.github.com/repos/$(REPOSITORY)/commits/$(BRANCH)"
$commit = Invoke-RestMethod -Uri $url -Method "GET"
Write-Output "##vso[task.setvariable variable=COMMIT_SHA]$($commit.sha)"
- task: PowerShell@2
displayName: 'Run builds'
inputs:
targetType: filePath
filePath: './azure-devops/run-ci-builds.ps1'
filePath: './github/run-ci-builds.ps1'
arguments: |
-TeamFoundationCollectionUri $(System.TeamFoundationCollectionUri) `
-AzureDevOpsProjectName $(System.TeamProject) `
-AzureDevOpsAccessToken $(System.AccessToken) `
-SourceBranch $(BRANCH) `
-DefinitionId $(DEFINITION_ID) `
-SourceVersion $(COMMIT_SHA) `
-ManifestLink $(MANIFEST_URL) `
-WaitForBuilds $(WAIT_FOR_BUILDS) `
-RepositoryFullName $(REPOSITORY_FULL_NAME) `
-AccessToken $(GITHUB_TOKEN) `
-WorkflowFileName $(WORKFLOW_FILE_NAME) `
-WorkflowDispatchRef $(DISPATCH_REF) `
-ToolVersions "$(ToolVersions)" `
-RetryIntervalSec $(INTERVAL_SEC) `
-RetryCount $(RETRY_COUNT)
-PublishReleases $(PUPLISH_RELEASES)

View File

@@ -3,6 +3,11 @@ param (
)
$targetPath = $env:AGENT_TOOLSDIRECTORY
if ([string]::IsNullOrEmpty($targetPath)) {
# GitHub Windows images don't have `AGENT_TOOLSDIRECTORY` variable
$targetPath = $env:RUNNER_TOOL_CACHE
}
if ($ToolName) {
$targetPath = Join-Path $targetPath $ToolName
}

View File

@@ -2,10 +2,8 @@
.SYNOPSIS
Create commit with all unstaged changes in repository and create pull-request
.PARAMETER RepositoryOwner
Required parameter. The organization which tool repository belongs
.PARAMETER RepositoryName
Optional parameter. The name of tool repository
.PARAMETER RepositoryFullName
Required parameter. The owner and repository name. For example, 'actions/versions-package-tools'
.PARAMETER AccessToken
Required parameter. PAT Token to authorize
.PARAMETER BranchName
@@ -18,8 +16,7 @@ Required parameter. The title of pull-request
Required parameter. The description of pull-request
#>
param (
[Parameter(Mandatory)] [string] $RepositoryOwner,
[Parameter(Mandatory)] [string] $RepositoryName,
[Parameter(Mandatory)] [string] $RepositoryFullName,
[Parameter(Mandatory)] [string] $AccessToken,
[Parameter(Mandatory)] [string] $BranchName,
[Parameter(Mandatory)] [string] $CommitMessage,
@@ -46,11 +43,11 @@ function Update-PullRequest {
$updatedPullRequest = $GitHubApi.UpdatePullRequest($Title, $Body, $BranchName, $PullRequest.number)
if (($updatedPullRequest -eq $null) -or ($updatedPullRequest.html_url -eq $null)) {
Write-Host "##vso[task.logissue type=error;] Unexpected error occurs while updating pull request."
if (($null -eq $updatedPullRequest) -or ($null -eq $updatedPullRequest.html_url)) {
Write-Host "Unexpected error occurs while updating pull request."
exit 1
}
Write-host "##[section] Pull request updated: $($updatedPullRequest.html_url)"
Write-host "Pull request updated: $($updatedPullRequest.html_url)"
}
function Create-PullRequest {
@@ -67,12 +64,12 @@ function Create-PullRequest {
$createdPullRequest = $GitHubApi.CreateNewPullRequest($Title, $Body, $BranchName)
if (($createdPullRequest -eq $null) -or ($createdPullRequest.html_url -eq $null)) {
Write-Host "##vso[task.logissue type=error;] Unexpected error occurs while creating pull request."
if (($null -eq $createdPullRequest) -or ($null -eq $createdPullRequest.html_url)) {
Write-Host "Unexpected error occurs while creating pull request."
exit 1
}
Write-host "##[section] Pull request created: $($createdPullRequest.html_url)"
Write-host "Pull request created: $($createdPullRequest.html_url)"
}
Write-Host "Configure local git preferences"
@@ -87,8 +84,8 @@ Git-CommitAllChanges -Message $CommitMessage
Write-Host "Push branch: $BranchName"
Git-PushBranch -Name $BranchName -Force $true
$gitHubApi = Get-GitHubApi -AccountName $RepositoryOwner -ProjectName $RepositoryName -AccessToken $AccessToken
$pullRequest = $gitHubApi.GetPullRequest($BranchName, $RepositoryOwner)
$gitHubApi = Get-GitHubApi -RepositoryFullName $RepositoryFullName -AccessToken $AccessToken
$pullRequest = $gitHubApi.GetPullRequest($BranchName)
if ($pullRequest.Count -gt 0) {
Write-Host "Update pull request"

View File

@@ -5,8 +5,8 @@ The module that contains a bunch of methods to interact with GitHub API V3
class GitHubApi
{
[string] $BaseUrl
[string] $RepoOwner
[object] $AuthHeader
[string] $RepositoryOwner
GitHubApi(
[string] $AccountName,
@@ -15,6 +15,7 @@ class GitHubApi
) {
$this.BaseUrl = $this.BuildBaseUrl($AccountName, $ProjectName)
$this.AuthHeader = $this.BuildAuth($AccessToken)
$this.RepositoryOwner = $AccountName
}
[object] hidden BuildAuth([string]$AccessToken) {
@@ -43,9 +44,9 @@ class GitHubApi
return $this.InvokeRestMethod($url, 'Post', $null, $requestBody)
}
[object] GetPullRequest([string]$BranchName, [string]$RepositoryOwner){
[object] GetPullRequest([string]$BranchName){
$url = "pulls"
return $this.InvokeRestMethod($url, 'GET', "head=${RepositoryOwner}:$BranchName&base=main", $null)
return $this.InvokeRestMethod($url, 'GET', "head=$($this.RepositoryOwner):${BranchName}&base=main", $null)
}
[object] UpdatePullRequest([string]$Title, [string]$Body, [string]$BranchName, [string]$PullRequestNumber){
@@ -82,6 +83,35 @@ class GitHubApi
return $releases
}
[void] DispatchWorkflow([string]$EventType) {
$url = "dispatches"
$body = @{
event_type = $EventType
} | ConvertTo-Json
$this.InvokeRestMethod($url, 'POST', $null, $body)
}
[object] GetWorkflowRuns([string]$WorkflowFileName) {
$url = "actions/workflows/$WorkflowFileName/runs"
return $this.InvokeRestMethod($url, 'GET', $null, $null)
}
[object] GetWorkflowRunJobs([string]$WorkflowRunId) {
$url = "actions/runs/$WorkflowRunId/jobs"
return $this.InvokeRestMethod($url, 'GET', $null, $null)
}
[void] CreateWorkflowDispatch([string]$WorkflowFileName, [string]$Ref, [object]$Inputs) {
$url = "actions/workflows/${WorkflowFileName}/dispatches"
$body = @{
ref = $Ref
inputs = $Inputs
} | ConvertTo-Json
$this.InvokeRestMethod($url, 'POST', $null, $body)
}
[string] hidden BuildUrl([string]$Url, [string]$RequestParams) {
if ([string]::IsNullOrEmpty($RequestParams)) {
return "$($this.BaseUrl)/$($Url)"
@@ -117,10 +147,18 @@ class GitHubApi
function Get-GitHubApi {
param (
[string] $AccountName,
[string] $ProjectName,
[Parameter(ParameterSetName = 'RepositorySingle')]
[string] $RepositoryFullName,
[Parameter(ParameterSetName = 'RepositorySplitted')]
[string] $RepositoryOwner,
[Parameter(ParameterSetName = 'RepositorySplitted')]
[string] $RepositoryName,
[string] $AccessToken
)
return [GitHubApi]::New($AccountName, $ProjectName, $AccessToken)
if ($PSCmdlet.ParameterSetName -eq "RepositorySingle") {
$RepositoryOwner, $RepositoryName = $RepositoryFullName.Split('/', 2)
}
return [GitHubApi]::New($RepositoryOwner, $RepositoryName, $AccessToken)
}

91
github/run-ci-builds.ps1 Normal file
View File

@@ -0,0 +1,91 @@
<#
.SYNOPSIS
Trigger runs on the workflow_dispatch event to build and upload tool packages
.PARAMETER RepositoryFullName
Required parameter. The owner and repository name. For example, 'actions/versions-package-tools'
.PARAMETER AccessToken
Required parameter. PAT Token to authorize
.PARAMETER WorkflowFileName
Required parameter. The name of workflow file that will be triggered
.PARAMETER WorkflowDispatchRef
Required parameter. The reference of the workflow run. The reference can be a branch, tag, or a commit SHA.
.PARAMETER ToolVersions
Required parameter. List of tool versions to build and upload
.PARAMETER PublishReleases
Required parameter. Whether to publish releases, true or false
#>
param (
[Parameter(Mandatory)] [string] $RepositoryFullName,
[Parameter(Mandatory)] [string] $AccessToken,
[Parameter(Mandatory)] [string] $WorkflowFileName,
[Parameter(Mandatory)] [string] $WorkflowDispatchRef,
[Parameter(Mandatory)] [string] $ToolVersions,
[Parameter(Mandatory)] [string] $PublishReleases
)
Import-Module (Join-Path $PSScriptRoot "github-api.psm1")
function Get-WorkflowRunLink {
param(
[Parameter(Mandatory)] [object] $GitHubApi,
[Parameter(Mandatory)] [string] $WorkflowFileName,
[Parameter(Mandatory)] [string] $ToolVersion
)
$listWorkflowRuns = $GitHubApi.GetWorkflowRuns($WorkflowFileName).workflow_runs | Sort-Object -Property 'run_number' -Descending
foreach ($workflowRun in $listWorkflowRuns) {
$workflowRunJob = $gitHubApi.GetWorkflowRunJobs($workflowRun.id).jobs | Select-Object -First 1
if ($workflowRunJob.name -match $ToolVersion) {
return $workflowRun.html_url
}
}
return $null
}
function Queue-Builds {
param (
[Parameter(Mandatory)] [object] $GitHubApi,
[Parameter(Mandatory)] [string] $ToolVersions,
[Parameter(Mandatory)] [string] $WorkflowFileName,
[Parameter(Mandatory)] [string] $WorkflowDispatchRef,
[Parameter(Mandatory)] [string] $PublishReleases
)
$inputs = @{
PUBLISH_RELEASES = $PublishReleases
}
$ToolVersions.Split(',') | ForEach-Object {
$version = $_.Trim()
$inputs.VERSION = $version
Write-Host "Queue build for $version..."
$GitHubApi.CreateWorkflowDispatch($WorkflowFileName, $WorkflowDispatchRef, $inputs)
Start-Sleep -s 10
$workflowRunLink = Get-WorkflowRunLink -GitHubApi $GitHubApi `
-WorkflowFileName $WorkflowFileName `
-ToolVersion $version
if (-not $workflowRunLink) {
Write-Host "Could not find build for $version..."
exit 1
}
Write-Host "Link to the build: $workflowRunLink"
}
}
$gitHubApi = Get-GitHubApi -RepositoryFullName $RepositoryFullName -AccessToken $AccessToken
Write-Host "Versions to build: $ToolVersions"
Queue-Builds -GitHubApi $gitHubApi `
-ToolVersions $ToolVersions `
-WorkflowFileName $WorkflowFileName `
-WorkflowDispatchRef $WorkflowDispatchRef `
-PublishReleases $PublishReleases

View File

@@ -1,13 +1,10 @@
<#
.SYNOPSIS
Generate versions manifest based on repository releases
.DESCRIPTION
Versions manifest is needed to find the latest assets for particular version of tool
.PARAMETER GitHubRepositoryOwner
Required parameter. The organization which tool repository belongs
.PARAMETER GitHubRepositoryName
Required parameter. The name of tool repository
.PARAMETER RepositoryFullName
Required parameter. The owner and repository name. For example, 'actions/versions-package-tools'
.PARAMETER GitHubAccessToken
Required parameter. PAT Token to overcome GitHub API Rate limit
.PARAMETER OutputFile
@@ -17,8 +14,7 @@ Path to the json file with parsing configuration
#>
param (
[Parameter(Mandatory)] [string] $GitHubRepositoryOwner,
[Parameter(Mandatory)] [string] $GitHubRepositoryName,
[Parameter(Mandatory)] [string] $RepositoryFullName,
[Parameter(Mandatory)] [string] $GitHubAccessToken,
[Parameter(Mandatory)] [string] $OutputFile,
[Parameter(Mandatory)] [string] $ConfigurationFile
@@ -29,7 +25,7 @@ Import-Module (Join-Path $PSScriptRoot "manifest-utils.psm1") -Force
$configuration = Read-ConfigurationFile -Filepath $ConfigurationFile
$gitHubApi = Get-GitHubApi -AccountName $GitHubRepositoryOwner -ProjectName $GitHubRepositoryName -AccessToken $GitHubAccessToken
$gitHubApi = Get-GitHubApi -RepositoryFullName $RepositoryFullName -AccessToken $GitHubAccessToken
$releases = $gitHubApi.GetReleases()
$versionIndex = Build-VersionsManifest -Releases $releases -Configuration $configuration
$versionIndex | ConvertTo-Json -Depth 5 | Out-File $OutputFile -Encoding UTF8NoBOM -Force

View File

@@ -4,30 +4,47 @@ Pester extension that allows to run command and validate exit code
.EXAMPLE
"python file.py" | Should -ReturnZeroExitCode
#>
function Get-CommandResult {
param (
[Parameter(Mandatory=$true)]
[string] $Command,
[switch] $Multiline
)
# Bash trick to suppress and show error output because some commands write to stderr (for example, "python --version")
$stdout = & bash -c "$Command 2>&1"
$exitCode = $LASTEXITCODE
return @{
Output = If ($Multiline -eq $true) { $stdout } else { [string]$stdout }
ExitCode = $exitCode
}
}
function ShouldReturnZeroExitCode {
Param(
[Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()]
[String]$ActualValue,
[switch]$Negate
[String] $ActualValue,
[switch] $Negate,
[string] $Because # This parameter is unused by we need it to match Pester asserts signature
)
Write-Host "Run command '${ActualValue}'"
Invoke-Expression -Command $ActualValue | ForEach-Object { Write-Host $_ }
$actualExitCode = $LASTEXITCODE
$result = Get-CommandResult $ActualValue
[bool]$succeeded = $actualExitCode -eq 0
[bool]$succeeded = $result.ExitCode -eq 0
if ($Negate) { $succeeded = -not $succeeded }
if (-not $succeeded)
{
$failureMessage = "Command '${ActualValue}' has finished with exit code ${actualExitCode}"
$commandOutputIndent = " " * 4
$commandOutput = ($result.Output | ForEach-Object { "${commandOutputIndent}${_}" }) -join "`n"
$failureMessage = "Command '${ActualValue}' has finished with exit code ${actualExitCode}`n${commandOutput}"
}
return New-Object PSObject -Property @{
return [PSCustomObject] @{
Succeeded = $succeeded
FailureMessage = $failureMessage
}
}
Add-AssertionOperator -Name ReturnZeroExitCode `
-Test $function:ShouldReturnZeroExitCode
if (Get-Command -Name Add-AssertionOperator -ErrorAction SilentlyContinue) {
Add-AssertionOperator -Name ReturnZeroExitCode -InternalName ShouldReturnZeroExitCode -Test ${function:ShouldReturnZeroExitCode}
}