Implement builders to build Boost from source code

This commit is contained in:
Maxim Lobanov
2020-05-27 09:24:57 +03:00
parent 886c1d6104
commit 610555ac3d
36 changed files with 2005 additions and 0 deletions

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at opensource@github.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

52
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,52 @@
## Contributing
[fork]: https://github.com/actions/boost-versions/fork
[pr]: https://github.com/actions/boost-versions/compare
[code-of-conduct]: CODE_OF_CONDUCT.md
Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great.
Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [MIT](LICENSE.md).
Please note that this project is released with a [Contributor Code of Conduct][code-of-conduct]. By participating in this project you agree to abide by its terms.
## Submitting a pull request
1. [Fork][fork] and clone the repository
1. Create a new branch: `git checkout -b my-branch-name`
1. Make your changes
1. Push to your fork and [submit a pull request][pr]
1. Make sure that checks in your pull request are green
Here are a few things you can do that will increase the likelihood of your pull request being accepted:
- Please include a summary of the change and which issue is fixed. Also include relevant motivation and context.
- Follow the style guide for [PowerShell](https://github.com/PoshCode/PowerShellPracticeAndStyle).
- Write [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
## Code structure
### Directory structure
```
├── azure-pipelines/
| └──templates/
├── builders/
├── helpers/
├── installers/
└── tests/
└──sources/
```
- `azure-pipelines*` - contains global YAML definitions for build pipelines. Reusable templates for specific jobs are located in `templates` subfolder.
- `builders` - contains Boost builder classes and functions.
- `helpers` - contains global helper functions and functions.
- `installers` - contains installation script templates.
- `tests` - contains test scripts. Required tests sources are located in `sources` subfolder.
\* _We use Azure Pipelines because there are a few features that Actions is still missing, we'll move to Actions as soon as possible_.
## Resources
- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/)
- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/)
- [GitHub Help](https://help.github.com)

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1 +1,13 @@
# Boost for Actions
This repository contains the code and scripts that we use to prepare Boost packages used in [virtual-environments](https://github.com/actions/virtual-environments).
The file [versions-manifest.json](./versions-manifest.json) contains the list of available and released versions.
> Caution: this is prepared for and only permitted for use by actions `virtual-environments`.
**Status**: Currently under development and in use for beta and preview actions. This repo is undergoing rapid changes.
## Adding new versions
We are trying to prepare packages for new versions of Boost as soon as they are released. Please open an issue if any versions are missing.
## Contribution
Contributions are welcome! See [Contributor's Guide](./CONTRIBUTING.md) for more details about contribution process and code structure

3
SECURITY.md Normal file
View File

@@ -0,0 +1,3 @@
If you discover a security issue in this repo, please submit it through the [GitHub Security Bug Bounty](https://hackerone.com/github)
Thanks for helping make GitHub Actions safe for everyone.

View File

@@ -0,0 +1,95 @@
name: $(date:yyyyMMdd)$(rev:.r)-Boost-$(VERSION)
trigger: none
pr:
autoCancel: true
branches:
include:
- master
paths:
exclude:
- versions-manifest.json
stages:
- stage: Build_Boost_Ubuntu_1604
dependsOn: []
variables:
VmImage: ubuntu-16.04
Platform: linux-16.04
Architecture: x64
Toolset: gcc
jobs:
- template: /azure-pipelines/templates/build-job.yml
- stage: Test_Boost_Ubuntu_1604
condition: succeeded()
dependsOn: Build_Boost_Ubuntu_1604
variables:
VmImage: ubuntu-16.04
Platform: linux-16.04
Architecture: x64
Toolset: gcc
jobs:
- template: /azure-pipelines/templates/test-job.yml
- stage: Build_Boost_Ubuntu_1804
dependsOn: []
variables:
VmImage: 'ubuntu-18.04'
Platform: linux-18.04
Architecture: x64
Toolset: gcc
jobs:
- template: /azure-pipelines/templates/build-job.yml
- stage: Test_Boost_Ubuntu_1804
condition: succeeded()
dependsOn: Build_Boost_Ubuntu_1804
variables:
VmImage: 'ubuntu-18.04'
Platform: linux-18.04
Architecture: x64
Toolset: gcc
jobs:
- template: /azure-pipelines/templates/test-job.yml
- stage: Build_Boost_msvc141
dependsOn: []
variables:
VmImage: 'vs2017-win2016'
Platform: win32
Architecture: x86_64
Toolset: msvc-14.1
jobs:
- template: /azure-pipelines/templates/build-job.yml
- stage: Test_Boost_msvc141
condition: succeeded()
dependsOn: Build_Boost_msvc141
variables:
VmImage: 'vs2017-win2016'
Platform: win32
Architecture: x86_64
Toolset: msvc-14.1
jobs:
- template: /azure-pipelines/templates/test-job.yml
- stage: Build_Boost_msvc142
dependsOn: []
variables:
VmImage: 'windows-2019'
Platform: win32
Architecture: x86_64
Toolset: msvc-14.2
jobs:
- template: /azure-pipelines/templates/build-job.yml
- stage: Test_Boost_msvc142
condition: succeeded()
dependsOn: Build_Boost_msvc142
variables:
VmImage: 'windows-2019'
Platform: win32
Architecture: x86_64
Toolset: msvc-14.2
jobs:
- template: /azure-pipelines/templates/test-job.yml

View File

@@ -0,0 +1,20 @@
jobs:
- job: Build_Boost
timeoutInMinutes: 300
pool:
name: Azure Pipelines
vmImage: $(VmImage)
steps:
- checkout: self
- task: PowerShell@2
displayName: 'Build Boost $(Version)'
inputs:
targetType: filePath
filePath: './builders/build-boost.ps1'
arguments: '-Version $(Version) -Platform $(Platform) -Architecture $(Architecture) -Toolset $(Toolset)'
- task: PublishPipelineArtifact@1
displayName: 'Publish Artifact: Boost $(Version)'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
artifactName: 'boost-$(Version)-$(Platform)-$(Toolset)-$(Architecture)'

View File

@@ -0,0 +1,80 @@
jobs:
- job: Test_Boost
pool:
name: Azure Pipelines
vmImage: $(VmImage)
steps:
- checkout: self
submodules: true
- task: PowerShell@2
displayName: Fully cleanup the toolcache directory before testing
inputs:
targetType: filePath
filePath: helpers/clean-toolcache.ps1
arguments: -ToolName "boost"
- task: DownloadPipelineArtifact@2
inputs:
source: 'current'
artifact: 'boost-$(Version)-$(Platform)-$(Toolset)-$(Architecture)'
path: $(Build.ArtifactStagingDirectory)
- task: ExtractFiles@1
inputs:
archiveFilePatterns: '$(Build.ArtifactStagingDirectory)/boost-$(Version)-$(Platform)-*-$(Architecture).*'
destinationFolder: $(Build.BinariesDirectory)
cleanDestinationFolder: false
- task: PowerShell@2
displayName: 'Apply build artifact to the local machines'
inputs:
targetType: inline
script: |
if ("$(Platform)" -match 'win32') { powershell ./setup.ps1 } else { sh ./setup.sh }
workingDirectory: '$(Build.BinariesDirectory)'
- task: PowerShell@2
displayName: 'Set up BOOST_ROOT'
inputs:
TargetType: inline
script: |
$BoostToolcachePath = Join-Path -Path $env:AGENT_TOOLSDIRECTORY -ChildPath "boost"
$BoostToolcacheVersionPath = Join-Path -Path $BoostToolcachePath -ChildPath "$(Version)"
$boostDirectory = Join-Path $BoostToolcacheVersionPath "$(Architecture)"
$LD_LIBRARY = Join-Path -Path $boostDirectory -ChildPath "lib"
Write-Host "BOOST_ROOT = ${boostDirectory}"
Write-Host "##vso[task.setvariable variable=BOOST_ROOT]${boostDirectory}"
Write-Host "##vso[task.setvariable variable=LD_LIBRARY_PATH]${LD_LIBRARY}"
- task: PowerShell@2
displayName: 'Run tests'
inputs:
TargetType: inline
script: |
Install-Module Pester -Force -Scope CurrentUser
Import-Module Pester
$Platform = If ("$(Platform)" -eq "win32") { "Windows" } else { "Nix" }
$pesterParams = @(
@{
Path="./Common.Tests.ps1";
},
@{
Path="./${Platform}.Tests.ps1";
Parameters=@{
Version="$(Version)";
Platform="$(Platform)"
}
}
)
Invoke-Pester -Script $pesterParams -OutputFile "$(Build.SourcesDirectory)/tests/test_results.xml" -OutputFormat NUnitXml
workingDirectory: '$(Build.SourcesDirectory)/tests'
- task: PublishTestResults@2
displayName: 'Publish test results'
inputs:
testResultsFiles: '*.xml'
testResultsFormat: NUnit
searchFolder: 'tests'
failTaskOnFailedTests: true
testRunTitle: "Boost $(Version)-$(Platform)-$(Toolset)"
condition: always()

View File

@@ -0,0 +1,90 @@
class BoostBuilder {
<#
.SYNOPSIS
Base Boost builder class.
.DESCRIPTION
Base Boost builder class that contains general builder methods.
.PARAMETER Version
The version of Boost that should be built.
.PARAMETER Platform
The platform of Boost that should be built.
.PARAMETER Architecture
The architecture with which Boost should be built.
.PARAMETER TempFolderLocation
The location of temporary files that will be used during Boost package generation. Using system BUILD_STAGINGDIRECTORY variable value.
.PARAMETER ArtifactLocation
The location of generated Boost artifact. Using system environment BUILD_BINARIESDIRECTORY variable value.
.PARAMETER InstallationTemplatesLocation
The location of installation script template. Using "installers" folder from current repository.
#>
[version] $Version
[string] $Platform
[string] $Architecture
[string] $Toolset
[string] $TempFolderLocation
[string] $WorkFolderLocation
[string] $ArtifactFolderLocation
[string] $InstallationTemplatesLocation
BoostBuilder ([version] $version, [string] $platform, [string] $architecture, [string] $toolset) {
$this.Version = $version
$this.Platform = $platform
$this.Architecture = $architecture
$this.Toolset = $toolset
$this.TempFolderLocation = [IO.Path]::GetTempPath()
$this.WorkFolderLocation = $env:BUILD_BINARIESDIRECTORY
$this.ArtifactFolderLocation = $env:BUILD_STAGINGDIRECTORY
$this.InstallationTemplatesLocation = Join-Path -Path $PSScriptRoot -ChildPath "../installers"
}
[void] Download() {
<#
.SYNOPSIS
Download Boost source code.
#>
$gitArguments = @(
"clone"
"https://github.com/boostorg/boost.git",
$this.WorkFolderLocation,
"--branch", "boost-$($this.Version)",
"--single-branch",
"--recursive"
) -join " "
Execute-Command "git $gitArguments"
Write-Host "Removing .git subfolder to reduce artifact size..."
$gitFolder = Join-Path $this.WorkFolderLocation ".git"
Remove-Item $gitFolder -Recurse -Force
}
[void] Build() {
<#
.SYNOPSIS
Generates Boost artifact from downloaded binaries.
#>
Write-Host "Download Boost $($this.Version) source code..."
$this.Download()
Write-Host "Build source code..."
$this.Make()
Write-Host "Create installation script..."
$this.CreateInstallationScript()
Write-Host "Archive artifact..."
$this.ArchiveArtifact()
}
}

75
builders/build-boost.ps1 Normal file
View File

@@ -0,0 +1,75 @@
using module "./builders/win-boost-builder.psm1"
using module "./builders/nix-boost-builder.psm1"
<#
.SYNOPSIS
Generate Boost artifact.
.DESCRIPTION
Main script that creates instance of BoostBuilder and builds of Boost using specified parameters.
.PARAMETER Version
Required parameter. The version with which Boost will be built.
.PARAMETER Architecture
Optional parameter. The architecture with which Boost will be built. Using x64 by default.
.PARAMETER Platform
Required parameter. The platform for which Boost will be built.
#>
param(
[Parameter (Mandatory=$true)][Version] $Version,
[Parameter (Mandatory=$true)][string] $Platform,
[Parameter (Mandatory=$true)][string] $Architecture,
[Parameter (Mandatory=$true)][string] $Toolset
)
Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "common-helpers.psm1") -DisableNameChecking
Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "nix-helpers.psm1") -DisableNameChecking
Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "win-helpers.psm1") -DisableNameChecking
Import-Module (Join-Path $PSScriptRoot "../helpers" | Join-Path -ChildPath "win-vs-env.psm1") -DisableNameChecking
$ErrorActionPreference = "Stop"
function Get-BoostBuilder {
<#
.SYNOPSIS
Wrapper for class constructor to simplify importing BoostBuilder.
.DESCRIPTION
Create instance of BoostBuilder with specified parameters.
.PARAMETER Version
The version with which Boost will be built.
.PARAMETER Platform
The platform for which Boost will be built.
.PARAMETER Architecture
The architecture with which Boost will be built.
#>
param (
[version] $Version,
[string] $Architecture,
[string] $Platform
)
if ($Platform -match 'win32') {
$builder = [WinBoostBuilder]::New($Version, $Platform, $Architecture, $Toolset)
} elseif ($Platform -match 'linux') {
$builder = [NixBoostBuilder]::New($Version, $Platform, $Architecture, $Toolset)
} else {
Write-Host "##vso[task.logissue type=error;] Invalid platform: $Platform"
exit 1
}
return $builder
}
### Create Boost builder instance, and build artifact
$Builder = Get-BoostBuilder -Version $Version -Platform $Platform -Architecture $Architecture
$Builder.Build()

View File

@@ -0,0 +1,77 @@
using module "./builders/boost-builder.psm1"
class NixBoostBuilder : BoostBuilder {
<#
.SYNOPSIS
Ubuntu Boost builder class.
.DESCRIPTION
Contains methods that required to build Ubuntu Boost artifact from sources. Inherited from base BoostBuilder.
#>
[string] $InstallationTemplateName
[string] $InstallationScriptName
[string] $OutputArtifactName
NixBoostBuilder(
[version] $version,
[string] $platform,
[string] $architecture,
[string] $toolset
) : Base($version, $platform, $architecture, $toolset) {
$this.InstallationTemplateName = "nix-setup-template.sh"
$this.InstallationScriptName = "setup.sh"
$this.OutputArtifactName = "boost-$Version-$Platform-$Toolset-$Architecture.tar.gz"
}
[void] Make() {
Push-Location -Path $this.WorkFolderLocation
Write-Host "Invoke bootstrap.sh"
Execute-Command "sudo ./bootstrap.sh"
$commonArguments = @(
"install"
"--prefix=$($this.WorkFolderLocation)",
"--build-dir=$($this.TempFolderLocation)",
"--layout='tagged'",
"link='static,shared'",
"runtime-link='static,shared'"
) -join " "
Write-Host "Build boost static and shared binaries in release"
Execute-Command "sudo ./b2 $commonArguments variant='release'"
Write-Host "Build boost static and shared binaries in debug"
Execute-Command "sudo ./b2 $commonArguments variant='debug'"
Pop-Location
}
[void] CreateInstallationScript() {
<#
.SYNOPSIS
Create Boost artifact installation script based on template specified in InstallationTemplateName property.
#>
$installationScriptLocation = New-Item -Path $this.WorkFolderLocation -Name $this.InstallationScriptName -ItemType File
$installationTemplateLocation = Join-Path -Path $this.InstallationTemplatesLocation -ChildPath $this.InstallationTemplateName
$installationTemplateContent = Get-Content -Path $installationTemplateLocation -Raw
$variablesToReplace = @{
"{{__VERSION__}}" = $this.Version;
"{{__ARCHITECTURE__}}" = "x64";
}
$variablesToReplace.keys | ForEach-Object { $installationTemplateContent = $installationTemplateContent.Replace($_, $variablesToReplace[$_]) }
$installationTemplateContent | Out-File -FilePath $installationScriptLocation
Write-Debug "Done; Installation script location: $installationScriptLocation)"
}
[void] ArchiveArtifact() {
$OutputPath = Join-Path $this.ArtifactFolderLocation $this.OutputArtifactName
Create-TarArchive -SourceFolder $this.WorkFolderLocation -ArchivePath $OutputPath
}
}

View File

@@ -0,0 +1,105 @@
using module "./builders/boost-builder.psm1"
class WinBoostBuilder : BoostBuilder {
<#
.SYNOPSIS
Ubuntu Boost builder class.
.DESCRIPTION
Contains methods that required to build Ubuntu Boost artifact from sources. Inherited from base NixBoostBuilder.
.PARAMETER Toolset
The toolset which well be used to buil source code on windows vs.
#>
[string] $InstallationTemplateName
[string] $InstallationScriptName
[string] $OutputArtifactName
WinBoostBuilder(
[version] $version,
[string] $platform,
[string] $architecture,
[string] $toolset
) : Base($version, $platform, $architecture, $toolset) {
$this.InstallationTemplateName = "win-setup-template.ps1"
$this.InstallationScriptName = "setup.ps1"
$toolsetPart = $toolset.Replace("-", "")
$this.OutputArtifactName = "boost-$Version-$Platform-$toolsetPart-$Architecture.tar.gz"
}
[void] CreateIncludeSymlink() {
$includeFolder = "$($this.WorkFolderLocation)\include"
$headerDestination = "$($this.WorkFolderLocation)\include\boost"
if ((Test-Path $includeFolder) -and (-not(Test-Path $headerDestination))) {
Write-Host "Move headers to root"
$headersSource = Get-Childitem $includeFolder | Where-Object { $_.PsIsContainer } | Select-Object -First 1 -ExpandProperty FullName
Copy-Item -Path "${headersSource}\boost" -Destination $headerDestination -Recurse -Container
}
if (-not (Test-Path -Path "$($this.WorkFolderLocation)\bjam.exe")) {
Copy-Item -Path "$($this.WorkFolderLocation)\b2.exe" -Destination "$($this.WorkFolderLocation)\bjam.exe"
}
}
[void] Make() {
Write-Host "Initialize VS dev environment"
Invoke-VSDevEnvironment
Push-Location -Path $this.WorkFolderLocation
Write-Host "Invoke bootstrap.sh"
Execute-Command "./bootstrap.bat msvc"
Write-Host "Build boost with '$($this.Toolset)' toolset..."
$commonArguments = @(
"install",
"--prefix='$($this.WorkFolderLocation)'",
"--build-dir='$($this.TempFolderLocation)'",
"variant='debug,release'",
"link='static,shared'",
"runtime-link='static,shared'",
"address-model='32,64'",
"toolset='$($this.Toolset)'",
"-j4"
) -join " "
Execute-Command "./b2 $commonArguments" -ErrorAction Continue
$this.CreateIncludeSymlink()
Pop-Location
}
[void] CreateInstallationScript() {
<#
.SYNOPSIS
Create Boost artifact installation script based on specified template.
#>
$installationScriptLocation = New-Item -Path $this.WorkFolderLocation -Name $this.InstallationScriptName -ItemType File
$installationTemplateLocation = Join-Path -Path $this.InstallationTemplatesLocation -ChildPath $this.InstallationTemplateName
$installationTemplateContent = Get-Content -Path $installationTemplateLocation -Raw
$variablesToReplace = @{
"{{__VERSION__}}" = $this.Version;
"{{__ARCHITECTURE__}}" = $this.Architecture;
}
$variablesToReplace.keys | ForEach-Object { $installationTemplateContent = $installationTemplateContent.Replace($_, $variablesToReplace[$_]) }
$installationTemplateContent | Out-File -FilePath $installationScriptLocation
Write-Debug "Done; Installation script location: $installationScriptLocation)"
}
[void] ArchiveArtifact() {
$archiveTempDir = (New-Item -Name "tempArchive" -ItemType Directory -Path $this.TempFolderLocation).Fullname
$TempTarArchive = [IO.Path]::GetFileNameWithoutExtension($this.OutputArtifactName)
$OutPathTempTar = Join-Path -Path $archiveTempDir -ChildPath $TempTarArchive
$OutputPath = Join-Path -Path $this.ArtifactFolderLocation -ChildPath $this.OutputArtifactName
Write-Host "Pack to tar"
Create-SevenZipArchive -SourceFolder $this.WorkFolderLocation -ArchivePath $OutPathTempTar -ArchiveType "tar" -IncludeSymlinks
Write-Host "Pack to tar.gz"
Create-SevenZipArchive -SourceFolder $archiveTempDir -ArchivePath $OutputPath -ArchiveType "gzip"
}
}

View File

@@ -0,0 +1,9 @@
{
"regex": "boost-\\d+\\.\\d+\\.\\d+-(\\w+)-([\\d\\.]+)?-?([\\w\\.]+)-(x[\\d_]+)",
"groups": {
"arch": 4,
"platform": 1,
"platform_version": 2,
"toolset": 3
}
}

View File

@@ -0,0 +1,89 @@
class AzureDevOpsApi
{
[string] $BaseUrl
[string] $RepoOwner
[object] $AuthHeader
AzureDevOpsApi(
[string] $TeamFoundationCollectionUri,
[string] $ProjectName,
[string] $AccessToken
) {
$this.BaseUrl = $this.BuildBaseUrl($TeamFoundationCollectionUri, $ProjectName)
$this.AuthHeader = $this.BuildAuth($AccessToken)
}
[object] hidden BuildAuth([string]$AccessToken) {
if ([string]::IsNullOrEmpty($AccessToken)) {
return $null
}
return @{
Authorization = "Bearer $AccessToken"
}
}
[string] hidden BuildBaseUrl([string]$TeamFoundationCollectionUri, [string]$ProjectName) {
return "${TeamFoundationCollectionUri}/${ProjectName}/_apis"
}
[object] QueueBuild([string]$ToolVersion, [string]$SourceBranch, [string]$SourceVersion, [UInt32]$DefinitionId){
$url = "build/builds"
# The content of parameters field should be a json string
$buildParameters = @{ VERSION = $ToolVersion } | ConvertTo-Json
$body = @{
definition = @{
id = $DefinitionId
}
sourceBranch = $SourceBranch
sourceVersion = $SourceVersion
parameters = $buildParameters
} | ConvertTo-Json
return $this.InvokeRestMethod($url, 'POST', $body)
}
[object] GetBuildInfo([UInt32]$BuildId){
$url = "build/builds/$BuildId"
return $this.InvokeRestMethod($url, 'GET', $null)
}
[string] hidden BuildUrl([string]$Url) {
return "$($this.BaseUrl)/${Url}/?api-version=5.1"
}
[object] hidden InvokeRestMethod(
[string] $Url,
[string] $Method,
[string] $Body
) {
$requestUrl = $this.BuildUrl($Url)
$params = @{
Method = $Method
ContentType = "application/json"
Uri = $requestUrl
Headers = @{}
}
if ($this.AuthHeader) {
$params.Headers += $this.AuthHeader
}
if (![string]::IsNullOrEmpty($body)) {
$params.Body = $Body
}
return Invoke-RestMethod @params
}
}
function Get-AzureDevOpsApi {
param (
[string] $TeamFoundationCollectionUri,
[string] $ProjectName,
[string] $AccessToken
)
return [AzureDevOpsApi]::New($TeamFoundationCollectionUri, $ProjectName, $AccessToken)
}

View File

@@ -0,0 +1,44 @@
Import-Module (Join-Path $PSScriptRoot "azure-devops-api.ps1")
class BuildInfo
{
[AzureDevOpsApi] $AzureDevOpsApi
[String] $Name
[UInt32] $Id
[String] $Status
[String] $Result
[String] $Link
BuildInfo([AzureDevOpsApi] $AzureDevOpsApi, [object] $Build)
{
$this.AzureDevOpsApi = $AzureDevOpsApi
$this.Id = $Build.id
$this.Name = $Build.buildNumber
$this.Link = $Build._links.web.href
$this.Status = $Build.status
$this.Result = $Build.result
}
[boolean] IsFinished() {
return ($this.Status -eq "completed") -or ($this.Status -eq "cancelling")
}
[boolean] IsSuccess() {
return $this.Result -eq "succeeded"
}
[void] UpdateBuildInfo() {
$buildInfo = $this.AzureDevOpsApi.GetBuildInfo($this.Id)
$this.Status = $buildInfo.status
$this.Result = $buildInfo.result
}
}
function Get-BuildInfo {
param (
[AzureDevOpsApi] $AzureDevOpsApi,
[object] $Build
)
return [BuildInfo]::New($AzureDevOpsApi, $Build)
}

View File

@@ -0,0 +1,94 @@
param (
[Parameter(Mandatory)] [string] $TeamFoundationCollectionUri,
[Parameter(Mandatory)] [string] $AzureDevOpsProjectName,
[Parameter(Mandatory)] [string] $AzureDevOpsAccessToken,
[Parameter(Mandatory)] [string] $SourceBranch,
[Parameter(Mandatory)] [string] $ToolVersions,
[Parameter(Mandatory)] [UInt32] $DefinitionId,
[string] $SourceVersion
)
Import-Module (Join-Path $PSScriptRoot "azure-devops-api.ps1")
Import-Module (Join-Path $PSScriptRoot "build-info.ps1")
function Queue-Builds {
param (
[Parameter(Mandatory)] [AzureDevOpsApi] $AzureDevOpsApi,
[Parameter(Mandatory)] [string] $ToolVersions,
[Parameter(Mandatory)] [string] $SourceBranch,
[Parameter(Mandatory)] [string] $SourceVersion,
[Parameter(Mandatory)] [string] $DefinitionId
)
[BuildInfo[]]$queuedBuilds = @()
$ToolVersions.Split(',') | ForEach-Object {
$version = $_.Trim()
Write-Host "Queue build for $version..."
$queuedBuild = $AzureDevOpsApi.QueueBuild($version, $SourceBranch, $SourceVersion, $DefinitionId)
$buildInfo = Get-BuildInfo -AzureDevOpsApi $AzureDevOpsApi -Build $queuedBuild
Write-Host "Queued build: $($buildInfo.Link)"
$queuedBuilds += $buildInfo
}
return $queuedBuilds
}
function Wait-Builds {
param (
[Parameter(Mandatory)] [BuildInfo[]] $Builds
)
$timeoutBetweenRefreshSec = 30
do {
# If build is still running - refresh its status
foreach($build in $builds) {
if (!$build.IsFinished()) {
$build.UpdateBuildInfo()
if ($build.IsFinished()) {
Write-Host "The $($build.Name) build was completed: $($build.Link)"
}
}
}
$runningBuildsCount = ($builds | Where-Object { !$_.IsFinished() }).Length
Start-Sleep -Seconds $timeoutBetweenRefreshSec
} while($runningBuildsCount -gt 0)
}
function Make-BuildsOutput {
param (
[Parameter(Mandatory)] [BuildInfo[]] $Builds
)
Write-Host "Builds info:"
$builds | Format-Table -AutoSize -Property Name,Id,Status,Result,Link | Out-String -Width 10000
# Return exit code based on status of builds
$failedBuilds = ($builds | Where-Object { !$_.IsSuccess() })
if ($failedBuilds.Length -ne 0) {
Write-Host "##vso[task.logissue type=error;]Builds failed"
$failedBuilds | ForEach-Object -Process { Write-Host "##vso[task.logissue type=error;]Name: $($_.Name); Link: $($_.Link)" }
Write-Host "##vso[task.complete result=Failed]"
} else {
Write-host "##[section] All builds have been passed successfully"
}
}
$azureDevOpsApi = Get-AzureDevOpsApi -TeamFoundationCollectionUri $TeamFoundationCollectionUri `
-ProjectName $AzureDevOpsProjectName `
-AccessToken $AzureDevOpsAccessToken
$queuedBuilds = Queue-Builds -AzureDevOpsApi $azureDevOpsApi `
-ToolVersions $ToolVersions `
-SourceBranch $SourceBranch `
-SourceVersion $SourceVersion `
-DefinitionId $DefinitionId
Write-Host "Waiting results of builds ..."
Wait-Builds -Builds $queuedBuilds
Make-BuildsOutput -Builds $queuedBuilds

View File

@@ -0,0 +1,13 @@
param (
[string] $ToolName
)
$targetPath = $env:AGENT_TOOLSDIRECTORY
if ($ToolName) {
$targetPath = Join-Path $targetPath $ToolName
}
if (Test-Path $targetPath) {
Get-ChildItem -Path $targetPath -Recurse | Where-Object { $_.LinkType -eq "SymbolicLink" } | ForEach-Object { $_.Delete() }
Remove-Item -Path $targetPath -Recurse -Force
}

View File

@@ -0,0 +1,85 @@
<#
.SYNOPSIS
The execute command and print all output to the logs
#>
function Execute-Command {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string] $Command
)
Write-Debug "Execute $Command"
try {
Invoke-Expression $Command | ForEach-Object { Write-Host $_ }
if ($LASTEXITCODE -ne 0) { throw "Exit code: $LASTEXITCODE"}
}
catch {
$errorMessage = "Error happened during command execution: $Command `n $_"
Write-Host $errorMessage
if ($ErrorActionPreference -ne "Continue") {
# avoid logging Azure DevOps issues in case of $ErrorActionPreference -eq Continue
Write-Host "##vso[task.logissue type=error;] $errorMessage"
}
}
}
<#
.SYNOPSIS
Download file from url and return local path to file
#>
function Download-File {
param(
[Parameter(Mandatory=$true)]
[Uri]$Uri,
[Parameter(Mandatory=$true)]
[String]$OutputFolder
)
$targetFilename = [IO.Path]::GetFileName($Uri)
$targetFilepath = Join-Path $OutputFolder $targetFilename
Write-Debug "Download source from $Uri to $OutFile"
try {
(New-Object System.Net.WebClient).DownloadFile($Uri, $targetFilepath)
return $targetFilepath
} catch {
Write-Host "Error during downloading file from '$Uri'"
"$_"
exit 1
}
}
<#
.SYNOPSIS
Generate file that contains the list of all files in particular directory
#>
function New-ToolStructureDump {
param(
[Parameter(Mandatory=$true)]
[String]$ToolPath,
[Parameter(Mandatory=$true)]
[String]$OutputFolder
)
$outputFile = Join-Path $OutputFolder "tools_structure.txt"
$folderContent = Get-ChildItem -Path $ToolPath -Recurse | Sort-Object | Select-Object -Property FullName, Length
$folderContent | ForEach-Object {
$relativePath = $_.FullName.Replace($ToolPath, "");
return "${relativePath}"
} | Out-File -FilePath $outputFile
}
<#
.SYNOPSIS
Check if it is macOS / Ubuntu platform
#>
function IsNixPlatform {
param(
[Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()]
[String]$Platform
)
return ($Platform -match "macos") -or ($Platform -match "ubuntu") -or ($Platform -match "linux")
}

View File

@@ -0,0 +1,106 @@
<#
.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 AccessToken
Required parameter. PAT Token to authorize
.PARAMETER BranchName
Required parameter. The name of branch where changes will be pushed
.PARAMETER CommitMessage
Required parameter. The commit message to push changes
.PARAMETER PullRequestTitle
Required parameter. The title of pull-request
.PARAMETER PullRequestBody
Required parameter. The description of pull-request
#>
param (
[Parameter(Mandatory)] [string] $RepositoryOwner,
[Parameter(Mandatory)] [string] $RepositoryName,
[Parameter(Mandatory)] [string] $AccessToken,
[Parameter(Mandatory)] [string] $BranchName,
[Parameter(Mandatory)] [string] $CommitMessage,
[Parameter(Mandatory)] [string] $PullRequestTitle,
[Parameter(Mandatory)] [string] $PullRequestBody
)
Import-Module (Join-Path $PSScriptRoot "github-api.psm1")
Import-Module (Join-Path $PSScriptRoot "git.psm1")
function Update-PullRequest {
Param (
[Parameter(Mandatory=$true)]
[object] $GitHubApi,
[Parameter(Mandatory=$true)]
[string] $Title,
[Parameter(Mandatory=$true)]
[string] $Body,
[Parameter(Mandatory=$true)]
[string] $BranchName,
[Parameter(Mandatory=$true)]
[object] $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."
exit 1
}
Write-host "##[section] Pull request updated: $($updatedPullRequest.html_url)"
}
function Create-PullRequest {
Param (
[Parameter(Mandatory=$true)]
[object] $GitHubApi,
[Parameter(Mandatory=$true)]
[string] $Title,
[Parameter(Mandatory=$true)]
[string] $Body,
[Parameter(Mandatory=$true)]
[string] $BranchName
)
$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."
exit 1
}
Write-host "##[section] Pull request created: $($createdPullRequest.html_url)"
}
Write-Host "Configure local git preferences"
Git-ConfigureUser -Name "Service account" -Email "no-reply@microsoft.com"
Write-Host "Create branch: $BranchName"
Git-CreateBranch -Name $BranchName
Write-Host "Create commit"
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)
if ($pullRequest.Count -gt 0) {
Write-Host "Update pull request"
Update-PullRequest -GitHubApi $gitHubApi `
-Title $PullRequestTitle `
-Body $PullRequestBody `
-BranchName $BranchName `
-PullRequest $pullRequest[0]
} else {
Write-Host "Create pull request"
Create-PullRequest -GitHubApi $gitHubApi `
-Title $PullRequestTitle `
-Body $PullRequestBody `
-BranchName $BranchName
}

81
helpers/github/git.psm1 Normal file
View File

@@ -0,0 +1,81 @@
<#
.SYNOPSIS
Configure git credentials to use with commits
#>
function Git-ConfigureUser {
Param (
[Parameter(Mandatory=$true)]
[string] $Name,
[Parameter(Mandatory=$true)]
[string] $Email
)
git config --global user.name $Name | Out-Host
git config --global user.email $Email | Out-Host
if ($LASTEXITCODE -ne 0) {
Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while configuring git preferences."
exit 1
}
}
<#
.SYNOPSIS
Create new branch
#>
function Git-CreateBranch {
Param (
[Parameter(Mandatory=$true)]
[string] $Name
)
git checkout -b $Name | Out-Host
if ($LASTEXITCODE -ne 0) {
Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while creating new branch: $Name."
exit 1
}
}
<#
.SYNOPSIS
Commit all staged and unstaged changes
#>
function Git-CommitAllChanges {
Param (
[Parameter(Mandatory=$true)]
[string] $Message
)
git add -A | Out-Host
git commit -m "$Message" | Out-Host
if ($LASTEXITCODE -ne 0) {
Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while commiting changes."
exit 1
}
}
<#
.SYNOPSIS
Push branch to remote repository
#>
function Git-PushBranch {
Param (
[Parameter(Mandatory=$true)]
[string] $Name,
[Parameter(Mandatory=$true)]
[boolean] $Force
)
if ($Force) {
git push --set-upstream origin $Name --force | Out-Host
} else {
git push --set-upstream origin $Name | Out-Host
}
if ($LASTEXITCODE -ne 0) {
Write-Host "##vso[task.logissue type=error;] Unexpected failure occurs while pushing changes."
exit 1
}
}

View File

@@ -0,0 +1,126 @@
<#
.SYNOPSIS
The module that contains a bunch of methods to interact with GitHub API V3
#>
class GitHubApi
{
[string] $BaseUrl
[string] $RepoOwner
[object] $AuthHeader
GitHubApi(
[string] $AccountName,
[string] $ProjectName,
[string] $AccessToken
) {
$this.BaseUrl = $this.BuildBaseUrl($AccountName, $ProjectName)
$this.AuthHeader = $this.BuildAuth($AccessToken)
}
[object] hidden BuildAuth([string]$AccessToken) {
if ([string]::IsNullOrEmpty($AccessToken)) {
return $null
}
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("'':${AccessToken}"))
return @{
Authorization = "Basic ${base64AuthInfo}"
}
}
[string] hidden BuildBaseUrl([string]$RepositoryOwner, [string]$RepositoryName) {
return "https://api.github.com/repos/$RepositoryOwner/$RepositoryName"
}
[object] CreateNewPullRequest([string]$Title, [string]$Body, [string]$BranchName){
$requestBody = @{
title = $Title
body = $Body
head = $BranchName
base = "master"
} | ConvertTo-Json
$url = "pulls"
return $this.InvokeRestMethod($url, 'Post', $null, $requestBody)
}
[object] GetPullRequest([string]$BranchName, [string]$RepositoryOwner){
$url = "pulls"
return $this.InvokeRestMethod($url, 'GET', "head=${RepositoryOwner}:$BranchName&base=master", $null)
}
[object] UpdatePullRequest([string]$Title, [string]$Body, [string]$BranchName, [string]$PullRequestNumber){
$requestBody = @{
title = $Title
body = $Body
head = $BranchName
base = "master"
} | ConvertTo-Json
$url = "pulls/$PullRequestNumber"
return $this.InvokeRestMethod($url, 'Post', $null, $requestBody)
}
[array] GetReleases(){
$url = "releases"
$releases = @()
$pageNumber = 1
$releaseNumberLimit = 10000
while ($releases.Count -le $releaseNumberLimit)
{
$requestParams = "page=${pageNumber}&per_page=100"
[array] $response = $this.InvokeRestMethod($url, 'GET', $requestParams, $null)
if ($response.Count -eq 0) {
break
} else {
$releases += $response
$pageNumber++
}
}
return $releases
}
[string] hidden BuildUrl([string]$Url, [string]$RequestParams) {
if ([string]::IsNullOrEmpty($RequestParams)) {
return "$($this.BaseUrl)/$($Url)"
} else {
return "$($this.BaseUrl)/$($Url)?$($RequestParams)"
}
}
[object] hidden InvokeRestMethod(
[string] $Url,
[string] $Method,
[string] $RequestParams,
[string] $Body
) {
$requestUrl = $this.BuildUrl($Url, $RequestParams)
$params = @{
Method = $Method
ContentType = "application/json"
Uri = $requestUrl
Headers = @{}
}
if ($this.AuthHeader) {
$params.Headers += $this.AuthHeader
}
if (![string]::IsNullOrEmpty($Body)) {
$params.Body = $Body
}
return Invoke-RestMethod @params
}
}
function Get-GitHubApi {
param (
[string] $AccountName,
[string] $ProjectName,
[string] $AccessToken
)
return [GitHubApi]::New($AccountName, $ProjectName, $AccessToken)
}

44
helpers/nix-helpers.psm1 Normal file
View File

@@ -0,0 +1,44 @@
<#
.SYNOPSIS
Unpack *.tar file
#>
function Extract-TarArchive {
param(
[Parameter(Mandatory=$true)]
[String]$ArchivePath,
[Parameter(Mandatory=$true)]
[String]$OutputDirectory
)
Write-Debug "Extract $ArchivePath to $OutputDirectory"
tar -C $OutputDirectory -xzf $ArchivePath --strip 1
}
function Create-TarArchive {
param(
[Parameter(Mandatory=$true)]
[String]$SourceFolder,
[Parameter(Mandatory=$true)]
[String]$ArchivePath,
[string]$CompressionType = "gz",
[switch]$DereferenceSymlinks
)
$arguments = @(
"-c",
"-f", $ArchivePath,
"."
)
If ($CompressionType) {
$arguments += "--${CompressionType}"
}
if ($DereferenceSymlinks) {
$arguments += "-h"
}
Push-Location $SourceFolder
Write-Debug "tar $arguments"
tar @arguments
Pop-Location
}

View File

@@ -0,0 +1,35 @@
<#
.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
Optional parameter. The name of tool repository
.PARAMETER GitHubAccessToken
Required parameter. PAT Token to overcome GitHub API Rate limit
.PARAMETER OutputFile
Required parameter. File "*.json" where generated results will be saved
.PARAMETER ConfigurationFile
Path to the json file with parsing configuration
#>
param (
[Parameter(Mandatory)] [string] $GitHubRepositoryOwner,
[Parameter(Mandatory)] [string] $GitHubRepositoryName,
[Parameter(Mandatory)] [string] $GitHubAccessToken,
[Parameter(Mandatory)] [string] $OutputFile,
[Parameter(Mandatory)] [string] $ConfigurationFile
)
Import-Module (Join-Path $PSScriptRoot "../github/github-api.psm1")
Import-Module (Join-Path $PSScriptRoot "manifest-utils.psm1") -Force
$configuration = Read-ConfigurationFile -Filepath $ConfigurationFile
$gitHubApi = Get-GitHubApi -AccountName $GitHubRepositoryOwner -ProjectName $GitHubRepositoryName -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

@@ -0,0 +1,116 @@
Import-Module (Join-Path $PSScriptRoot "manifest-utils.psm1") -Force
Describe "New-AssetItem" {
It "use regex to parse all values in correct order" {
$githubAsset = @{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "long_url"; }
$configuration = @{
regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)";
groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = 3; };
}
$expectedOutput = [PSCustomObject]@{
filename = "python-3.8.3-linux-16.04-x64.tar.gz"; platform = "linux"; platform_version = "16.04";
arch = "x64"; download_url = "long_url";
}
$actualOutput = New-AssetItem -ReleaseAsset $githubAsset -Configuration $configuration
Assert-Equivalent -Actual $actualOutput -Expected $expectedOutput
}
It "support constant values in groups" {
$githubAsset = @{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "long_url"; }
$configuration = @{
regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)";
groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = "x64"; }
}
$expectedOutput = [PSCustomObject]@{
filename = "python-3.8.3-linux-16.04-x64.tar.gz"; platform = "linux"; platform_version = "16.04";
arch = "x64"; download_url = "long_url";
}
$actualOutput = New-AssetItem -ReleaseAsset $githubAsset -Configuration $configuration
Assert-Equivalent -Actual $actualOutput -Expected $expectedOutput
}
It "Skip empty groups" {
$githubAsset = @{ name = "python-3.8.3-win32-x64.zip"; browser_download_url = "long_url"; }
$configuration = @{
regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)";
groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = 3; }
}
$expectedOutput = [PSCustomObject]@{
filename = "python-3.8.3-win32-x64.zip"; platform = "win32";
arch = "x64"; download_url = "long_url";
}
$actualOutput = New-AssetItem -ReleaseAsset $githubAsset -Configuration $configuration
Assert-Equivalent -Actual $actualOutput -Expected $expectedOutput
}
}
Describe "Get-VersionFromRelease" {
It "clear version" {
$release = @{ name = "3.8.3" }
Get-VersionFromRelease -Release $release | Should -Be "3.8.3"
}
It "version with title" {
$release = @{ name = "3.8.3: Release title" }
Get-VersionFromRelease -Release $release | Should -Be "3.8.3"
}
}
Describe "Build-VersionsManifest" {
$assets = @(
@{ name = "python-3.8.3-linux-16.04-x64.tar.gz"; browser_download_url = "fake_url"; }
@{ name = "python-3.8.3-linux-18.04-x64.tar.gz"; browser_download_url = "fake_url"; }
)
$configuration = @{
regex = "python-\d+\.\d+\.\d+-(\w+)-([\w\.]+)?-?(x\d+)";
groups = [PSCustomObject]@{ platform = 1; platform_version = 2; arch = "x64"; }
}
$expectedManifestFiles = @(
[PSCustomObject]@{ filename = "python-3.8.3-linux-16.04-x64.tar.gz"; arch = "x64"; platform = "linux"; platform_version = "16.04"; download_url = "fake_url" },
[PSCustomObject]@{ filename = "python-3.8.3-linux-18.04-x64.tar.gz"; arch = "x64"; platform = "linux"; platform_version = "18.04"; download_url = "fake_url" }
)
It "build manifest with correct version order" {
$releases = @(
@{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-14T09:54:06Z"; assets = $assets },
@{ name = "3.5.2: Hello"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:45:36Z"; assets = $assets },
@{ name = "3.8.3: Release title"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:43:38Z"; assets = $assets }
)
$expectedManifest = @(
[PSCustomObject]@{ version = "3.8.3"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles },
[PSCustomObject]@{ version = "3.8.1"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles },
[PSCustomObject]@{ version = "3.5.2"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles }
)
$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
}
It "Skip draft and prerelease" {
$releases = @(
@{ name = "3.8.1"; draft = $true; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-14T09:54:06Z"; assets = $assets },
@{ name = "3.5.2"; draft = $false; prerelease = $true; html_url = "fake_html_url"; published_at = "2020-05-06T11:45:36Z"; assets = $assets },
@{ name = "3.8.3"; draft = $false; prerelease = $false; html_url = "fake_html_url"; published_at = "2020-05-06T11:43:38Z"; assets = $assets }
)
$expectedManifest = @(
[PSCustomObject]@{ version = "3.8.3"; stable = $true; release_url = "fake_html_url"; files = $expectedManifestFiles }
)
[array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
}
It "take latest published release for each version" {
$releases = @(
@{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url1"; published_at = "2020-05-06T11:45:36Z"; assets = $assets },
@{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url2"; published_at = "2020-05-14T09:54:06Z"; assets = $assets },
@{ name = "3.8.1"; draft = $false; prerelease = $false; html_url = "fake_html_url3"; published_at = "2020-05-06T11:43:38Z"; assets = $assets }
)
$expectedManifest = @(
[PSCustomObject]@{ version = "3.8.1"; stable = $true; release_url = "fake_html_url2"; files = $expectedManifestFiles }
)
[array]$actualManifest = Build-VersionsManifest -Releases $releases -Configuration $configuration
Assert-Equivalent -Actual $actualManifest -Expected $expectedManifest
}
}

View File

@@ -0,0 +1,77 @@
function Read-ConfigurationFile {
param ([Parameter(Mandatory)][string]$Filepath)
return Get-Content $Filepath -Raw | ConvertFrom-Json
}
function New-AssetItem {
param (
[Parameter(Mandatory)][object]$ReleaseAsset,
[Parameter(Mandatory)][object]$Configuration
)
$regexResult = [regex]::Match($ReleaseAsset.name, $Configuration.regex)
if (-not $regexResult.Success) { throw "Can't match asset filename '$($_.name)' to regex" }
$result = New-Object PSObject
$result | Add-Member -Name "filename" -Value $ReleaseAsset.name -MemberType NoteProperty
$Configuration.groups.PSObject.Properties | ForEach-Object {
if (($_.Value).GetType().Name.StartsWith("Int")) {
$value = $regexResult.Groups[$_.Value].Value
} else {
$value = $_.Value
}
if (-not ([string]::IsNullOrEmpty($value))) {
$result | Add-Member -Name $_.Name -Value $value -MemberType NoteProperty
}
}
$result | Add-Member -Name "download_url" -Value $ReleaseAsset.browser_download_url -MemberType NoteProperty
return $result
}
function Get-VersionFromRelease {
param (
[Parameter(Mandatory)][object]$Release
)
# Release name can contain additional information after ':' so filter it
[string]$releaseName = $Release.name.Split(':')[0]
[Version]$version = $null
if (![Version]::TryParse($releaseName, [ref]$version)) {
throw "Release '$($Release.id)' has invalid title '$($Release.name)'. It can't be parsed as version. ( $($Release.html_url) )"
}
return $version
}
function Build-VersionsManifest {
param (
[Parameter(Mandatory)][array]$Releases,
[Parameter(Mandatory)][object]$Configuration
)
$Releases = $Releases | Sort-Object -Property "published_at" -Descending
$versionsHash = @{}
foreach ($release in $Releases) {
if (($release.draft -eq $true) -or ($release.prerelease -eq $true)) {
continue
}
[Version]$version = Get-VersionFromRelease $release
$versionKey = $version.ToString()
if ($versionsHash.ContainsKey($versionKey)) {
continue
}
$versionsHash.Add($versionKey, [PSCustomObject]@{
version = $versionKey
stable = $true
release_url = $release.html_url
files = $release.assets | ForEach-Object { New-AssetItem -ReleaseAsset $_ -Configuration $Configuration }
})
}
# Sort versions by descending
return $versionsHash.Values | Sort-Object -Property @{ Expression = { [Version]$_.version }; Descending = $true }
}

View File

@@ -0,0 +1,33 @@
<#
.SYNOPSIS
Pester extension that allows to run command and validate exit code
.EXAMPLE
"python file.py" | Should -ReturnZeroExitCode
#>
function ShouldReturnZeroExitCode {
Param(
[Parameter (Mandatory = $true)] [ValidateNotNullOrEmpty()]
[String]$ActualValue,
[switch]$Negate
)
Write-Host "Run command '${ActualValue}'"
Invoke-Expression -Command $ActualValue | ForEach-Object { Write-Host $_ }
$actualExitCode = $LASTEXITCODE
[bool]$succeeded = $actualExitCode -eq 0
if ($Negate) { $succeeded = -not $succeeded }
if (-not $succeeded)
{
$failureMessage = "Command '${ActualValue}' has finished with exit code ${actualExitCode}"
}
return New-Object PSObject -Property @{
Succeeded = $succeeded
FailureMessage = $failureMessage
}
}
Add-AssertionOperator -Name ReturnZeroExitCode `
-Test $function:ShouldReturnZeroExitCode

39
helpers/win-helpers.psm1 Normal file
View File

@@ -0,0 +1,39 @@
<#
.SYNOPSIS
Unpack *.7z file
#>
function Extract-SevenZipArchive {
param(
[Parameter(Mandatory=$true)]
[String]$ArchivePath,
[Parameter(Mandatory=$true)]
[String]$OutputDirectory
)
Write-Debug "Extract $ArchivePath to $OutputDirectory"
7z x $ArchivePath -o"$OutputDirectory" -y | Out-Null
}
function Create-SevenZipArchive {
param(
[Parameter(Mandatory=$true)]
[String]$SourceFolder,
[Parameter(Mandatory=$true)]
[String]$ArchivePath,
[String]$ArchiveType = "zip",
[String]$CompressionLevel = 5,
[switch]$IncludeSymlinks
)
$ArchiveTypeArguments = @(
"-t${ArchiveType}",
"-mx=${CompressionLevel}"
)
if ($IncludeSymlinks) {
$ArchiveTypeArguments += "-snl"
}
Push-Location $SourceFolder
Write-Debug "7z a $ArchiveTypeArgument $ArchivePath @$SourceFolder"
7z a @ArchiveTypeArguments $ArchivePath $SourceFolder\*
Pop-Location
}

48
helpers/win-vs-env.psm1 Normal file
View File

@@ -0,0 +1,48 @@
###
# Visual Studio helper functions
###
function Get-VSWhere {
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe";
if (-not (Test-Path $vswhere )) {
[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12
$vswhere = ".\vswhere.exe"
$vswhereApiUri = "https://api.github.com/repos/Microsoft/vswhere/releases/latest"
$tag = (Invoke-RestMethod -Uri $vswhereApiUri)[0].tag_name
$vswhereUri = "https://github.com/Microsoft/vswhere/releases/download/$tag/vswhere.exe"
Invoke-WebRequest -Uri $vswhereUri -OutFile $vswhere | Out-Null
}
return $vswhere
}
function Invoke-Environment
{
Param
(
[Parameter(Mandatory)]
[string]
$Command
)
& "${env:COMSPEC}" /s /c "`"$Command`" -no_logo && set" | Foreach-Object {
if ($_ -match '^([^=]+)=(.*)') {
[System.Environment]::SetEnvironmentVariable($matches[1], $matches[2])
}
}
}
function Get-VSInstallationPath {
$vswhere = Get-VSWhere
$installationPath = & $vswhere -prerelease -legacy -latest -property installationPath
return $installationPath
}
function Invoke-VSDevEnvironment {
Write-Host "Invoke-VSDevEnvironment had been invoked"
$installationPath = Get-VSInstallationPath
$envFilepath = Join-Path $installationPath "Common7\Tools\vsdevcmd.bat"
Invoke-Environment -Command $envFilepath
}

View File

@@ -0,0 +1,33 @@
set -e
BOOST_VERSION="{{__VERSION__}}"
ARCHITECTURE="{{__ARCHITECTURE__}}"
if [ -z ${AGENT_TOOLSDIRECTORY+x} ]; then
# No AGENT_TOOLSDIRECTORY on GitHub images
TOOLCACHE_ROOT=$RUNNER_TOOL_CACHE
else
TOOLCACHE_ROOT=$AGENT_TOOLSDIRECTORY
fi
BOOST_TOOLCACHE_PATH=$TOOLCACHE_ROOT/boost
BOOST_TOOLCACHE_VERSION_PATH=$BOOST_TOOLCACHE_PATH/$BOOST_VERSION
BOOST_TOOLCACHE_VERSION_ARCH_PATH=$BOOST_TOOLCACHE_VERSION_PATH/$ARCHITECTURE
echo "Check if Boost hostedtoolcache folder exist..."
if [ ! -d $BOOST_TOOLCACHE_PATH ]; then
mkdir -p $BOOST_TOOLCACHE_PATH
fi
echo "Delete Boost $BOOST_VERSION [$ARCHITECTURE] if installed"
rm -rf $BOOST_TOOLCACHE_VERSION_ARCH_PATH
echo "Create Boost $BOOST_VERSION [$ARCHITECTURE] folder"
mkdir -p $BOOST_TOOLCACHE_VERSION_ARCH_PATH
echo "Copy Boost binaries to hostedtoolcache folder"
cp -R ./* $BOOST_TOOLCACHE_VERSION_ARCH_PATH
rm $BOOST_TOOLCACHE_VERSION_ARCH_PATH/setup.sh
echo "Create complete file"
touch $BOOST_TOOLCACHE_VERSION_PATH/$ARCHITECTURE.complete

View File

@@ -0,0 +1,33 @@
$ErrorActionPreference = "Stop"
[Version]$Version = "{{__VERSION__}}"
[string]$Architecture = "{{__ARCHITECTURE__}}"
$ToolcacheRoot = $env:AGENT_TOOLSDIRECTORY
if ([string]::IsNullOrEmpty($ToolcacheRoot)) {
# GitHub images don't have `AGENT_TOOLSDIRECTORY` variable
$ToolcacheRoot = $env:RUNNER_TOOL_CACHE
}
$BoostToolcachePath = Join-Path -Path $ToolcacheRoot -ChildPath "boost"
$BoostToolcacheVersionPath = Join-Path -Path $BoostToolcachePath -ChildPath $Version.ToString()
$BoostToolcacheArchitecturePath = Join-Path $BoostToolcacheVersionPath $Architecture
Write-Host "Check if Boost hostedtoolcache folder exist..."
if (-not (Test-Path $BoostToolcachePath)) {
New-Item -ItemType Directory -Path $BoostToolcachePath | Out-Null
}
if (Test-Path $BoostToolcacheArchitecturePath) {
Write-Host "Delete existing Boost $Version [$Architecture]"
Remove-Item $BoostToolcacheArchitecturePath -Recurse -Force | Out-Null
}
Write-Host "Create Boost $Version [$Architecture] folder"
New-Item -ItemType Directory -Path $BoostToolcacheArchitecturePath | Out-Null
Write-Host "Copy Boost binaries to hostedtoolcache folder"
Copy-Item -Path * -Destination $BoostToolcacheArchitecturePath -Recurse
Remove-Item $BoostToolcacheArchitecturePath\setup.ps1 -Force | Out-Null
Write-Host "Create complete file"
New-Item -ItemType File -Path $BoostToolcacheVersionPath -Name "$Architecture.complete" | Out-Null

15
tests/Common.Tests.ps1 Normal file
View File

@@ -0,0 +1,15 @@
Describe "Common Tests" {
$BOOST_HEADERS = Join-Path -Path ${env:BOOST_ROOT} -ChildPath "boost"
It "Check that symlinks points to existing files" {
Get-ChildItem $BOOST_HEADERS -File -Recurse | Where-Object { $_.Target } | ForEach-Object {
$_.Target | Should -Exist
}
}
It "Check if there is no invalid symlinks" {
Get-ChildItem $BOOST_HEADERS -Recurse -File | ForEach-Object {
Get-Content $_.FullName -Raw | Should -Not -BeNullOrEmpty
}
}
}

62
tests/Nix.Tests.ps1 Normal file
View File

@@ -0,0 +1,62 @@
param (
[Version] $Version,
[String] $Platform
)
Import-Module (Join-Path $PSScriptRoot "../helpers/pester-extensions.psm1")
Set-Location "sources"
$env:Path="$env:Path;${env:BOOST_ROOT}\lib"
if ($Platform -eq "linux-16.04") {
Write-Host "Install dependencies"
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 `
--slave /usr/bin/g++ g++ /usr/bin/g++-7
sudo update-alternatives --config gcc
}
Describe "Nix Tests" {
It "Simple code" {
"g++ -w -I ${env:BOOST_ROOT}/include main.cpp -o headers_test" | Should -ReturnZeroExitCode
"./headers_test" | Should -ReturnZeroExitCode
}
It "Test header existence" {
"g++ -w -std=c++14 -I ${env:BOOST_ROOT}/include main-headers.cpp -o headers_test_2" | Should -ReturnZeroExitCode
"./headers_test_2" | Should -ReturnZeroExitCode
}
It "Test shared debug" {
$BuildParams = (
"-w", "-DBOOST_LOG_DYN_LINK",
"-I", "${env:BOOST_ROOT}/include",
"-L", "${env:BOOST_ROOT}/lib", "main_log.cpp",
"-l:libboost_log_setup-mt-d-x64.so.${Version}",
"-l:libboost_log-mt-d-x64.so.${Version}",
"-l:libboost_thread-mt-d-x64.so.${Version}",
"-l:libboost_filesystem-mt-d-x64.so.${Version}",
"-lpthread"
)
"g++ $BuildParams -o test_shared_debug" | Should -ReturnZeroExitCode
"./test_shared_debug" | Should -ReturnZeroExitCode
}
It "Test shared release" {
$BuildParams = (
"-w", "-DBOOST_LOG_DYN_LINK",
"-I", "${env:BOOST_ROOT}/include",
"-L", "${env:BOOST_ROOT}/lib", "main_log.cpp",
"-l:libboost_log_setup-mt-x64.so.${Version}",
"-l:libboost_log-mt-x64.so.${Version}",
"-l:libboost_thread-mt-x64.so.${Version}",
"-l:libboost_filesystem-mt-x64.so.${Version}",
"-lpthread"
)
"g++ $BuildParams -o test_shared_release" | Should -ReturnZeroExitCode
"./test_shared_release" | Should -ReturnZeroExitCode
}
}

69
tests/Windows.Tests.ps1 Normal file
View File

@@ -0,0 +1,69 @@
param (
[Version] $Version,
[String] $Platform
)
Import-Module (Join-Path $PSScriptRoot "../helpers/pester-extensions.psm1")
Import-Module (Join-Path $PSScriptRoot "../helpers/win-vs-env.psm1")
Set-Location -Path "sources"
$env:Path="$env:Path;${env:BOOST_ROOT}\lib"
Write-Host "Initialize VS dev environment"
Invoke-VSDevEnvironment
Describe "Windows Tests" {
It "Run simple code" {
"cl -nologo /EHsc -I ${env:BOOST_ROOT}\include main.cpp" | Should -ReturnZeroExitCode
".\main.exe" | Should -ReturnZeroExitCode
}
It "Build with static libraries 1" {
$buildArguments = @(
"/EHsc",
"/I", "${env:BOOST_ROOT}\include",
"main-headers.cpp",
"/link", "/LIBPATH:${env:BOOST_ROOT}\lib",
"/OUT:main_static_lib_1.exe"
)
"cl -nologo $buildArguments" | Should -ReturnZeroExitCode
".\main_static_lib_1.exe" | Should -ReturnZeroExitCode
}
It "Build with dynamic libraries 1" {
$buildArguments = @(
"/EHsc", "/MD",
"/I", "${env:BOOST_ROOT}\include",
"main-headers.cpp",
"/link", "/LIBPATH:${env:BOOST_ROOT}\lib",
"/OUT:main_dynamic_lib_1.exe"
)
"cl -nologo $buildArguments" | Should -ReturnZeroExitCode
".\main_dynamic_lib_1.exe" | Should -ReturnZeroExitCode
}
It "Build with static libraries 2" {
$buildArguments = @(
"/EHsc",
"/I", "${env:BOOST_ROOT}\include",
"main_log.cpp",
"/link", "/LIBPATH:${env:BOOST_ROOT}\lib",
"/OUT:main_static_lib_2.exe"
)
"cl -nologo $buildArguments" | Should -ReturnZeroExitCode
".\main_static_lib_2.exe" | Should -ReturnZeroExitCode
}
It "Build with dynamic libraries 2" {
$buildArguments = @(
"/EHsc", "/MD",
"/I", "${env:BOOST_ROOT}\include",
"main_log.cpp",
"/link", "/LIBPATH:${env:BOOST_ROOT}\lib",
"/OUT:main_dynamic_lib_2.exe"
)
"cl -nologo $buildArguments" | Should -ReturnZeroExitCode
".\main_dynamic_lib_2.exe" | Should -ReturnZeroExitCode
}
}

View File

@@ -0,0 +1,22 @@
#include <cassert>
#include <exception>
#include <iostream>
#include <cstdint>
#include <boost/safe_numerics/safe_integer.hpp>
int main()
{
std::cout << "Using safe numerics" << std::endl;
try{
using namespace boost::safe_numerics;
safe<int> x = INT_MAX - 5;
++x;
}
catch(const std::exception & e){
std::cout << e.what() << std::endl;
std::cout << "error detected!" << std::endl;
}
return 0;
}

13
tests/sources/main.cpp Normal file
View File

@@ -0,0 +1,13 @@
#include <boost/algorithm/string.hpp>
#include <string>
#include <iostream>
using namespace boost::algorithm;
int main()
{
std::string s = "Boost C++ Libraries";
std::cout << to_upper_copy(s) << '\n';
return 0;
}

View File

@@ -0,0 +1,13 @@
#include <boost/log/trivial.hpp>
int main(int, char*[])
{
BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
BOOST_LOG_TRIVIAL(debug) << "A debug severity message";
BOOST_LOG_TRIVIAL(info) << "An informational severity message";
BOOST_LOG_TRIVIAL(warning) << "A warning severity message";
BOOST_LOG_TRIVIAL(error) << "An error severity message";
BOOST_LOG_TRIVIAL(fatal) << "A fatal severity message";
return 0;
}