diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 727dfd76..4903ba5f 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "cake.tool": { - "version": "0.38.5", + "version": "1.3.0", "commands": [ "dotnet-cake" ] diff --git a/README.md b/README.md index b6bf5872..7918b140 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,32 @@ This addin for Cake allows you to work with Azure DevOps. |GitHub Release|-|[![GitHub release](https://img.shields.io/github/release/cake-contrib/Cake.AzureDevOps.svg)](https://github.com/cake-contrib/Cake.AzureDevOps/releases/latest)| |NuGet|[![NuGet](https://img.shields.io/nuget/v/Cake.AzureDevOps.svg)](https://www.nuget.org/packages/Cake.AzureDevOps)|[![NuGet](https://img.shields.io/nuget/vpre/Cake.AzureDevOps.svg)](https://www.nuget.org/packages/Cake.AzureDevOps)| -## Build Status - -| | Develop | Master | -|:--:|:--:|:--:| -|AppVeyor Windows|[![Build status](https://ci.appveyor.com/api/projects/status/45blf3csh70opuos/branch/develop?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-azuredevops/branch/develop)|[![Build status](https://ci.appveyor.com/api/projects/status/45blf3csh70opuos/branch/master?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-azuredevops/branch/master)| -|Azure DevOps Windows|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Windows)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Windows)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| -|Azure DevOps macOS|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=macOS)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=macOS)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| -|Azure DevOps Ubuntu|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Ubuntu)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Ubuntu)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +## Build & Test Status + +| Type | CI Server | Runner | Operating System | Develop | Master | +|:------------------:|:------------:|:-------------------------------:|:-------------------:|:-------:|:------:| +| Build & Unit Tests | AppVeyor | N/A | Windows Server 2019 |[![Build status](https://ci.appveyor.com/api/projects/status/45blf3csh70opuos/branch/develop?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-azuredevops/branch/develop)|[![Build status](https://ci.appveyor.com/api/projects/status/45blf3csh70opuos/branch/master?svg=true)](https://ci.appveyor.com/project/cakecontrib/cake-azuredevops/branch/master)| +| Build & Unit Tests | Azure DevOps | N/A | Windows Server 2022 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Build%20%26%20Test%20Windows)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Build%20%26%20Test%20Windows)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Build & Unit Tests | Azure DevOps | N/A | macOS 11 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Build%20%26%20Test%20macOS)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Build%20%26%20Test%20macOS)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Build & Unit Tests | Azure DevOps | N/A | Ubuntu 20.04 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Build%20%26%20Test%20Ubuntu)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Build%20%26%20Test%20Ubuntu)](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET Core 3.1 | Windows Server 2019 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20Windows%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20Windows%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET 5.0 | Windows Server 2019 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20Windows%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20Windows%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET 6.0 | Windows Server 2022 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20Windows%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20Windows%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET Core 3.1 | Windows Server 2019 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20Windows%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20Windows%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET 5.0 | Windows Server 2019 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20Windows%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20Windows%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET 6.0 | Windows Server 2022 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20Windows%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20Windows%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET Core 3.1 | macOS 11 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20macOS%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20macOS%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET 5.0 | macOS 11 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20macOS%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20macOS%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET 6.0 | macOS 11 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20macOS%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20macOS%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET Core 3.1 | macOS 11 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20macOS%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20macOS%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET 5.0 | macOS 11 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20macOS%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20macOS%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET 6.0 | macOS 11 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20macOS%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20macOS%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET Core 3.1 | Ubuntu 20.04 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20Ubuntu%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20Ubuntu%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET 5.0 | Ubuntu 20.04 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20Ubuntu%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20Ubuntu%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Frosting on .NET 6.0 | Ubuntu 20.04 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Frosting%20Ubuntu%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Frosting%20Ubuntu%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET Core 3.1 | Ubuntu 20.04 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20Ubuntu%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20Ubuntu%20(.NET%20Core%203.1))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET 5.0 | Ubuntu 20.04 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20Ubuntu%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20Ubuntu%20(.NET%205))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| +| Integration Tests | Azure DevOps | Cake Scripting on .NET 6.0 | Ubuntu 20.04 |[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=develop&jobName=Integration%20Tests%20Script%20Runner%20Ubuntu%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=develop)|[![Build Status](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_apis/build/status/cake-contrib.Cake.AzureDevOps?branchName=master&jobName=Integration%20Tests%20Script%20Runner%20Ubuntu%20(.NET%206))](https://dev.azure.com/cake-contrib/Cake.AzureDevOps/_build/latest?definitionId=24&branchName=master)| ## Demos diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 390e5c0c..b6097c25 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,7 +10,9 @@ pr: - hotfix/* jobs: -- job: Windows +# Build & Test Windows +- job: Build_Windows + displayName: Build & Test Windows pool: vmImage: 'windows-2022' steps: @@ -19,10 +21,15 @@ jobs: .\build.ps1 exit $LASTEXITCODE displayName: 'Cake Build' + - publish: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + artifact: NuGet Package Windows + displayName: 'Publish NuGet package as build artifact' -- job: macOS +# Build & Test macOS +- job: Build_macOS + displayName: Build & Test macOS pool: - vmImage: 'macOS-10.15' + vmImage: 'macOS-11' steps: - task: UseDotNet@2 inputs: @@ -36,10 +43,15 @@ jobs: - bash: | ./build.sh displayName: 'Cake Build' + - publish: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + artifact: NuGet Package macOS + displayName: 'Publish NuGet package as build artifact' -- job: Ubuntu +# Build & Test Ubuntu +- job: Build_Ubuntu + displayName: Build & Test Ubuntu pool: - vmImage: 'ubuntu-18.04' + vmImage: 'ubuntu-20.04' steps: - task: UseDotNet@2 inputs: @@ -52,4 +64,421 @@ jobs: version: '3.x' - bash: | ./build.sh --verbosity=diagnostic - displayName: 'Cake Build' \ No newline at end of file + displayName: 'Cake Build' + - publish: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + artifact: NuGet Package Ubuntu + displayName: 'Publish NuGet package as build artifact' + +# Integration Tests Frosting Windows (.NET Core 3.1) +- job: Test_Frosting_Windows_NetCoreApp31 + displayName: Integration Tests Frosting Windows (.NET Core 3.1) + dependsOn: Build_Windows + pool: + vmImage: 'windows-2019' + steps: + - download: current + artifact: NuGet Package Windows + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Windows + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - powershell: ./build.ps1 --verbosity=diagnostic + workingDirectory: ./tests/frosting/netcoreapp3.1 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting Windows (.NET 5) +- job: Test_Frosting_Windows_Net5 + displayName: Integration Tests Frosting Windows (.NET 5) + dependsOn: Build_Windows + pool: + vmImage: 'windows-2019' + steps: + - download: current + artifact: NuGet Package Windows + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Windows + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - powershell: ./build.ps1 --verbosity=diagnostic + workingDirectory: ./tests/frosting/net5.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting Windows (.NET 6) +- job: Test_Frosting_Windows_Net6 + displayName: Integration Tests Frosting Windows (.NET 6) + dependsOn: Build_Windows + pool: + vmImage: 'windows-2022' + steps: + - download: current + artifact: NuGet Package Windows + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Windows + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - powershell: ./build.ps1 --verbosity=diagnostic + workingDirectory: ./tests/frosting/net6.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner Windows (.NET Core 3.1) +- job: Test_Script_Runner_Windows_NetCoreApp31 + displayName: Integration Tests Script Runner Windows (.NET Core 3.1) + dependsOn: Build_Windows + pool: + vmImage: 'windows-2019' + steps: + - download: current + artifact: NuGet Package Windows + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Windows + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - powershell: ./build.ps1 --verbosity=diagnostic + workingDirectory: ./tests/script-runner/netcoreapp3.1 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner Windows (.NET 5) +- job: Test_Script_Runner_Windows_Net5 + displayName: Integration Tests Script Runner Windows (.NET 5) + dependsOn: Build_Windows + pool: + vmImage: 'windows-2019' + steps: + - download: current + artifact: NuGet Package Windows + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Windows + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - powershell: ./build.ps1 --verbosity=diagnostic + workingDirectory: ./tests/script-runner/net5.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner Windows (.NET 6) +- job: Test_Script_Runner_Windows_Net6 + displayName: Integration Tests Script Runner Windows (.NET 6) + dependsOn: Build_Windows + pool: + vmImage: 'windows-2022' + steps: + - download: current + artifact: NuGet Package Windows + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Windows + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - powershell: ./build.ps1 --verbosity=diagnostic + workingDirectory: ./tests/script-runner/net6.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting macOS (.NET Core 3.1) +- job: Test_Frosting_macOS_NetCoreApp31 + displayName: Integration Tests Frosting macOS (.NET Core 3.1) + dependsOn: Build_macOS + pool: + vmImage: 'macOS-11' + steps: + - download: current + artifact: NuGet Package macOS + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package macOS + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '3.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/frosting/netcoreapp3.1 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting macOS (.NET 5) +- job: Test_Frosting_macOS_Net5 + displayName: Integration Tests Frosting macOS (.NET 5) + dependsOn: Build_macOS + pool: + vmImage: 'macOS-11' + steps: + - download: current + artifact: NuGet Package macOS + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package macOS + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '5.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/frosting/net5.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting macOS (.NET 6) +- job: Test_Frosting_macOS_Net6 + displayName: Integration Tests Frosting macOS (.NET 6) + dependsOn: Build_macOS + pool: + vmImage: 'macOS-11' + steps: + - download: current + artifact: NuGet Package macOS + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package macOS + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '6.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/frosting/net6.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner macOS (.NET Core 3.1) +- job: Test_Script_Runner_macOS_NetCoreApp31 + displayName: Integration Tests Script Runner macOS (.NET Core 3.1) + dependsOn: Build_macOS + pool: + vmImage: 'macOS-11' + steps: + - download: current + artifact: NuGet Package macOS + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package macOS + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '3.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/script-runner/netcoreapp3.1 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner macOS (.NET 5) +- job: Test_Script_Runner_macOS_Net5 + displayName: Integration Tests Script Runner macOS (.NET 5) + dependsOn: Build_macOS + pool: + vmImage: 'macOS-11' + steps: + - download: current + artifact: NuGet Package macOS + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package macOS + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '5.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/script-runner/net5.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner macOS (.NET 6) +- job: Test_Script_Runner_macOS_Net6 + displayName: Integration Tests Script Runner macOS (.NET 6) + dependsOn: Build_macOS + pool: + vmImage: 'macOS-11' + steps: + - download: current + artifact: NuGet Package macOS + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package macOS + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '6.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/script-runner/net6.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting Ubuntu (.NET Core 3.1) +- job: Test_Frosting_Ubuntu_NetCoreApp31 + displayName: Integration Tests Frosting Ubuntu (.NET Core 3.1) + dependsOn: Build_Ubuntu + pool: + vmImage: 'ubuntu-20.04' + steps: + - download: current + artifact: NuGet Package Ubuntu + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Ubuntu + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '3.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/frosting/netcoreapp3.1 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting Ubuntu (.NET 5) +- job: Test_Frosting_Ubuntu_Net5 + displayName: Integration Tests Frosting Ubuntu (.NET 5) + dependsOn: Build_Ubuntu + pool: + vmImage: 'ubuntu-20.04' + steps: + - download: current + artifact: NuGet Package Ubuntu + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Ubuntu + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '5.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/frosting/net5.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Frosting Ubuntu (.NET 6) +- job: Test_Frosting_Ubuntu_Net6 + displayName: Integration Tests Frosting Ubuntu (.NET 6) + dependsOn: Build_Ubuntu + pool: + vmImage: 'ubuntu-20.04' + steps: + - download: current + artifact: NuGet Package Ubuntu + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Ubuntu + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '6.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/frosting/net6.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner Ubuntu (.NET Core 3.1) +- job: Test_Script_Runner_Ubuntu_NetCoreApp31 + displayName: Integration Tests Script Runner Ubuntu (.NET Core 3.1) + dependsOn: Build_Ubuntu + pool: + vmImage: 'ubuntu-20.04' + steps: + - download: current + artifact: NuGet Package Ubuntu + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Ubuntu + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '3.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/script-runner/netcoreapp3.1 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner Ubuntu (.NET 5) +- job: Test_Script_Runner_Ubuntu_Net5 + displayName: Integration Tests Script Runner Ubuntu (.NET 5) + dependsOn: Build_Ubuntu + pool: + vmImage: 'ubuntu-20.04' + steps: + - download: current + artifact: NuGet Package Ubuntu + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Ubuntu + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '5.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/script-runner/net5.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) + +# Integration Tests Script Runner Ubuntu (.NET 6) +- job: Test_Script_Runner_Ubuntu_Net6 + displayName: Integration Tests Script Runner Ubuntu (.NET 6) + dependsOn: Build_Ubuntu + pool: + vmImage: 'ubuntu-20.04' + steps: + - download: current + artifact: NuGet Package Ubuntu + displayName: 'Download build artifact' + - task: CopyFiles@2 + inputs: + sourceFolder: $(Pipeline.Workspace)/NuGet Package Ubuntu + targetFolder: $(Build.SourcesDirectory)/BuildArtifacts/Packages/NuGet + displayName: 'Copy build artifact for test run' + - task: UseDotNet@2 + inputs: + version: '6.x' + - bash: ./build.sh --verbosity=diagnostic + workingDirectory: ./tests/script-runner/net6.0 + displayName: 'Run integration tests' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/recipe.cake b/recipe.cake index 32a2ad86..f9917f57 100644 --- a/recipe.cake +++ b/recipe.cake @@ -1,4 +1,4 @@ -#load nuget:?package=Cake.Recipe&version=2.2.1 +#load nuget:?package=Cake.Recipe&version=3.0.1 Environment.SetVariableNames(); @@ -11,7 +11,6 @@ BuildParameters.SetParameters( repositoryName: "Cake.AzureDevOps", appVeyorAccountName: "cakecontrib", shouldCalculateVersion: true, - shouldRunDupFinder: false, // dupFinder is missing in 2021.3.0-eap shouldRunDotNetCorePack: true, shouldGenerateDocumentation: false, // Fails to restore tool on AppVeyor shouldRunCoveralls: false, // Fails to restore tool on AppVeyor @@ -21,17 +20,10 @@ BuildParameters.PrintParameters(Context); ToolSettings.SetToolSettings( context: Context, - dupFinderExcludePattern: new string[] { BuildParameters.RootDirectoryPath + "/src/Cake.AzureDevOps.Tests/**/*.cs" }, testCoverageFilter: "+[*]* -[xunit.*]* -[Cake.Core]* -[Cake.Common]* -[*.Tests]* -[Cake.Testing]* -[Moq]* -[Shouldly]* -[DiffEngine]* -[EmptyFiles]*", testCoverageExcludeByAttribute: "*.ExcludeFromCodeCoverage*", testCoverageExcludeByFile: "*/*Designer.cs;*/*.g.cs;*/*.g.i.cs"); -// Workaround until https://github.com/cake-contrib/Cake.Recipe/issues/862 has been fixed in Cake.Recipe -ToolSettings.SetToolPreprocessorDirectives( - reSharperTools: "#tool nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2021.3.1", - coverallsGlobalTool: "#tool dotnet:?package=coveralls.net&version=3.0.0", - gitVersionGlobalTool: "#tool dotnet:?package=GitVersion.Tool&version=5.8.1"); - // Disable Upload-Coveralls-Report task since it fails to install the tool on AppVeyor BuildParameters.Tasks.UploadCoverallsReportTask.WithCriteria(() => false); diff --git a/src/Cake.AzureDevOps.Tests/Boards/WorkItemTracking/AzureDevOpsWorkItemSettingsTests.cs b/src/Cake.AzureDevOps.Tests/Boards/WorkItemTracking/AzureDevOpsWorkItemSettingsTests.cs new file mode 100644 index 00000000..07f39499 --- /dev/null +++ b/src/Cake.AzureDevOps.Tests/Boards/WorkItemTracking/AzureDevOpsWorkItemSettingsTests.cs @@ -0,0 +1,891 @@ +namespace Cake.AzureDevOps.Tests.Boards.WorkItemTracking +{ + using System; + using Cake.AzureDevOps.Authentication; + using Cake.AzureDevOps.Boards.WorkItemTracking; + using Shouldly; + using Xunit; + + public sealed class AzureDevOpsWorkItemSettingsTests + { + public sealed class TheCtorForProjectGuid + { + [Fact] + public void Should_Throw_If_CollectionUrl_Is_Null() + { + // Given + Uri collectionUrl = null; + var projectGuid = Guid.NewGuid(); + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials)); + + // Then + result.IsArgumentNullException("collectionUrl"); + } + + [Fact] + public void Should_Throw_If_ProjectGuid_Is_Empty() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.Empty; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials)); + + // Then + result.IsArgumentOutOfRangeException("projectGuid"); + } + + [Fact] + public void Should_Throw_If_WorkItemId_Is_Zero() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var workItemId = 0; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials)); + + // Then + result.IsArgumentOutOfRangeException("workItemId"); + } + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void Should_Throw_If_WorkItemId_Is_Negative(int workItemId) + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials)); + + // Then + result.IsArgumentOutOfRangeException("workItemId"); + } + + [Fact] + public void Should_Throw_If_Credentials_Are_Null() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var workItemId = 42; + IAzureDevOpsCredentials credentials = null; + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials)); + + // Then + result.IsArgumentNullException("credentials"); + } + + [Fact] + public void Should_Set_Collection_Url() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials); + + // Then + result.CollectionUrl.ShouldBe(collectionUrl); + } + + [Fact] + public void Should_Set_ProjectGuid() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials); + + // Then + result.ProjectGuid.ShouldBe(projectGuid); + } + + [Theory] + [InlineData(1)] + [InlineData(int.MaxValue)] + public void Should_Set_WorkItemId(int workItemId) + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials); + + // Then + result.WorkItemId.ShouldBe(workItemId); + } + + [Fact] + public void Should_Set_Credentials() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials); + + // Then + result.Credentials.ShouldBe(credentials); + } + } + + public sealed class TheCtorForProjectName + { + [Fact] + public void Should_Throw_If_CollectionUrl_Is_Null() + { + // Given + Uri collectionUrl = null; + var projectName = "MyProject"; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials)); + + // Then + result.IsArgumentNullException("collectionUrl"); + } + + [Fact] + public void Should_Throw_If_ProjectName_Is_Null() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + string projectName = null; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials)); + + // Then + result.IsArgumentNullException("projectName"); + } + + [Fact] + public void Should_Throw_If_ProjectName_Is_Empty() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = string.Empty; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials)); + + // Then + result.IsArgumentOutOfRangeException("projectName"); + } + + [Fact] + public void Should_Throw_If_ProjectName_Is_WhiteSpace() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = " "; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials)); + + // Then + result.IsArgumentOutOfRangeException("projectName"); + } + + [Fact] + public void Should_Throw_If_WorkItemId_Is_Zero() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 0; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials)); + + // Then + result.IsArgumentOutOfRangeException("workItemId"); + } + + [Theory] + [InlineData(-1)] + [InlineData(int.MinValue)] + public void Should_Throw_If_WorkItemId_Is_Negative(int workItemId) + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials)); + + // Then + result.IsArgumentOutOfRangeException("workItemId"); + } + + [Fact] + public void Should_Throw_If_Credentials_Are_Null() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 42; + IAzureDevOpsCredentials credentials = null; + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials)); + + // Then + result.IsArgumentNullException("credentials"); + } + + [Fact] + public void Should_Set_Collection_Url() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials); + + // Then + result.CollectionUrl.ShouldBe(collectionUrl); + } + + [Fact] + public void Should_Set_ProjectName() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials); + + // Then + result.ProjectName.ShouldBe(projectName); + } + + [Theory] + [InlineData(1)] + [InlineData(int.MaxValue)] + public void Should_Set_WorkItemId(int workItemId) + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials); + + // Then + result.WorkItemId.ShouldBe(workItemId); + } + + [Fact] + public void Should_Set_Credentials() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + + // When + var result = new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials); + + // Then + result.Credentials.ShouldBe(credentials); + } + } + + public sealed class TheCtorForSettings + { + [Fact] + public void Should_Throw_If_Settings_Are_Null() + { + // Given + AzureDevOpsWorkItemSettings settings = null; + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(settings)); + + // Then + result.IsArgumentNullException("settings"); + } + + [Fact] + public void Should_Set_Collection_Url() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + var settings = new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials); + + // When + var result = new AzureDevOpsWorkItemSettings(settings); + + // Then + result.CollectionUrl.ShouldBe(collectionUrl); + } + + [Fact] + public void Should_Set_ProjectGuid() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + var settings = new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials); + + // When + var result = new AzureDevOpsWorkItemSettings(settings); + + // Then + result.ProjectGuid.ShouldBe(projectGuid); + } + + [Fact] + public void Should_Set_ProjectName() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + var settings = new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials); + + // When + var result = new AzureDevOpsWorkItemSettings(settings); + + // Then + result.ProjectName.ShouldBe(projectName); + } + + [Theory] + [InlineData(1)] + [InlineData(int.MaxValue)] + public void Should_Set_WorkItemId(int workItemId) + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectGuid = Guid.NewGuid(); + var credentials = AuthenticationProvider.AuthenticationNtlm(); + var settings = new AzureDevOpsWorkItemSettings(collectionUrl, projectGuid, workItemId, credentials); + + // When + var result = new AzureDevOpsWorkItemSettings(settings); + + // Then + result.WorkItemId.ShouldBe(workItemId); + } + + [Fact] + public void Should_Set_Credentials() + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + var settings = new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials); + + // When + var result = new AzureDevOpsWorkItemSettings(settings); + + // Then + result.Credentials.ShouldBe(credentials); + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Should_Set_ThrowExceptionIfWorkItemCouldNotBeFound(bool value) + { + // Given + var collectionUrl = new Uri("http://example.com/collection"); + var projectName = "MyProject"; + var workItemId = 42; + var credentials = AuthenticationProvider.AuthenticationNtlm(); + var settings = new AzureDevOpsWorkItemSettings(collectionUrl, projectName, workItemId, credentials) + { + ThrowExceptionIfWorkItemCouldNotBeFound = value, + }; + + // When + var result = new AzureDevOpsWorkItemSettings(settings); + + // Then + result.ThrowExceptionIfWorkItemCouldNotBeFound.ShouldBe(value); + } + } + + public sealed class TheCtorForEnvironmentVariables : IDisposable + { + private readonly string originalCollectionUrl; + private readonly string originalProjectName; + + public TheCtorForEnvironmentVariables() + { + this.originalCollectionUrl = Environment.GetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"); + this.originalProjectName = Environment.GetEnvironmentVariable("SYSTEM_TEAMPROJECT"); + } + + [Fact] + public void Should_Throw_If_Credentials_Are_Null() + { + // Given + IAzureDevOpsCredentials creds = null; + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsArgumentNullException("credentials"); + } + + [Fact] + public void Should_Throw_If_Collection_Url_Env_Var_Is_Not_Set() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", null); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Collection_Url_Env_Var_Is_Empty() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", string.Empty); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Collection_Url_Env_Var_Is_WhiteSpace() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", " "); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Project_Name_Env_Var_Is_Not_Set() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", null); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Project_Name_Env_Var_Is_Empty() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", string.Empty); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Project_Name_Env_Var_Is_WhiteSpace() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", " "); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Set_Collection_Url() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + + // When + var settings = new AzureDevOpsWorkItemSettings(42, creds); + + // Then + settings.CollectionUrl.ToString().ShouldBe(new Uri("https://example.com/collection").ToString()); + } + + [Fact] + public void Should_Set_Project_Name() + { + // Given + var projectName = "MyProject"; + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", projectName); + + // When + var settings = new AzureDevOpsWorkItemSettings(42, creds); + + // Then + settings.ProjectName.ShouldBe(projectName); + } + + [Fact] + public void Should_Set_Credentials() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + + // When + var settings = new AzureDevOpsWorkItemSettings(42, creds); + + // Then + settings.Credentials.ShouldBe(creds); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", this.originalCollectionUrl); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", this.originalProjectName); + } + } + + public sealed class TheUsingAzurePipelinesOAuthTokenMethod : IDisposable + { + private readonly string originalCollectionUrl; + private readonly string originalProjectName; + private readonly string originalAccessToken; + + public TheUsingAzurePipelinesOAuthTokenMethod() + { + this.originalCollectionUrl = Environment.GetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI"); + this.originalProjectName = Environment.GetEnvironmentVariable("SYSTEM_TEAMPROJECT"); + this.originalAccessToken = Environment.GetEnvironmentVariable("SYSTEM_ACCESSTOKEN"); + } + + [Fact] + public void Should_Throw_If_Collection_Url_Env_Var_Is_Not_Set() + { + // Given + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", null); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => AzureDevOpsWorkItemSettings.UsingAzurePipelinesOAuthToken(42)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Collection_Url_Env_Var_Is_Empty() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", string.Empty); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Collection_Url_Env_Var_Is_WhiteSpace() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", " "); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Project_Name_Env_Var_Is_Not_Set() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", null); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Project_Name_Env_Var_Is_Empty() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", string.Empty); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_Project_Name_Env_Var_Is_WhiteSpace() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", " "); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(42, creds)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_WorkItem_Id_Value_Zero() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(0, creds)); + + // Then + result.IsArgumentOutOfRangeException("workItemId"); + } + + [Fact] + public void Should_Throw_If_WorkItem_Id_Value_Negative() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var result = Record.Exception(() => new AzureDevOpsWorkItemSettings(-1, creds)); + + // Then + result.IsArgumentOutOfRangeException("workItemId"); + } + + [Fact] + public void Should_Throw_If_System_Access_Token_Env_Var_Is_Not_Set_With_OAuthToken() + { + // Given + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", null); + + // When + var result = Record.Exception(() => AzureDevOpsWorkItemSettings.UsingAzurePipelinesOAuthToken(42)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_System_Access_Token_Env_Var_Is_Empty_With_OAuthToken() + { + // Given + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", string.Empty); + + // When + var result = Record.Exception(() => AzureDevOpsWorkItemSettings.UsingAzurePipelinesOAuthToken(42)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Throw_If_System_Access_Token_Env_Var_Is_WhiteSpace_With_OAuthToken() + { + // Given + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", " "); + + // When + var result = Record.Exception(() => AzureDevOpsWorkItemSettings.UsingAzurePipelinesOAuthToken(42)); + + // Then + result.IsInvalidOperationException(); + } + + [Fact] + public void Should_Set_Collection_Url() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var settings = new AzureDevOpsWorkItemSettings(42, creds); + + // Then + settings.CollectionUrl.ToString().ShouldBe(new Uri("https://example.com/collection").ToString()); + } + + [Fact] + public void Should_Set_Project_Name() + { + // Given + var projectName = "MyProject"; + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", projectName); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var settings = new AzureDevOpsWorkItemSettings(42, creds); + + // Then + settings.ProjectName.ShouldBe(projectName); + } + + [Fact] + public void Should_Set_WorkItem_Id() + { + // Given + var workItemId = 42; + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var settings = new AzureDevOpsWorkItemSettings(workItemId, creds); + + // Then + settings.WorkItemId.ShouldBe(workItemId); + } + + [Fact] + public void Should_Set_Credentials() + { + // Given + var creds = new AzureDevOpsNtlmCredentials(); + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", "foo"); + + // When + var settings = new AzureDevOpsWorkItemSettings(42, creds); + + // Then + settings.Credentials.ShouldBe(creds); + } + + public void Dispose() + { + Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", this.originalCollectionUrl); + Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", this.originalProjectName); + Environment.SetEnvironmentVariable("SYSTEM_ACCESSTOKEN", this.originalAccessToken); + } + } + } +} \ No newline at end of file diff --git a/src/Cake.AzureDevOps.Tests/Cake.AzureDevOps.Tests.csproj b/src/Cake.AzureDevOps.Tests/Cake.AzureDevOps.Tests.csproj index 7dc28d5b..0fdbd7ba 100644 --- a/src/Cake.AzureDevOps.Tests/Cake.AzureDevOps.Tests.csproj +++ b/src/Cake.AzureDevOps.Tests/Cake.AzureDevOps.Tests.csproj @@ -14,15 +14,15 @@ - + - - + + - - + + diff --git a/src/Cake.AzureDevOps.Tests/ExceptionAssertExtensions.cs b/src/Cake.AzureDevOps.Tests/ExceptionAssertExtensions.cs index 12c9e813..7dd44750 100644 --- a/src/Cake.AzureDevOps.Tests/ExceptionAssertExtensions.cs +++ b/src/Cake.AzureDevOps.Tests/ExceptionAssertExtensions.cs @@ -23,6 +23,17 @@ public static void IsArgumentException(this T exception, Type expectedExcepti Assert.Equal(parameterName, exception.ParamName); } + /// + /// Checks if an exception is of type . + /// + /// Exception to check. + /// Expected name of the parameter which has caused the exception. + public static void IsArgumentException(this Exception exception, string parameterName) + { + Assert.IsType(exception); + Assert.Equal(parameterName, ((ArgumentException)exception).ParamName); + } + /// /// Checks if an exception is of type . /// @@ -45,17 +56,6 @@ public static void IsArgumentOutOfRangeException(this Exception exception, strin Assert.Equal(parameterName, ((ArgumentOutOfRangeException)exception).ParamName); } - /// - /// Checks if an exception is of type . - /// - /// Exception to check. - /// Expected name of the parameter which has caused the exception. - public static void IsArgumentException(this Exception exception, string parameterName) - { - Assert.IsType(exception); - Assert.Equal(parameterName, ((ArgumentException)exception).ParamName); - } - /// /// Checks if an exception is of type . /// diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs index 237b7490..9c745eb6 100644 --- a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetBuildClientFactory.cs @@ -1,10 +1,12 @@ namespace Cake.AzureDevOps.Tests.Fakes { using System; + using System.Collections.Generic; using System.Threading; using Cake.AzureDevOps.Authentication; using Microsoft.TeamFoundation.Build.WebApi; using Microsoft.TeamFoundation.Core.WebApi; + using Microsoft.VisualStudio.Services.WebApi; using Moq; public class FakeAllSetBuildClientFactory : FakeBuildClientFactory @@ -13,7 +15,7 @@ public override BuildHttpClient CreateBuildClient(Uri collectionUrl, IAzureDevOp { var mock = new Mock(MockBehavior.Loose, collectionUrl, credentials.ToVssCredentials()); - mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default)) .ReturnsAsync((Guid projectId, int buildId, string propertyFilters, object userState, CancellationToken token) => new Build { Id = buildId, @@ -21,7 +23,7 @@ public override BuildHttpClient CreateBuildClient(Uri collectionUrl, IAzureDevOp Project = new TeamProjectReference { Id = projectId }, }); - mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default)) .ReturnsAsync((string projectName, int buildId, string propertyFilters, object userState, CancellationToken token) => new Build { Id = buildId, @@ -29,6 +31,12 @@ public override BuildHttpClient CreateBuildClient(Uri collectionUrl, IAzureDevOp Project = new TeamProjectReference { Name = projectName }, }); + mock.Setup(arg => arg.GetBuildWorkItemsRefsAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, default)) + .ReturnsAsync((string projectName, int buildId, int? top, object userState, CancellationToken token) => new List + { + new ResourceRef { Id = "42" }, + }); + mock = this.Setup(mock); return mock.Object; diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetGitClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetGitClientFactory.cs index 8bd386bd..e3a1ad0e 100644 --- a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetGitClientFactory.cs +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetGitClientFactory.cs @@ -14,7 +14,7 @@ public override GitHttpClient CreateGitClient(Uri collectionUrl, IAzureDevOpsCre { var mock = new Mock(MockBehavior.Loose, collectionUrl, credentials.ToVssCredentials()); - mock.Setup(arg => arg.GetPullRequestAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, null, null, null, null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetPullRequestAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, null, null, null, null, null, default)) .ReturnsAsync((string project1, string repoId1, int prId, int i1, int i2, int i3, bool b1, bool b2, object o1, CancellationToken c1) => new GitPullRequest { PullRequestId = prId, @@ -39,7 +39,7 @@ public override GitHttpClient CreateGitClient(Uri collectionUrl, IAzureDevOpsCre null, 1, null, - default(CancellationToken))) + default)) .ReturnsAsync((string project2, string repoId2, GitPullRequestSearchCriteria sc, int j1, int j2, int top, object o2, CancellationToken c2) => new List(new[] { @@ -73,7 +73,7 @@ protected override Mock Setup(Mock m) It.IsAny(), It.IsAny(), It.IsAny(), - default(CancellationToken))) + default)) .ReturnsAsync((IdentityRefWithVote identity, Guid project, int prId, string reviewerId, object o, CancellationToken c) => new IdentityRefWithVote { @@ -86,7 +86,7 @@ protected override Mock Setup(Mock m) It.IsAny(), It.IsAny(), It.IsAny(), - default(CancellationToken))) + default)) .Throws(new Exception("Something went wrong")); m.Setup(arg => arg.CreatePullRequestStatusAsync( diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetTestManagementClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetTestManagementClientFactory.cs index 67891bc1..431ae2fc 100644 --- a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetTestManagementClientFactory.cs +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetTestManagementClientFactory.cs @@ -14,10 +14,10 @@ public override TestManagementHttpClient CreateTestManagementClient(Uri collecti { var mock = new Mock(MockBehavior.Strict, collectionUrl, credentials.ToVssCredentials()); - mock.Setup(arg => arg.GetTestResultDetailsForBuildAsync(It.IsAny(), It.Is(id => id == 1), null, null, null, null, null, null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetTestResultDetailsForBuildAsync(It.IsAny(), It.Is(id => id == 1), null, null, null, null, null, null, null, default)) .ReturnsAsync(() => new TestResultsDetails { ResultsForGroup = new List() }); - mock.Setup(arg => arg.GetTestResultDetailsForBuildAsync(It.IsAny(), It.Is(id => id > 1), null, null, null, null, null, null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetTestResultDetailsForBuildAsync(It.IsAny(), It.Is(id => id > 1), null, null, null, null, null, null, null, default)) .ReturnsAsync(() => new TestResultsDetails { ResultsForGroup = new List() @@ -34,7 +34,7 @@ public override TestManagementHttpClient CreateTestManagementClient(Uri collecti }, }); - mock.Setup(arg => arg.GetTestResultsAsync(It.IsAny(), It.IsAny(), null, It.IsAny(), It.IsAny(), It.IsAny>(), null, default(CancellationToken))) + mock.Setup(arg => arg.GetTestResultsAsync(It.IsAny(), It.IsAny(), null, It.IsAny(), It.IsAny(), It.IsAny>(), null, default)) .ReturnsAsync((Guid projectId, int testRunId, ResultDetails? details, int? skip, int? top, IEnumerable outcomes, object userState, CancellationToken token) => new List() { new TestCaseResult { AutomatedTestName = "t1", Outcome = "Passed", ErrorMessage = string.Empty }, diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetWorkItemTrackingClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetWorkItemTrackingClientFactory.cs new file mode 100644 index 00000000..59027717 --- /dev/null +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeAllSetWorkItemTrackingClientFactory.cs @@ -0,0 +1,36 @@ +namespace Cake.AzureDevOps.Tests.Fakes +{ + using System; + using System.Collections.Generic; + using System.Threading; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.TestManagement.WebApi; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; + using Moq; + + public class FakeAllSetWorkItemTrackingClientFactory : FakeWorkItemTrackingClientFactory + { + public override WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials) + { + var mock = new Mock(MockBehavior.Strict, collectionUrl, credentials.ToVssCredentials()); + + mock.Setup(arg => arg.GetWorkItemsAsync(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny(), It.IsAny(), null, default)) + .ReturnsAsync((IEnumerable workItemIds, IEnumerable fields, DateTime? asOf, WorkItemExpand? expand, WorkItemErrorPolicy? errorPolicy, object userState, CancellationToken token) => + { + var result = new List(); + + foreach (var workItemId in workItemIds) + { + result.Add(new WorkItem { Id = workItemId }); + } + + return result; + }); + + mock = this.Setup(mock); + + return mock.Object; + } + } +} \ No newline at end of file diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeNullBuildClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeNullBuildClientFactory.cs index 5f381a4c..13d9b34f 100644 --- a/src/Cake.AzureDevOps.Tests/Fakes/FakeNullBuildClientFactory.cs +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeNullBuildClientFactory.cs @@ -12,10 +12,10 @@ public override BuildHttpClient CreateBuildClient(Uri collectionUrl, IAzureDevOp { var mock = new Mock(MockBehavior.Loose, collectionUrl, credentials.ToVssCredentials()); - mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default)) .ReturnsAsync(() => null); - mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetBuildAsync(It.IsAny(), It.IsAny(), null, null, default)) .ReturnsAsync(() => null); mock = this.Setup(mock); diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeNullForMethodsGitClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeNullForMethodsGitClientFactory.cs index fb3d3be3..d68101df 100644 --- a/src/Cake.AzureDevOps.Tests/Fakes/FakeNullForMethodsGitClientFactory.cs +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeNullForMethodsGitClientFactory.cs @@ -10,7 +10,7 @@ public class FakeNullForMethodsGitClientFactory : FakeAllSetGitClientFactory { protected override Mock Setup(Mock m) { - m.Setup(arg => arg.CreatePullRequestReviewerAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), default(CancellationToken))) + m.Setup(arg => arg.CreatePullRequestReviewerAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), default)) .ReturnsAsync(() => null); m.Setup(arg => arg.CreatePullRequestStatusAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())) diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeNullGitClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeNullGitClientFactory.cs index 5970f0ce..603500df 100644 --- a/src/Cake.AzureDevOps.Tests/Fakes/FakeNullGitClientFactory.cs +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeNullGitClientFactory.cs @@ -13,10 +13,10 @@ public override GitHttpClient CreateGitClient(Uri collectionUrl, IAzureDevOpsCre { var mock = new Mock(MockBehavior.Loose, collectionUrl, credentials.ToVssCredentials()); - mock.Setup(arg => arg.GetPullRequestAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, null, null, null, null, null, default(CancellationToken))) + mock.Setup(arg => arg.GetPullRequestAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, null, null, null, null, null, default)) .ReturnsAsync(() => null); - mock.Setup(arg => arg.GetPullRequestsAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, null, 1, null, default(CancellationToken))) + mock.Setup(arg => arg.GetPullRequestsAsync(It.IsAny(), It.IsAny(), It.IsAny(), null, null, 1, null, default)) .ReturnsAsync(() => new List()); mock = this.Setup(mock); diff --git a/src/Cake.AzureDevOps.Tests/Fakes/FakeWorkItemTrackingClientFactory.cs b/src/Cake.AzureDevOps.Tests/Fakes/FakeWorkItemTrackingClientFactory.cs new file mode 100644 index 00000000..61c8ab0f --- /dev/null +++ b/src/Cake.AzureDevOps.Tests/Fakes/FakeWorkItemTrackingClientFactory.cs @@ -0,0 +1,24 @@ +namespace Cake.AzureDevOps.Tests.Fakes +{ + using System; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + using Microsoft.VisualStudio.Services.Identity; + using Moq; + + public abstract class FakeWorkItemTrackingClientFactory : IWorkItemTrackingClientFactory + { + public abstract WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials); + + public WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity) + { + authorizedIdentity = new Identity { ProviderDisplayName = "FakeUser", Id = Guid.NewGuid(), IsActive = true }; + return this.CreateWorkItemTrackingClient(collectionUrl, credentials); + } + + protected virtual Mock Setup(Mock m) + { + return m; + } + } +} diff --git a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildSettingsTests.cs b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildSettingsTests.cs index 14992564..b2a96301 100644 --- a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildSettingsTests.cs +++ b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildSettingsTests.cs @@ -991,7 +991,6 @@ public void Should_Throw_If_System_Access_Token_Env_Var_Is_Empty_And_Correct_Bui public void Should_Throw_If_Build_Id_Env_Var_Is_Set_But_Ctor_Build_Id_Value_Zero_With_OAuthToken() { // Given - var creds = new AzureDevOpsNtlmCredentials(); Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); Environment.SetEnvironmentVariable("BUILD_BUILDID", "20"); @@ -1008,7 +1007,6 @@ public void Should_Throw_If_Build_Id_Env_Var_Is_Set_But_Ctor_Build_Id_Value_Zero public void Should_Throw_If_Build_Id_Env_Var_Is_Set_But_Ctor_Build_Id_Value_Negative_With_OAuthToken() { // Given - var creds = new AzureDevOpsNtlmCredentials(); Environment.SetEnvironmentVariable("SYSTEM_TEAMFOUNDATIONCOLLECTIONURI", "https://example.com/collection"); Environment.SetEnvironmentVariable("SYSTEM_TEAMPROJECT", "MyProject"); Environment.SetEnvironmentVariable("BUILD_BUILDID", "20"); diff --git a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs index 999776d5..293cf5ad 100644 --- a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs +++ b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildTests.cs @@ -23,7 +23,8 @@ public void Should_Return_Empty_List_If_Build_Is_Invalid() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(); @@ -42,7 +43,8 @@ public void Should_Return_Empty_List_If_Build_Does_Not_Contain_Test_Runs() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(); @@ -64,7 +66,8 @@ public void Should_Return_List_Of_Test_Runs_With_X_Test_Results_If_X_Is_Less_The fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(testRunsCount); @@ -87,7 +90,8 @@ public void Should_Throw_If_Input_Test_Outcomes_Are_Invalid() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = Record.Exception(() => build.GetTestRuns(null, new string[] { "FakeOutcome" })); @@ -105,7 +109,8 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results() fixture.Log, fixture.Settings, fixture.BuildClientFactory, - fixture.TestManagementClientFactory); + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); // When var result = build.GetTestRuns(); @@ -126,5 +131,29 @@ public void Should_Return_List_Of_Test_Runs_With_Test_Results() new AzureDevOpsTestResult { AutomatedTestName = "t3", Outcome = "Passed", ErrorMessage = string.Empty }); } } + + public sealed class TheGetWorkItemsMethod + { + [Fact] + public void Should_Return_List_Of_WorkItems() + { + // Given + var fixture = new BuildFixture(BuildFixture.ValidAzureDevOpsCollectionUrl, "Foo", 42); + var build = new AzureDevOpsBuild( + fixture.Log, + fixture.Settings, + fixture.BuildClientFactory, + fixture.TestManagementClientFactory, + fixture.WorkItemTrackingClientFactory); + + // When + var result = build.GetWorkItems(); + + // Then + result.ShouldNotBeNull(); + result.ShouldHaveSingleItem(); + result.First().WorkItemId.ShouldBe(42); + } + } } } diff --git a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildsSettingsTests.cs b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildsSettingsTests.cs index aae605cc..52154b4c 100644 --- a/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildsSettingsTests.cs +++ b/src/Cake.AzureDevOps.Tests/Pipelines/AzureDevOpsBuildsSettingsTests.cs @@ -6,7 +6,7 @@ using Shouldly; using Xunit; - public sealed class BaseAzureDevOpsCredentialsSettingsTests + public sealed class AzureDevOpsBuildsSettingsTests { public sealed class TheCtorForProjectGuid { diff --git a/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs b/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs index 54f16707..7d5dd8cd 100644 --- a/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs +++ b/src/Cake.AzureDevOps.Tests/Pipelines/BuildFixture.cs @@ -23,6 +23,8 @@ public BuildFixture(string collectionUrl, string projectName, int buildId) public ITestManagementClientFactory TestManagementClientFactory { get; set; } + public IWorkItemTrackingClientFactory WorkItemTrackingClientFactory { get; set; } + public AzureDevOpsBuildSettings Settings { get; set; } private void InitialzeFakes() @@ -30,6 +32,7 @@ private void InitialzeFakes() this.Log = new FakeLog(); this.BuildClientFactory = new FakeAllSetBuildClientFactory(); this.TestManagementClientFactory = new FakeAllSetTestManagementClientFactory(); + this.WorkItemTrackingClientFactory = new FakeAllSetWorkItemTrackingClientFactory(); } } } diff --git a/src/Cake.AzureDevOps.Tests/Repos/PullRequest/AzureDevOpsPullRequestTests.cs b/src/Cake.AzureDevOps.Tests/Repos/PullRequest/AzureDevOpsPullRequestTests.cs index c04b3adf..08e7eeec 100644 --- a/src/Cake.AzureDevOps.Tests/Repos/PullRequest/AzureDevOpsPullRequestTests.cs +++ b/src/Cake.AzureDevOps.Tests/Repos/PullRequest/AzureDevOpsPullRequestTests.cs @@ -681,6 +681,20 @@ public void Should_Resolve_Comment_Thread() // ?? Nothing to validate here since the method returns void } + [Fact] + public void Should_Resolve_Close_Thread() + { + // Given + var fixture = new PullRequestFixture(BasePullRequestFixture.ValidAzureDevOpsUrl, 21); + var pullRequest = new AzureDevOpsPullRequest(fixture.Log, fixture.Settings, fixture.GitClientFactory); + + // When + pullRequest.CloseCommentThread(123); + + // Then + // ?? Nothing to validate here since the method returns void + } + [Fact] public void Should_Not_Throw_If_Null_Is_Returned() { @@ -1018,8 +1032,10 @@ public void Should_Delete_Comment_By_Comment_Properties() // Given var fixture = new PullRequestFixture(BasePullRequestFixture.ValidAzureDevOpsServerUrl, 100); var pullRequest = new AzureDevOpsPullRequest(fixture.Log, fixture.Settings, fixture.GitClientFactory); - var inComment = new AzureDevOpsComment(new Microsoft.TeamFoundation.SourceControl.WebApi.Comment { Id = 1 }, 5); - inComment.Content = "new Content"; + var inComment = new AzureDevOpsComment(new Microsoft.TeamFoundation.SourceControl.WebApi.Comment { Id = 1 }, 5) + { + Content = "new Content", + }; // When pullRequest.DeleteComment(inComment); @@ -1089,8 +1105,10 @@ public void Should_Return_Updated_Comment() // Given var fixture = new PullRequestFixture(BasePullRequestFixture.ValidAzureDevOpsUrl, 200); var pullRequest = new AzureDevOpsPullRequest(fixture.Log, fixture.Settings, fixture.GitClientFactory); - var inComment = new AzureDevOpsComment(new Microsoft.TeamFoundation.SourceControl.WebApi.Comment { Id = 1 }, 5); - inComment.Content = "new Content"; + var inComment = new AzureDevOpsComment(new Microsoft.TeamFoundation.SourceControl.WebApi.Comment { Id = 1 }, 5) + { + Content = "new Content", + }; // When var outComment = pullRequest.UpdateComment(inComment); diff --git a/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsCommentTests.cs b/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsCommentTests.cs index dd8f05e8..195b5758 100644 --- a/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsCommentTests.cs +++ b/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsCommentTests.cs @@ -57,9 +57,9 @@ public void Should_Return_Empty_Comment() // Then comment.ShouldNotBeNull(); - comment.Content.ShouldBe(default(string)); - comment.IsDeleted.ShouldBe(default(bool)); - comment.CommentType.ShouldBe(default(AzureDevOpsCommentType)); + comment.Content.ShouldBe(default); + comment.IsDeleted.ShouldBe(default); + comment.CommentType.ShouldBe(default); comment.ThreadId.ShouldBe(0); } @@ -73,7 +73,7 @@ public void Should_Return_Valid_Comment_With_Default_Comment_Type() comment.ShouldNotBeNull(); comment.Content.ShouldBe("Hello"); comment.IsDeleted.ShouldBeFalse(); - comment.CommentType.ShouldBe(default(AzureDevOpsCommentType)); + comment.CommentType.ShouldBe(default); comment.ThreadId.ShouldBe(0); } diff --git a/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThreadTests.cs b/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThreadTests.cs index bc13b513..1ed49d07 100644 --- a/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThreadTests.cs +++ b/src/Cake.AzureDevOps.Tests/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThreadTests.cs @@ -32,9 +32,9 @@ public void Should_Return_Empty_Comment_Thread() // Then thread.ShouldNotBeNull(); thread.InnerThread.ShouldBeOfType(typeof(GitPullRequestCommentThread)); - thread.Id.ShouldBe(default(int)); + thread.Id.ShouldBe(default); thread.FilePath.ShouldBeNull(); - thread.Status.ShouldBe(default(AzureDevOpsCommentThreadStatus)); + thread.Status.ShouldBe(default); thread.Properties.ShouldBeNull(); getCommentsResult.IsInvalidOperationException(); @@ -268,7 +268,7 @@ public void Should_Return_Default_Value_If_Property_Collection_Is_Null_For_Strin var result = azureDevOpsThread.GetValue("key"); // Then - result.ShouldBe(default(string)); + result.ShouldBe(default); } [Fact] @@ -281,7 +281,7 @@ public void Should_Return_Default_Value_If_Property_Collection_Is_Null_For_Integ var result = azureDevOpsThread.GetValue("key"); // Then - result.ShouldBe(default(int)); + result.ShouldBe(default); } [Fact] @@ -300,7 +300,7 @@ public void Should_Return_Default_Value_If_Property_Does_Not_Exist_For_String_Va var result = azureDevOpsThread.GetValue("key"); // Then - result.ShouldBe(default(string)); + result.ShouldBe(default); } [Fact] @@ -319,7 +319,7 @@ public void Should_Return_Default_Value_If_Property_Does_Not_Exist_For_Int_Value var result = azureDevOpsThread.GetValue("key"); // Then - result.ShouldBe(default(int)); + result.ShouldBe(default); } [Fact] diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs index 20769ac6..01744f53 100644 --- a/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.Pipelines.cs @@ -1,6 +1,7 @@ namespace Cake.AzureDevOps { using System.Collections.Generic; + using Cake.AzureDevOps.Boards.WorkItemTracking; using Cake.AzureDevOps.Pipelines; using Cake.Core; using Cake.Core.Annotations; @@ -47,7 +48,7 @@ public static AzureDevOpsBuild AzureDevOpsBuild( context.NotNull(nameof(context)); settings.NotNull(nameof(settings)); - var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()); + var build = new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()); if (build.HasBuildLoaded) { @@ -279,7 +280,7 @@ public static bool AzureDevOpsBuildIsFailing( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .IsBuildFailing(); } @@ -327,10 +328,106 @@ public static IEnumerable AzureDevOpsBuildChanges( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetChanges(); } + /// + /// Gets the work item ids associated with an Azure Pipelines build. + /// + /// The context. + /// Settings for getting the build. + /// + /// Get work item ids associated with an Azure Pipelines build: + /// + /// + /// + /// + /// The work item ids associated with the build. + /// Returns an empty list if build could not be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + [CakeMethodAlias] + [CakeAliasCategory("Azure Pipelines")] + [CakeNamespaceImport("Cake.AzureDevOps.Pipelines")] + public static IEnumerable AzureDevOpsBuildWorkItemIds( + this ICakeContext context, + AzureDevOpsBuildSettings settings) + { + context.NotNull(nameof(context)); + settings.NotNull(nameof(settings)); + + return + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) + .GetWorkItemIds(); + } + + /// + /// Gets the work items associated with an Azure Pipelines build. + /// + /// The context. + /// Settings for getting the build. + /// + /// Get work items associated with an Azure Pipelines build: + /// + /// + /// + /// + /// The work items associated with the build. + /// Returns an empty list if build could not be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + [CakeMethodAlias] + [CakeAliasCategory("Azure Pipelines")] + [CakeNamespaceImport("Cake.AzureDevOps.Pipelines")] + public static IEnumerable AzureDevOpsBuildWorkItems( + this ICakeContext context, + AzureDevOpsBuildSettings settings) + { + context.NotNull(nameof(context)); + settings.NotNull(nameof(settings)); + + return + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) + .GetWorkItems(); + } + /// /// Gets the timeline entries for an Azure Pipelines build. /// @@ -375,7 +472,7 @@ public static IEnumerable AzureDevOpsBuildTimelineRec settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetTimelineRecords(); } @@ -423,7 +520,7 @@ public static IEnumerable AzureDevOpsBuildArtifacts( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetArtifacts(); } @@ -471,7 +568,7 @@ public static IEnumerable AzureDevOpsBuildTestRuns( settings.NotNull(nameof(settings)); return - new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + new AzureDevOpsBuild(context.Log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) .GetTestRuns(); } diff --git a/src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs b/src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs new file mode 100644 index 00000000..f78528dd --- /dev/null +++ b/src/Cake.AzureDevOps/AzureDevOpsAliases.WorkItemTracking.cs @@ -0,0 +1,133 @@ +namespace Cake.AzureDevOps +{ + using Cake.AzureDevOps.Boards.WorkItemTracking; + using Cake.Core; + using Cake.Core.Annotations; + + /// + /// Contains functionality related to Azure DevOps work item tracking. + /// + public static partial class AzureDevOpsAliases + { + /// + /// Gets an Azure DevOps work item using the specified settings. + /// + /// The context. + /// Settings for getting the work item. + /// + /// Get a work item from Azure DevOps Server: + /// + /// + /// + /// + /// Description of the work item. + /// Returns null if work item could not be found and + /// is set to false. + /// If work item could not be found and + /// is set to true. + [CakeMethodAlias] + [CakeAliasCategory("Azure Boards")] + [CakeNamespaceImport("Cake.AzureDevOps.Boards.WorkItemTracking")] + public static AzureDevOpsWorkItem AzureDevOpsWorkItem( + this ICakeContext context, + AzureDevOpsWorkItemSettings settings) + { + context.NotNull(nameof(context)); + settings.NotNull(nameof(settings)); + + var workItem = new AzureDevOpsWorkItem(context.Log, settings, new WorkItemTrackingClientFactory()); + + if (workItem.HasWorkItemLoaded) + { + return workItem; + } + + return null; + } + + /// + /// Gets the description of a specific work item the access token provided by Azure Pipelines. + /// Make sure the build has the 'Allow Scripts to access OAuth token' option enabled. + /// + /// The context. + /// ID of the work item. + /// + /// Get an Azure DevOps work item: + /// + /// + /// + /// + /// Description of the work item. + /// Returns null if work item could not be found and + /// is set to false. + /// If work item could not be found and + /// is set to true. + [CakeMethodAlias] + [CakeAliasCategory("Azure Boards")] + [CakeNamespaceImport("Cake.AzureDevOps.Boards.WorkItemTracking")] + public static AzureDevOpsWorkItem AzureDevOpsWorkItemUsingAzurePipelinesOAuthToken( + this ICakeContext context, + int workItemId) + { + context.NotNull(nameof(context)); + workItemId.NotNegativeOrZero(nameof(workItemId)); + + var settings = AzureDevOpsWorkItemSettings.UsingAzurePipelinesOAuthToken(workItemId); + + return AzureDevOpsWorkItem(context, settings); + } + + /// + /// Gets the description of a specific work item the access token provided by Azure Pipelines. + /// Make sure the build has the 'Allow Scripts to access OAuth token' option enabled. + /// + /// The context. + /// ID of the work item. + /// Value indicating whether an exception + /// should be thrown if work item could not be found. + /// + /// Get an Azure DevOps work item. Don't throw exception in case the work item is not found: + /// + /// + /// + /// + /// Description of the work item. + /// Returns null if work item could not be found and + /// is set to false. + /// If work item could not be found and + /// is set to true. + [CakeMethodAlias] + [CakeAliasCategory("Azure Boards")] + [CakeNamespaceImport("Cake.AzureDevOps.Boards.WorkItemTracking")] + public static AzureDevOpsWorkItem AzureDevOpsWorkItemUsingAzurePipelinesOAuthToken( + this ICakeContext context, + int workItemId, + bool throwExceptionIfWorkItemCouldNotBeFound) + { + context.NotNull(nameof(context)); + workItemId.NotNegativeOrZero(nameof(workItemId)); + + var settings = AzureDevOpsWorkItemSettings.UsingAzurePipelinesOAuthToken(workItemId); + settings.ThrowExceptionIfWorkItemCouldNotBeFound = throwExceptionIfWorkItemCouldNotBeFound; + + return AzureDevOpsWorkItem(context, settings); + } + } +} \ No newline at end of file diff --git a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs new file mode 100644 index 00000000..fadfff74 --- /dev/null +++ b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItem.cs @@ -0,0 +1,482 @@ +namespace Cake.AzureDevOps.Boards.WorkItemTracking +{ + using System; + using System.Collections.Generic; + using Cake.Core.Diagnostics; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models; + using Microsoft.VisualStudio.Services.Common; + + /// + /// Class to work with work item tracking. + /// + public sealed class AzureDevOpsWorkItem + { + private readonly ICakeLog log; + private readonly AzureDevOpsWorkItemSettings settings; + private readonly IWorkItemTrackingClientFactory workItemTrackingClientFactory; + private readonly WorkItem workItem; + + /// + /// Initializes a new instance of the class. + /// + /// The Cake log context. + /// Settings for accessing AzureDevOps. + /// If + /// is set to true and no work item could be found. + public AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings) + : this(log, settings, new WorkItemTrackingClientFactory()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The Cake log context. + /// Settings for accessing AzureDevOps. + /// The work item. + /// A factory to communicate with work item tracking client. + internal AzureDevOpsWorkItem(ICakeLog log, AzureDevOpsWorkItemSettings settings, WorkItem workItem, IWorkItemTrackingClientFactory workItemTrackingClientFactory) + { + log.NotNull(nameof(log)); + settings.NotNull(nameof(settings)); + workItem.NotNull(nameof(workItem)); + + this.log = log; + this.workItem = workItem; + this.workItemTrackingClientFactory = workItemTrackingClientFactory; + this.settings = settings; + } + + /// + /// Initializes a new instance of the class. + /// + /// The Cake log context. + /// Settings for accessing AzureDevOps. + /// A factory to communicate with work item tracking client. + /// If + /// is set to true and no work item could be found. + internal AzureDevOpsWorkItem( + ICakeLog log, + AzureDevOpsWorkItemSettings settings, + IWorkItemTrackingClientFactory workItemTrackingClientFactory) + { + log.NotNull(nameof(log)); + settings.NotNull(nameof(settings)); + workItemTrackingClientFactory.NotNull(nameof(workItemTrackingClientFactory)); + + this.log = log; + this.workItemTrackingClientFactory = workItemTrackingClientFactory; + this.settings = settings; + + using (var workItemTrackingClient = this.workItemTrackingClientFactory.CreateWorkItemTrackingClient(settings.CollectionUrl, settings.Credentials, out var authorizedIdenity)) + { + this.log.Verbose( + "Authorized Identity:\n Id: {0}\n DisplayName: {1}", + authorizedIdenity.Id, + authorizedIdenity.DisplayName); + + try + { + if (settings.ProjectGuid != Guid.Empty) + { + this.log.Verbose("Read work item with ID {0} from project with ID {1}", settings.WorkItemId, settings.ProjectGuid); + this.workItem = + workItemTrackingClient + .GetWorkItemAsync( + settings.ProjectGuid, + settings.WorkItemId, + expand: WorkItemExpand.Relations) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + } + else if (!string.IsNullOrWhiteSpace(settings.ProjectName)) + { + this.log.Verbose("Read work item with ID {0} from project with name {1}", settings.WorkItemId, settings.ProjectName); + this.workItem = + workItemTrackingClient + .GetWorkItemAsync( + settings.ProjectName, + settings.WorkItemId, + expand: WorkItemExpand.Relations) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + } + else + { + throw new ArgumentOutOfRangeException( + nameof(settings), + "Either ProjectGuid or ProjectName needs to be set"); + } + } + catch (VssServiceException ex) + { + if (settings.ThrowExceptionIfWorkItemCouldNotBeFound) + { + throw new AzureDevOpsWorkItemNotFoundException("Work item not found", ex); + } + + this.log.Warning("Could not find work item"); + return; + } + } + + if (this.workItem != null) + { + this.log.Verbose( + "Work item information:\n Id: {0}\n Url: {1}", + this.workItem.Id, + this.workItem.Url); + } + } + + /// + /// Gets a value indicating whether a work item has been successfully loaded. + /// + public bool HasWorkItemLoaded => this.workItem != null; + + /// + /// Gets the URL for accessing the web portal of the Azure DevOps collection. + /// + public Uri CollectionUrl => this.settings.CollectionUrl; + + /// + /// Gets the ID of the work item. + /// Returns 0 if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public int WorkItemId + { + get + { + if (!this.ValidateWorkItem()) + { + return 0; + } + + return this.workItem.Id ?? 0; + } + } + + /// + /// Gets the title of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string Title + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.Title"); + } + } + + /// + /// Gets the description of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string Description + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.Description"); + } + } + + /// + /// Gets the area path of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string AreaPath + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.AreaPath"); + } + } + + /// + /// Gets the team project name of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string TeamProject + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.TeamProject"); + } + } + + /// + /// Gets the iteration path of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string IterationPath + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.IterationPath"); + } + } + + /// + /// Gets the type of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string WorkItemType + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.WorkItemType"); + } + } + + /// + /// Gets the state of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string State + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.State"); + } + } + + /// + /// Gets the reason of the state of the work item. + /// Returns empty if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public string Reason + { + get + { + if (!this.ValidateWorkItem()) + { + return string.Empty; + } + + return this.GetField("System.Reason"); + } + } + + /// + /// Gets the create date of the work item. + /// Returns DateTime.MinValue if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public DateTime CreateDate + { + get + { + if (!this.ValidateWorkItem()) + { + return DateTime.MinValue; + } + + return this.GetFieldAsDate("System.CreatedDate"); + } + } + + /// + /// Gets the modify date of the work item. + /// Returns DateTime.MinValue if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public DateTime ChangeDate + { + get + { + if (!this.ValidateWorkItem()) + { + return DateTime.MinValue; + } + + return this.GetFieldAsDate("System.ChangedDate"); + } + } + + /// + /// Gets the tags of the work item. + /// Returns empty list if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public IEnumerable Tags + { + get + { + if (!this.ValidateWorkItem()) + { + return Array.Empty(); + } + + return this.GetField("System.Tags").Split("; ", StringSplitOptions.RemoveEmptyEntries); + } + } + + /// + /// Gets the id of the parent work item. + /// Returns zero if no work item could be found and + /// is set to false. + /// + /// If work item could not be found and + /// is set to true. + public int ParentWorkItemId + { + get + { + if (!this.ValidateWorkItem()) + { + return 0; + } + + return this.GetFieldAsInt("System.Parent"); + } + } + + /// + /// Gets the parent work item or null of no parent exists. + /// + /// The parent work item. + /// If work item could not be found and + /// is set to true. + public AzureDevOpsWorkItem GetParentWorkItem() + { + if (this.ParentWorkItemId > 0) + { + var parentSettings = new AzureDevOpsWorkItemSettings(this.settings, this.ParentWorkItemId); + + return new AzureDevOpsWorkItem(this.log, parentSettings, this.workItemTrackingClientFactory); + } + + return null; + } + + /// + /// Validates if a work item could be found. + /// Depending on + /// the work item instance can be null for subsequent calls. + /// + /// True if a valid work item instance exists. + /// If + /// is set to true and no work item could be found. + private bool ValidateWorkItem() + { + if (this.HasWorkItemLoaded) + { + return true; + } + + if (this.settings.ThrowExceptionIfWorkItemCouldNotBeFound) + { + throw new AzureDevOpsWorkItemNotFoundException("Work item not found"); + } + + this.log.Verbose("Skipping, since no work item instance could be found."); + return false; + } + + private string GetField(string fieldName) + { + if (this.workItem.Fields.TryGetValue(fieldName, out var field)) + { + return field.ToString(); + } + else + { + return string.Empty; + } + } + + private DateTime GetFieldAsDate(string fieldName) + { + if (this.workItem.Fields.TryGetValue(fieldName, out var field)) + { + return (DateTime)field; + } + else + { + return DateTime.MinValue; + } + } + + private int GetFieldAsInt(string fieldName) + { + if (this.workItem.Fields.TryGetValue(fieldName, out var field)) + { + return Convert.ToInt32(field); + } + else + { + return 0; + } + } + } +} diff --git a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemException.cs b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemException.cs new file mode 100644 index 00000000..29df36c0 --- /dev/null +++ b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemException.cs @@ -0,0 +1,52 @@ +namespace Cake.AzureDevOps.Boards.WorkItemTracking +{ + using System; + using System.Runtime.Serialization; + + /// + /// Represents an error for a work item. + /// + [Serializable] + public class AzureDevOpsWorkItemException : AzureDevOpsException + { + /// + /// Initializes a new instance of the class. + /// + public AzureDevOpsWorkItemException() + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public AzureDevOpsWorkItemException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null + /// reference if no inner exception is specified. + public AzureDevOpsWorkItemException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The that holds the serialized object data about + /// the exception being thrown. + /// The that contains contextual information about + /// the source or destination. + protected AzureDevOpsWorkItemException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemNotFoundException.cs b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemNotFoundException.cs new file mode 100644 index 00000000..beb6edc5 --- /dev/null +++ b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemNotFoundException.cs @@ -0,0 +1,61 @@ +namespace Cake.AzureDevOps.Boards.WorkItemTracking +{ + using System; + using System.Runtime.Serialization; + + /// + /// Represents an error if a work item was not found. + /// + [Serializable] + public class AzureDevOpsWorkItemNotFoundException : AzureDevOpsWorkItemException + { + /// + /// Initializes a new instance of the class. + /// + public AzureDevOpsWorkItemNotFoundException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// ID of the work item that was searched. + public AzureDevOpsWorkItemNotFoundException(int workItemId) + : this("Work item with ID " + workItemId + " not found") + { + } + + /// + /// Initializes a new instance of the class with a specified error message. + /// + /// The message that describes the error. + public AzureDevOpsWorkItemNotFoundException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class with a specified error message + /// and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null + /// reference if no inner exception is specified. + public AzureDevOpsWorkItemNotFoundException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Initializes a new instance of the class with serialized data. + /// + /// The that holds the serialized object data about + /// the exception being thrown. + /// The that contains contextual information about + /// the source or destination. + protected AzureDevOpsWorkItemNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } + } +} diff --git a/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemSettings.cs b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemSettings.cs new file mode 100644 index 00000000..c6860114 --- /dev/null +++ b/src/Cake.AzureDevOps/Boards/WorkItemTracking/AzureDevOpsWorkItemSettings.cs @@ -0,0 +1,119 @@ +namespace Cake.AzureDevOps.Boards.WorkItemTracking +{ + using System; + using Cake.AzureDevOps.Authentication; + using Cake.AzureDevOps.Pipelines; + + /// + /// Settings for aliases handling work items. + /// + public class AzureDevOpsWorkItemSettings : BaseAzureDevOpsProjectSettings + { + /// + /// Initializes a new instance of the class. + /// + /// Full URL of the Azure DevOps collection, + /// eg. http://myserver:8080/defaultcollection. + /// ID of the project. + /// ID of the work item. + /// Credentials to use to authenticate against Azure DevOps. + public AzureDevOpsWorkItemSettings(Uri collectionUrl, Guid projectGuid, int workItemId, IAzureDevOpsCredentials credentials) + : base(collectionUrl, projectGuid, credentials) + { + workItemId.NotNegativeOrZero(nameof(workItemId)); + + this.WorkItemId = workItemId; + } + + /// + /// Initializes a new instance of the class. + /// + /// Full URL of the Azure DevOps collection, + /// eg. http://myserver:8080/defaultcollection. + /// Name of the project. + /// ID of the work item. + /// Credentials to use to authenticate against Azure DevOps. + public AzureDevOpsWorkItemSettings(Uri collectionUrl, string projectName, int workItemId, IAzureDevOpsCredentials credentials) + : base(collectionUrl, projectName, credentials) + { + workItemId.NotNegativeOrZero(nameof(workItemId)); + + this.WorkItemId = workItemId; + } + + /// + /// Initializes a new instance of the class + /// based on the instance of a class. + /// + /// Settings containing the parameters. + public AzureDevOpsWorkItemSettings(AzureDevOpsWorkItemSettings settings) + : base(settings) + { + this.WorkItemId = settings.WorkItemId; + this.ThrowExceptionIfWorkItemCouldNotBeFound = settings.ThrowExceptionIfWorkItemCouldNotBeFound; + } + + /// + /// Initializes a new instance of the class + /// based on the instance of a class. + /// + /// Settings containing the parameters. + /// ID of the work item. + public AzureDevOpsWorkItemSettings(AzureDevOpsWorkItemSettings settings, int workItemId) + : base(settings) + { + workItemId.NotNegativeOrZero(nameof(workItemId)); + + this.WorkItemId = workItemId; + this.ThrowExceptionIfWorkItemCouldNotBeFound = settings.ThrowExceptionIfWorkItemCouldNotBeFound; + } + + /// + /// Initializes a new instance of the class using environment variables + /// as set by an Azure Pipelines build. + /// + /// ID of the work item. + /// Credentials to use to authenticate against Azure DevOps. + public AzureDevOpsWorkItemSettings(int workItemId, IAzureDevOpsCredentials credentials) + : base(credentials) + { + workItemId.NotNegativeOrZero(nameof(workItemId)); + + this.WorkItemId = workItemId; + } + + /// + /// Initializes a new instance of the class + /// based on the instance of a class. + /// + /// Settings containing the parameters. + public AzureDevOpsWorkItemSettings(BaseAzureDevOpsProjectSettings settings) + : base(settings) + { + } + + /// + /// Gets the ID of the work item. + /// + public int WorkItemId { get; private set; } + + /// + /// Gets or sets a value indicating whether an exception should be thrown if + /// work item for could not be found. + /// + public bool ThrowExceptionIfWorkItemCouldNotBeFound { get; set; } = true; + + /// + /// Constructs the settings object for a specific work item using the access token provided by Azure Pipelines. + /// + /// ID of the work item. + /// The instance of class. + public static AzureDevOpsWorkItemSettings UsingAzurePipelinesOAuthToken(int workItemId) + { + workItemId.NotNegativeOrZero(nameof(workItemId)); + + var accessToken = EnvironmentVariableHelper.GetSystemAccessToken(); + return new AzureDevOpsWorkItemSettings(workItemId, new AzureDevOpsOAuthCredentials(accessToken)); + } + } +} diff --git a/src/Cake.AzureDevOps/Cake.AzureDevOps.csproj b/src/Cake.AzureDevOps/Cake.AzureDevOps.csproj index 0f54a1f6..6c1e600a 100644 --- a/src/Cake.AzureDevOps/Cake.AzureDevOps.csproj +++ b/src/Cake.AzureDevOps/Cake.AzureDevOps.csproj @@ -22,7 +22,7 @@ MIT git https://github.com/cake-contrib/Cake.AzureDevOps.git - https://github.com/cake-contrib/Cake.AzureDevOps/releases/tag/2.0.0 + https://github.com/cake-contrib/Cake.AzureDevOps/releases/tag/2.1.0 true false $(TargetsForTfmSpecificContentInPackage);PackBuildOutputs @@ -74,6 +74,9 @@ lib\$(TargetFramework)\ + + lib\$(TargetFramework)\ + lib\$(TargetFramework)\ diff --git a/src/Cake.AzureDevOps/IWorkItemTrackingClientFactory.cs b/src/Cake.AzureDevOps/IWorkItemTrackingClientFactory.cs new file mode 100644 index 00000000..41531c29 --- /dev/null +++ b/src/Cake.AzureDevOps/IWorkItemTrackingClientFactory.cs @@ -0,0 +1,30 @@ +namespace Cake.AzureDevOps +{ + using System; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + using Microsoft.VisualStudio.Services.Identity; + + /// + /// The interface for a work item tracking client factory. + /// + internal interface IWorkItemTrackingClientFactory + { + /// + /// Creates the instance of the class. + /// + /// The URL of the Azure DevOps team project collection. + /// The credentials to connect to Azure DevOps. + /// The instance of class. + WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials); + + /// + /// Creates the instance of the class. + /// + /// The URL of the Azure DevOps team project collection. + /// The credentials to connect to Azure DevOps. + /// Returns identity which is authorized. + /// The instance of class. + WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity); + } +} diff --git a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs index 83979974..a8745ce1 100644 --- a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs +++ b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuild.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; - using Cake.AzureDevOps.Authentication; + using Cake.AzureDevOps.Boards.WorkItemTracking; using Cake.Core.Diagnostics; using Microsoft.TeamFoundation.Build.WebApi; using Microsoft.TeamFoundation.TestManagement.WebApi; @@ -15,10 +15,11 @@ public sealed class AzureDevOpsBuild { private readonly ICakeLog log; - private readonly IAzureDevOpsCredentials credentials; + private readonly BaseAzureDevOpsProjectSettings settings; private readonly bool throwExceptionIfBuildCouldNotBeFound; private readonly IBuildClientFactory buildClientFactory; private readonly ITestManagementClientFactory testClientFactory; + private readonly IWorkItemTrackingClientFactory workItemTrackingClientFactory; private readonly Build build; /// @@ -29,7 +30,7 @@ public sealed class AzureDevOpsBuild /// If /// is set to true and no build could be found. public AzureDevOpsBuild(ICakeLog log, AzureDevOpsBuildSettings settings) - : this(log, settings, new BuildClientFactory(), new TestManagementClientFactory()) + : this(log, settings, new BuildClientFactory(), new TestManagementClientFactory(), new WorkItemTrackingClientFactory()) { } @@ -49,8 +50,8 @@ internal AzureDevOpsBuild(ICakeLog log, AzureDevOpsBuildsSettings settings, Buil this.build = build; this.buildClientFactory = new BuildClientFactory(); this.testClientFactory = new TestManagementClientFactory(); - this.credentials = settings.Credentials; - this.CollectionUrl = settings.CollectionUrl; + this.workItemTrackingClientFactory = new WorkItemTrackingClientFactory(); + this.settings = settings; } /// @@ -60,24 +61,27 @@ internal AzureDevOpsBuild(ICakeLog log, AzureDevOpsBuildsSettings settings, Buil /// Settings for accessing AzureDevOps. /// A factory to communicate with build client. /// A factory to communicate with test management client. + /// A factory to communicate with work item tracking client. /// If /// is set to true and no build could be found. internal AzureDevOpsBuild( ICakeLog log, AzureDevOpsBuildSettings settings, IBuildClientFactory buildClientFactory, - ITestManagementClientFactory testManagementClientFactory) + ITestManagementClientFactory testManagementClientFactory, + IWorkItemTrackingClientFactory workItemTrackingClientFactory) { log.NotNull(nameof(log)); settings.NotNull(nameof(settings)); buildClientFactory.NotNull(nameof(buildClientFactory)); testManagementClientFactory.NotNull(nameof(testManagementClientFactory)); + workItemTrackingClientFactory.NotNull(nameof(workItemTrackingClientFactory)); this.log = log; this.buildClientFactory = buildClientFactory; this.testClientFactory = testManagementClientFactory; - this.credentials = settings.Credentials; - this.CollectionUrl = settings.CollectionUrl; + this.workItemTrackingClientFactory = workItemTrackingClientFactory; + this.settings = settings; this.throwExceptionIfBuildCouldNotBeFound = settings.ThrowExceptionIfBuildCouldNotBeFound; using (var buildClient = this.buildClientFactory.CreateBuildClient(settings.CollectionUrl, settings.Credentials, out var authorizedIdenity)) @@ -149,7 +153,7 @@ internal AzureDevOpsBuild( /// /// Gets the URL for accessing the web portal of the Azure DevOps collection. /// - public Uri CollectionUrl { get; } + public Uri CollectionUrl => this.settings.CollectionUrl; /// /// Gets the id of the Azure DevOps project. @@ -357,7 +361,7 @@ public IEnumerable GetChanges() return new List(); } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { return buildClient @@ -369,6 +373,72 @@ public IEnumerable GetChanges() } } + /// + /// Gets the work item ids associated with a build. + /// + /// The work item ids associated with a build or an empty list if no build could be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + public IEnumerable GetWorkItemIds() + { + if (!this.ValidateBuild()) + { + return Array.Empty(); + } + + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) + { + Task> task; + if (this.ProjectId != Guid.Empty) + { + task = buildClient.GetBuildWorkItemsRefsAsync(this.ProjectId, this.BuildId); + } + else if (!string.IsNullOrWhiteSpace(this.ProjectName)) + { + task = buildClient.GetBuildWorkItemsRefsAsync(this.ProjectName, this.BuildId); + } + else + { + throw new InvalidOperationException("Either ProjectId or ProjectName needs to be set"); + } + + return task + .ConfigureAwait(false) + .GetAwaiter() + .GetResult() + .Select(r => int.Parse(r.Id)); + } + } + + /// + /// Gets the work items associated with a build. + /// + /// The work items associated with a build or an empty list if no build could be found and + /// is set to false. + /// If build could not be found and + /// is set to true. + public IEnumerable GetWorkItems() + { + if (!this.ValidateBuild()) + { + return Array.Empty(); + } + + var workItemIds = this.GetWorkItemIds(); + + using (var workItemTrackingClient = this.workItemTrackingClientFactory.CreateWorkItemTrackingClient(this.settings.CollectionUrl, this.settings.Credentials)) + { + return + workItemTrackingClient + .GetWorkItemsAsync(workItemIds, expand: Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models.WorkItemExpand.Relations) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult() + .Select(x => new AzureDevOpsWorkItem(this.log, new AzureDevOpsWorkItemSettings(this.settings), x, this.workItemTrackingClientFactory)); + } + } + /// /// Checks if the build is failing. /// @@ -396,7 +466,7 @@ public IEnumerable GetTimelineRecords() return new List(); } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { return buildClient @@ -423,7 +493,7 @@ public IEnumerable GetArtifacts() return new List(); } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { return buildClient @@ -476,7 +546,7 @@ public AzureDevOpsBuildArtifact LinkArtifact(string name, string type, string lo return null; } - using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.credentials)) + using (var buildClient = this.buildClientFactory.CreateBuildClient(this.CollectionUrl, this.settings.Credentials)) { var artifact = new BuildArtifact @@ -543,7 +613,7 @@ public IEnumerable GetTestRuns(int? maxResultsPerTestRun, IE return new List(); } - using (var testClient = this.testClientFactory.CreateTestManagementClient(this.CollectionUrl, this.credentials)) + using (var testClient = this.testClientFactory.CreateTestManagementClient(this.CollectionUrl, this.settings.Credentials)) { // Read test result details for current build. var testResultDetails = diff --git a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuildNotFoundException.cs b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuildNotFoundException.cs index e1fb2371..c351dd80 100644 --- a/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuildNotFoundException.cs +++ b/src/Cake.AzureDevOps/Pipelines/AzureDevOpsBuildNotFoundException.cs @@ -16,16 +16,6 @@ public AzureDevOpsBuildNotFoundException() { } - /// - /// Initializes a new instance of the class. - /// - /// ID of the repository where the pull request was searched. - /// ID of the pull request which could not be found. - public AzureDevOpsBuildNotFoundException(Guid repositoryId, int pullRequestId) - : this("Pull request with ID " + pullRequestId + " not found in repository with GUID " + repositoryId) - { - } - /// /// Initializes a new instance of the class with a specified error message. /// diff --git a/src/Cake.AzureDevOps/Repos/PullRequest/AzureDevOpsPullRequest.cs b/src/Cake.AzureDevOps/Repos/PullRequest/AzureDevOpsPullRequest.cs index 75f54306..f2de8ed4 100644 --- a/src/Cake.AzureDevOps/Repos/PullRequest/AzureDevOpsPullRequest.cs +++ b/src/Cake.AzureDevOps/Repos/PullRequest/AzureDevOpsPullRequest.cs @@ -513,22 +513,26 @@ public IEnumerable GetModifiedFiles() .GetAwaiter() .GetResult(); - this.log.Verbose( - "Found {0} changed file(s) in the pull request", - commitDiffs.Changes.Count()); - if (!commitDiffs.ChangeCounts.Any()) { + this.log.Verbose("Found 0 changed files in the pull request"); + return new List(); } - return + var changes = from change in commitDiffs.Changes where change != null && !change.Item.IsFolder select new FilePath(change.Item.Path.TrimStart('/')); + + this.log.Verbose( + "Found {0} changed file(s) in the pull request", + changes.Count()); + + return changes; } } @@ -573,6 +577,15 @@ public void ActivateCommentThread(int threadId) this.SetCommentThreadStatus(threadId, CommentThreadStatus.Active); } + /// + /// Sets the pull request comment thread to . + /// + /// The Id of the comment thread. + public void CloseCommentThread(int threadId) + { + this.SetCommentThreadStatus(threadId, CommentThreadStatus.Closed); + } + /// /// Creates a new comment thread with a single comment in the pull request. /// diff --git a/src/Cake.AzureDevOps/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThread.cs b/src/Cake.AzureDevOps/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThread.cs index 2b65a54b..05322ce1 100644 --- a/src/Cake.AzureDevOps/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThread.cs +++ b/src/Cake.AzureDevOps/Repos/PullRequest/CommentThread/AzureDevOpsPullRequestCommentThread.cs @@ -180,7 +180,7 @@ public T GetValue(string propertyName) if (this.thread.Properties == null) { - return default(T); + return default; } return this.thread.Properties.GetValue(propertyName, default(T)); diff --git a/src/Cake.AzureDevOps/WorkItemTrackingClientFactory.cs b/src/Cake.AzureDevOps/WorkItemTrackingClientFactory.cs new file mode 100644 index 00000000..20962b9a --- /dev/null +++ b/src/Cake.AzureDevOps/WorkItemTrackingClientFactory.cs @@ -0,0 +1,36 @@ +namespace Cake.AzureDevOps +{ + using System; + using Cake.AzureDevOps.Authentication; + using Microsoft.TeamFoundation.Build.WebApi; + using Microsoft.TeamFoundation.WorkItemTracking.WebApi; + using Microsoft.VisualStudio.Services.Identity; + using Microsoft.VisualStudio.Services.WebApi; + + /// + internal class WorkItemTrackingClientFactory : IWorkItemTrackingClientFactory + { + /// + public WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials) + { + return this.CreateWorkItemTrackingClient(collectionUrl, credentials, out _); + } + + /// + public WorkItemTrackingHttpClient CreateWorkItemTrackingClient(Uri collectionUrl, IAzureDevOpsCredentials credentials, out Identity authorizedIdentity) + { + var connection = + new VssConnection(collectionUrl, credentials.ToVssCredentials()); + + authorizedIdentity = connection.AuthorizedIdentity; + + var workItemTrackingClient = connection.GetClient(); + if (workItemTrackingClient == null) + { + throw new AzureDevOpsException("Could not retrieve the WorkItemTrackingHttpClient object"); + } + + return workItemTrackingClient; + } + } +} diff --git a/tests/frosting/net5.0/build.ps1 b/tests/frosting/net5.0/build.ps1 new file mode 100644 index 00000000..c56e9b94 --- /dev/null +++ b/tests/frosting/net5.0/build.ps1 @@ -0,0 +1,13 @@ +$ADDIN_PACKAGE_PATH = "packages/cake.azuredevops" +if (Test-Path $ADDIN_PACKAGE_PATH) +{ + Write-Host "Cleaning up cached version of $ADDIN_PACKAGE_PATH..." + Remove-Item $ADDIN_PACKAGE_PATH -Recurse; +} +else +{ + Write-Host "$ADDIN_PACKAGE_PATH not cached..." +} + +dotnet run --project build/Build.csproj -- $args +exit $LASTEXITCODE; \ No newline at end of file diff --git a/tests/frosting/net5.0/build.sh b/tests/frosting/net5.0/build.sh new file mode 100755 index 00000000..8d8f53fa --- /dev/null +++ b/tests/frosting/net5.0/build.sh @@ -0,0 +1,10 @@ +$ADDIN_PACKAGE_PATH = "packages/cake.azuredevops" +if [ -d "$ADDIN_PACKAGE_PATH" ] +then + echo "Cleaning up cached version of $ADDIN_PACKAGE_PATH..." + rm -Rf $ADDIN_PACKAGE_PATH +else + echo "$ADDIN_PACKAGE_PATH not cached..." +fi + +dotnet run --project ./build/Build.csproj -- "$@" \ No newline at end of file diff --git a/tests/frosting/net5.0/build/Build.csproj b/tests/frosting/net5.0/build/Build.csproj new file mode 100644 index 00000000..9bdf5388 --- /dev/null +++ b/tests/frosting/net5.0/build/Build.csproj @@ -0,0 +1,11 @@ + + + Exe + net5.0 + $(MSBuildProjectDirectory) + + + + + + \ No newline at end of file diff --git a/tests/frosting/net5.0/build/Program.cs b/tests/frosting/net5.0/build/Program.cs new file mode 100644 index 00000000..61cd2527 --- /dev/null +++ b/tests/frosting/net5.0/build/Program.cs @@ -0,0 +1,157 @@ +using Cake.Common.Build; +using Cake.Common.Diagnostics; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Frosting; +using Cake.AzureDevOps; +using System.Linq; + +public static class Program +{ + public static int Main(string[] args) + { + return new CakeHost() + .UseContext() + .Run(args); + } +} + +public class BuildContext : FrostingContext +{ + public BuildContext(ICakeContext context) + : base(context) + { + } +} + +[TaskName("Read-Build")] +public sealed class ReadBuildTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + context.Information(build.BuildId); + } +} + +[TaskName("Read-BuildChanges")] +public sealed class ReadBuildChangesTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var changes = build.GetChanges(); + if (!changes.Any()) + { + context.Information("No changes found."); + } + else + { + foreach (var change in changes) + { + context.Information("{0}: {1} by {2}", change.Id, change.Message, change.Author); + } + } + } +} + +[TaskName("Read-BuildTimelineRecords")] +public sealed class ReadBuildTimelineRecordsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var timelineRecords = build.GetTimelineRecords(); + if (!timelineRecords.Any()) + { + context.Information("No timeline records found."); + } + else + { + foreach (var timelineRecord in timelineRecords) + { + context.Information("{0}: {1}", timelineRecord.Id, timelineRecord.Name); + } + } + } +} + +[TaskName("Read-BuildArtifacts")] +public sealed class ReadBuildArtifactsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + // var build = + // context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var artifacts = build.GetArtifacts(); + // if (!artifacts.Any()) + // { + // context.Information("No artifacts found."); + // } + // else + // { + // foreach (var artifact in artifacts) + // { + // context.Information("{0}: {1}", artifact.Id, artifact.Name); + // } + // } + } +} + +[TaskName("Read-BuildTestRuns")] +public sealed class ReadBuildTestRunsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + // var build = + // context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var testRuns = build.GetTestRuns(); + // if (!testRuns.Any()) + // { + // context.Information("No test runs found."); + // } + // else + // { + // foreach (var testRun in testRuns) + // { + // context.Information("{0}", testRun.RunId); + // } + // } + } +} + +[TaskName("Read-PullRequest")] +public sealed class ReadPullRequestTask : FrostingTask +{ + public override bool ShouldRun(BuildContext context) + { + return context.BuildSystem().IsPullRequest; + } + + public override void Run(BuildContext context) + { + // var pullRequest = + // context.AzureDevOpsPullRequestUsingAzurePipelinesOAuthToken(); + + // context.Information(pullRequest.TargetRefName); + } +} + +[TaskName("Default")] +[IsDependentOn(typeof(ReadBuildTask))] +[IsDependentOn(typeof(ReadBuildChangesTask))] +[IsDependentOn(typeof(ReadBuildTimelineRecordsTask))] +[IsDependentOn(typeof(ReadBuildArtifactsTask))] +[IsDependentOn(typeof(ReadBuildTestRunsTask))] +[IsDependentOn(typeof(ReadPullRequestTask))] +public class DefaultTask : FrostingTask +{ +} diff --git a/tests/frosting/net5.0/global.json b/tests/frosting/net5.0/global.json new file mode 100644 index 00000000..f3e48523 --- /dev/null +++ b/tests/frosting/net5.0/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "allowPrerelease": true, + "version": "5.0.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/tests/frosting/net5.0/nuget.config b/tests/frosting/net5.0/nuget.config new file mode 100644 index 00000000..663b555f --- /dev/null +++ b/tests/frosting/net5.0/nuget.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/frosting/net6.0/build.ps1 b/tests/frosting/net6.0/build.ps1 new file mode 100644 index 00000000..c56e9b94 --- /dev/null +++ b/tests/frosting/net6.0/build.ps1 @@ -0,0 +1,13 @@ +$ADDIN_PACKAGE_PATH = "packages/cake.azuredevops" +if (Test-Path $ADDIN_PACKAGE_PATH) +{ + Write-Host "Cleaning up cached version of $ADDIN_PACKAGE_PATH..." + Remove-Item $ADDIN_PACKAGE_PATH -Recurse; +} +else +{ + Write-Host "$ADDIN_PACKAGE_PATH not cached..." +} + +dotnet run --project build/Build.csproj -- $args +exit $LASTEXITCODE; \ No newline at end of file diff --git a/tests/frosting/net6.0/build.sh b/tests/frosting/net6.0/build.sh new file mode 100755 index 00000000..8d8f53fa --- /dev/null +++ b/tests/frosting/net6.0/build.sh @@ -0,0 +1,10 @@ +$ADDIN_PACKAGE_PATH = "packages/cake.azuredevops" +if [ -d "$ADDIN_PACKAGE_PATH" ] +then + echo "Cleaning up cached version of $ADDIN_PACKAGE_PATH..." + rm -Rf $ADDIN_PACKAGE_PATH +else + echo "$ADDIN_PACKAGE_PATH not cached..." +fi + +dotnet run --project ./build/Build.csproj -- "$@" \ No newline at end of file diff --git a/tests/frosting/net6.0/build/Build.csproj b/tests/frosting/net6.0/build/Build.csproj new file mode 100644 index 00000000..44b95893 --- /dev/null +++ b/tests/frosting/net6.0/build/Build.csproj @@ -0,0 +1,11 @@ + + + Exe + net6.0 + $(MSBuildProjectDirectory) + + + + + + \ No newline at end of file diff --git a/tests/frosting/net6.0/build/Program.cs b/tests/frosting/net6.0/build/Program.cs new file mode 100644 index 00000000..61cd2527 --- /dev/null +++ b/tests/frosting/net6.0/build/Program.cs @@ -0,0 +1,157 @@ +using Cake.Common.Build; +using Cake.Common.Diagnostics; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Frosting; +using Cake.AzureDevOps; +using System.Linq; + +public static class Program +{ + public static int Main(string[] args) + { + return new CakeHost() + .UseContext() + .Run(args); + } +} + +public class BuildContext : FrostingContext +{ + public BuildContext(ICakeContext context) + : base(context) + { + } +} + +[TaskName("Read-Build")] +public sealed class ReadBuildTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + context.Information(build.BuildId); + } +} + +[TaskName("Read-BuildChanges")] +public sealed class ReadBuildChangesTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var changes = build.GetChanges(); + if (!changes.Any()) + { + context.Information("No changes found."); + } + else + { + foreach (var change in changes) + { + context.Information("{0}: {1} by {2}", change.Id, change.Message, change.Author); + } + } + } +} + +[TaskName("Read-BuildTimelineRecords")] +public sealed class ReadBuildTimelineRecordsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var timelineRecords = build.GetTimelineRecords(); + if (!timelineRecords.Any()) + { + context.Information("No timeline records found."); + } + else + { + foreach (var timelineRecord in timelineRecords) + { + context.Information("{0}: {1}", timelineRecord.Id, timelineRecord.Name); + } + } + } +} + +[TaskName("Read-BuildArtifacts")] +public sealed class ReadBuildArtifactsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + // var build = + // context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var artifacts = build.GetArtifacts(); + // if (!artifacts.Any()) + // { + // context.Information("No artifacts found."); + // } + // else + // { + // foreach (var artifact in artifacts) + // { + // context.Information("{0}: {1}", artifact.Id, artifact.Name); + // } + // } + } +} + +[TaskName("Read-BuildTestRuns")] +public sealed class ReadBuildTestRunsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + // var build = + // context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var testRuns = build.GetTestRuns(); + // if (!testRuns.Any()) + // { + // context.Information("No test runs found."); + // } + // else + // { + // foreach (var testRun in testRuns) + // { + // context.Information("{0}", testRun.RunId); + // } + // } + } +} + +[TaskName("Read-PullRequest")] +public sealed class ReadPullRequestTask : FrostingTask +{ + public override bool ShouldRun(BuildContext context) + { + return context.BuildSystem().IsPullRequest; + } + + public override void Run(BuildContext context) + { + // var pullRequest = + // context.AzureDevOpsPullRequestUsingAzurePipelinesOAuthToken(); + + // context.Information(pullRequest.TargetRefName); + } +} + +[TaskName("Default")] +[IsDependentOn(typeof(ReadBuildTask))] +[IsDependentOn(typeof(ReadBuildChangesTask))] +[IsDependentOn(typeof(ReadBuildTimelineRecordsTask))] +[IsDependentOn(typeof(ReadBuildArtifactsTask))] +[IsDependentOn(typeof(ReadBuildTestRunsTask))] +[IsDependentOn(typeof(ReadPullRequestTask))] +public class DefaultTask : FrostingTask +{ +} diff --git a/tests/frosting/net6.0/global.json b/tests/frosting/net6.0/global.json new file mode 100644 index 00000000..c31f98b7 --- /dev/null +++ b/tests/frosting/net6.0/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "allowPrerelease": true, + "version": "6.0.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/tests/frosting/net6.0/nuget.config b/tests/frosting/net6.0/nuget.config new file mode 100644 index 00000000..663b555f --- /dev/null +++ b/tests/frosting/net6.0/nuget.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/frosting/netcoreapp3.1/build.ps1 b/tests/frosting/netcoreapp3.1/build.ps1 new file mode 100644 index 00000000..c56e9b94 --- /dev/null +++ b/tests/frosting/netcoreapp3.1/build.ps1 @@ -0,0 +1,13 @@ +$ADDIN_PACKAGE_PATH = "packages/cake.azuredevops" +if (Test-Path $ADDIN_PACKAGE_PATH) +{ + Write-Host "Cleaning up cached version of $ADDIN_PACKAGE_PATH..." + Remove-Item $ADDIN_PACKAGE_PATH -Recurse; +} +else +{ + Write-Host "$ADDIN_PACKAGE_PATH not cached..." +} + +dotnet run --project build/Build.csproj -- $args +exit $LASTEXITCODE; \ No newline at end of file diff --git a/tests/frosting/netcoreapp3.1/build.sh b/tests/frosting/netcoreapp3.1/build.sh new file mode 100755 index 00000000..8d8f53fa --- /dev/null +++ b/tests/frosting/netcoreapp3.1/build.sh @@ -0,0 +1,10 @@ +$ADDIN_PACKAGE_PATH = "packages/cake.azuredevops" +if [ -d "$ADDIN_PACKAGE_PATH" ] +then + echo "Cleaning up cached version of $ADDIN_PACKAGE_PATH..." + rm -Rf $ADDIN_PACKAGE_PATH +else + echo "$ADDIN_PACKAGE_PATH not cached..." +fi + +dotnet run --project ./build/Build.csproj -- "$@" \ No newline at end of file diff --git a/tests/frosting/netcoreapp3.1/build/Build.csproj b/tests/frosting/netcoreapp3.1/build/Build.csproj new file mode 100644 index 00000000..240b8b43 --- /dev/null +++ b/tests/frosting/netcoreapp3.1/build/Build.csproj @@ -0,0 +1,11 @@ + + + Exe + netcoreapp3.1 + $(MSBuildProjectDirectory) + + + + + + \ No newline at end of file diff --git a/tests/frosting/netcoreapp3.1/build/Program.cs b/tests/frosting/netcoreapp3.1/build/Program.cs new file mode 100644 index 00000000..61cd2527 --- /dev/null +++ b/tests/frosting/netcoreapp3.1/build/Program.cs @@ -0,0 +1,157 @@ +using Cake.Common.Build; +using Cake.Common.Diagnostics; +using Cake.Core; +using Cake.Core.Diagnostics; +using Cake.Frosting; +using Cake.AzureDevOps; +using System.Linq; + +public static class Program +{ + public static int Main(string[] args) + { + return new CakeHost() + .UseContext() + .Run(args); + } +} + +public class BuildContext : FrostingContext +{ + public BuildContext(ICakeContext context) + : base(context) + { + } +} + +[TaskName("Read-Build")] +public sealed class ReadBuildTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + context.Information(build.BuildId); + } +} + +[TaskName("Read-BuildChanges")] +public sealed class ReadBuildChangesTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var changes = build.GetChanges(); + if (!changes.Any()) + { + context.Information("No changes found."); + } + else + { + foreach (var change in changes) + { + context.Information("{0}: {1} by {2}", change.Id, change.Message, change.Author); + } + } + } +} + +[TaskName("Read-BuildTimelineRecords")] +public sealed class ReadBuildTimelineRecordsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + var build = + context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var timelineRecords = build.GetTimelineRecords(); + if (!timelineRecords.Any()) + { + context.Information("No timeline records found."); + } + else + { + foreach (var timelineRecord in timelineRecords) + { + context.Information("{0}: {1}", timelineRecord.Id, timelineRecord.Name); + } + } + } +} + +[TaskName("Read-BuildArtifacts")] +public sealed class ReadBuildArtifactsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + // var build = + // context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var artifacts = build.GetArtifacts(); + // if (!artifacts.Any()) + // { + // context.Information("No artifacts found."); + // } + // else + // { + // foreach (var artifact in artifacts) + // { + // context.Information("{0}: {1}", artifact.Id, artifact.Name); + // } + // } + } +} + +[TaskName("Read-BuildTestRuns")] +public sealed class ReadBuildTestRunsTask : FrostingTask +{ + public override void Run(BuildContext context) + { + // var build = + // context.AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var testRuns = build.GetTestRuns(); + // if (!testRuns.Any()) + // { + // context.Information("No test runs found."); + // } + // else + // { + // foreach (var testRun in testRuns) + // { + // context.Information("{0}", testRun.RunId); + // } + // } + } +} + +[TaskName("Read-PullRequest")] +public sealed class ReadPullRequestTask : FrostingTask +{ + public override bool ShouldRun(BuildContext context) + { + return context.BuildSystem().IsPullRequest; + } + + public override void Run(BuildContext context) + { + // var pullRequest = + // context.AzureDevOpsPullRequestUsingAzurePipelinesOAuthToken(); + + // context.Information(pullRequest.TargetRefName); + } +} + +[TaskName("Default")] +[IsDependentOn(typeof(ReadBuildTask))] +[IsDependentOn(typeof(ReadBuildChangesTask))] +[IsDependentOn(typeof(ReadBuildTimelineRecordsTask))] +[IsDependentOn(typeof(ReadBuildArtifactsTask))] +[IsDependentOn(typeof(ReadBuildTestRunsTask))] +[IsDependentOn(typeof(ReadPullRequestTask))] +public class DefaultTask : FrostingTask +{ +} diff --git a/tests/frosting/netcoreapp3.1/global.json b/tests/frosting/netcoreapp3.1/global.json new file mode 100644 index 00000000..9fa4eda5 --- /dev/null +++ b/tests/frosting/netcoreapp3.1/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "allowPrerelease": true, + "version": "3.1.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/tests/frosting/netcoreapp3.1/nuget.config b/tests/frosting/netcoreapp3.1/nuget.config new file mode 100644 index 00000000..663b555f --- /dev/null +++ b/tests/frosting/netcoreapp3.1/nuget.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/script-runner/net5.0/.config/dotnet-tools.json b/tests/script-runner/net5.0/.config/dotnet-tools.json new file mode 100644 index 00000000..db6b2cbc --- /dev/null +++ b/tests/script-runner/net5.0/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "2.0.0", + "commands": [ + "dotnet-cake" + ] + } + } + } \ No newline at end of file diff --git a/tests/script-runner/net5.0/build.cake b/tests/script-runner/net5.0/build.cake new file mode 100644 index 00000000..20015a30 --- /dev/null +++ b/tests/script-runner/net5.0/build.cake @@ -0,0 +1,120 @@ +#addin nuget:?package=Cake.AzureDevOps&prerelease + +////////////////////////////////////////////////// +// ARGUMENTS +////////////////////////////////////////////////// + +var target = Argument("target", "Default"); + +////////////////////////////////////////////////// +// TARGETS +////////////////////////////////////////////////// + +Task("Read-Build") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + Information(build.BuildId); +}); + +Task("Read-BuildChanges") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var changes = build.GetChanges(); + if (!changes.Any()) + { + Information("No changes found."); + } + else + { + foreach (var change in changes) + { + Information("{0}: {1} by {2}", change.Id, change.Message, change.Author); + } + } +}); + +Task("Read-BuildTimelineRecords") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var timelineRecords = build.GetTimelineRecords(); + if (!timelineRecords.Any()) + { + Information("No timeline records found."); + } + else + { + foreach (var timelineRecord in timelineRecords) + { + Information("{0}: {1}", timelineRecord.Id, timelineRecord.Name); + } + } +}); + +Task("Read-BuildArtifacts") + .Does(() => +{ + // var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var artifacts = build.GetArtifacts(); + // if (!artifacts.Any()) + // { + // Information("No artifacts found."); + // } + // else + // { + // foreach (var artifact in artifacts) + // { + // Information("{0}: {1}", artifact.Id, artifact.Name); + // } + // } +}); + +Task("Read-BuildTestRuns") + .Does(() => +{ + // var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var testRuns = build.GetTestRuns(); + // if (!testRuns.Any()) + // { + // Information("No test runs found."); + // } + // else + // { + // foreach (var testRun in testRuns) + // { + // Information("{0}", testRun.RunId); + // } + // } +}); + +Task("Read-PullRequest") + .WithCriteria((context) => context.BuildSystem().IsPullRequest, "Only supported for pull request builds.") + .Does(() => +{ + // var pullRequest = + // AzureDevOpsPullRequestUsingAzurePipelinesOAuthToken(); + + // Information(pullRequest.TargetRefName); +}); + + +Task("Default") + .IsDependentOn("Read-Build") + .IsDependentOn("Read-BuildChanges") + .IsDependentOn("Read-BuildTimelineRecords") + .IsDependentOn("Read-BuildArtifacts") + .IsDependentOn("Read-BuildTestRuns") + .IsDependentOn("Read-PullRequest"); + +////////////////////////////////////////////////// +// EXECUTION +////////////////////////////////////////////////// + +RunTarget(target); \ No newline at end of file diff --git a/tests/script-runner/net5.0/build.ps1 b/tests/script-runner/net5.0/build.ps1 new file mode 100644 index 00000000..fe602768 --- /dev/null +++ b/tests/script-runner/net5.0/build.ps1 @@ -0,0 +1,15 @@ +$ErrorActionPreference = 'Stop' + +$SCRIPT_NAME = "build.cake" + +Write-Host "Restoring .NET Core tools" +dotnet tool restore +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "Bootstrapping Cake" +dotnet cake $SCRIPT_NAME --bootstrap +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "Running Build" +dotnet cake $SCRIPT_NAME @args +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } \ No newline at end of file diff --git a/tests/script-runner/net5.0/build.sh b/tests/script-runner/net5.0/build.sh new file mode 100755 index 00000000..921a3241 --- /dev/null +++ b/tests/script-runner/net5.0/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +SCRIPT_NAME="build.cake" + +echo "Restoring .NET Core tools" +dotnet tool restore + +echo "Bootstrapping Cake" +dotnet cake $SCRIPT_NAME --bootstrap + +echo "Running Build" +dotnet cake $SCRIPT_NAME "$@" \ No newline at end of file diff --git a/tests/script-runner/net5.0/global.json b/tests/script-runner/net5.0/global.json new file mode 100644 index 00000000..f3e48523 --- /dev/null +++ b/tests/script-runner/net5.0/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "allowPrerelease": true, + "version": "5.0.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/tests/script-runner/net5.0/nuget.config b/tests/script-runner/net5.0/nuget.config new file mode 100644 index 00000000..62768c3d --- /dev/null +++ b/tests/script-runner/net5.0/nuget.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/script-runner/net6.0/.config/dotnet-tools.json b/tests/script-runner/net6.0/.config/dotnet-tools.json new file mode 100644 index 00000000..db6b2cbc --- /dev/null +++ b/tests/script-runner/net6.0/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "2.0.0", + "commands": [ + "dotnet-cake" + ] + } + } + } \ No newline at end of file diff --git a/tests/script-runner/net6.0/build.cake b/tests/script-runner/net6.0/build.cake new file mode 100644 index 00000000..20015a30 --- /dev/null +++ b/tests/script-runner/net6.0/build.cake @@ -0,0 +1,120 @@ +#addin nuget:?package=Cake.AzureDevOps&prerelease + +////////////////////////////////////////////////// +// ARGUMENTS +////////////////////////////////////////////////// + +var target = Argument("target", "Default"); + +////////////////////////////////////////////////// +// TARGETS +////////////////////////////////////////////////// + +Task("Read-Build") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + Information(build.BuildId); +}); + +Task("Read-BuildChanges") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var changes = build.GetChanges(); + if (!changes.Any()) + { + Information("No changes found."); + } + else + { + foreach (var change in changes) + { + Information("{0}: {1} by {2}", change.Id, change.Message, change.Author); + } + } +}); + +Task("Read-BuildTimelineRecords") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var timelineRecords = build.GetTimelineRecords(); + if (!timelineRecords.Any()) + { + Information("No timeline records found."); + } + else + { + foreach (var timelineRecord in timelineRecords) + { + Information("{0}: {1}", timelineRecord.Id, timelineRecord.Name); + } + } +}); + +Task("Read-BuildArtifacts") + .Does(() => +{ + // var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var artifacts = build.GetArtifacts(); + // if (!artifacts.Any()) + // { + // Information("No artifacts found."); + // } + // else + // { + // foreach (var artifact in artifacts) + // { + // Information("{0}: {1}", artifact.Id, artifact.Name); + // } + // } +}); + +Task("Read-BuildTestRuns") + .Does(() => +{ + // var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var testRuns = build.GetTestRuns(); + // if (!testRuns.Any()) + // { + // Information("No test runs found."); + // } + // else + // { + // foreach (var testRun in testRuns) + // { + // Information("{0}", testRun.RunId); + // } + // } +}); + +Task("Read-PullRequest") + .WithCriteria((context) => context.BuildSystem().IsPullRequest, "Only supported for pull request builds.") + .Does(() => +{ + // var pullRequest = + // AzureDevOpsPullRequestUsingAzurePipelinesOAuthToken(); + + // Information(pullRequest.TargetRefName); +}); + + +Task("Default") + .IsDependentOn("Read-Build") + .IsDependentOn("Read-BuildChanges") + .IsDependentOn("Read-BuildTimelineRecords") + .IsDependentOn("Read-BuildArtifacts") + .IsDependentOn("Read-BuildTestRuns") + .IsDependentOn("Read-PullRequest"); + +////////////////////////////////////////////////// +// EXECUTION +////////////////////////////////////////////////// + +RunTarget(target); \ No newline at end of file diff --git a/tests/script-runner/net6.0/build.ps1 b/tests/script-runner/net6.0/build.ps1 new file mode 100644 index 00000000..fe602768 --- /dev/null +++ b/tests/script-runner/net6.0/build.ps1 @@ -0,0 +1,15 @@ +$ErrorActionPreference = 'Stop' + +$SCRIPT_NAME = "build.cake" + +Write-Host "Restoring .NET Core tools" +dotnet tool restore +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "Bootstrapping Cake" +dotnet cake $SCRIPT_NAME --bootstrap +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "Running Build" +dotnet cake $SCRIPT_NAME @args +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } \ No newline at end of file diff --git a/tests/script-runner/net6.0/build.sh b/tests/script-runner/net6.0/build.sh new file mode 100755 index 00000000..921a3241 --- /dev/null +++ b/tests/script-runner/net6.0/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +SCRIPT_NAME="build.cake" + +echo "Restoring .NET Core tools" +dotnet tool restore + +echo "Bootstrapping Cake" +dotnet cake $SCRIPT_NAME --bootstrap + +echo "Running Build" +dotnet cake $SCRIPT_NAME "$@" \ No newline at end of file diff --git a/tests/script-runner/net6.0/global.json b/tests/script-runner/net6.0/global.json new file mode 100644 index 00000000..c31f98b7 --- /dev/null +++ b/tests/script-runner/net6.0/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "allowPrerelease": true, + "version": "6.0.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/tests/script-runner/net6.0/nuget.config b/tests/script-runner/net6.0/nuget.config new file mode 100644 index 00000000..62768c3d --- /dev/null +++ b/tests/script-runner/net6.0/nuget.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/script-runner/netcoreapp3.1/.config/dotnet-tools.json b/tests/script-runner/netcoreapp3.1/.config/dotnet-tools.json new file mode 100644 index 00000000..db6b2cbc --- /dev/null +++ b/tests/script-runner/netcoreapp3.1/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "cake.tool": { + "version": "2.0.0", + "commands": [ + "dotnet-cake" + ] + } + } + } \ No newline at end of file diff --git a/tests/script-runner/netcoreapp3.1/build.cake b/tests/script-runner/netcoreapp3.1/build.cake new file mode 100644 index 00000000..20015a30 --- /dev/null +++ b/tests/script-runner/netcoreapp3.1/build.cake @@ -0,0 +1,120 @@ +#addin nuget:?package=Cake.AzureDevOps&prerelease + +////////////////////////////////////////////////// +// ARGUMENTS +////////////////////////////////////////////////// + +var target = Argument("target", "Default"); + +////////////////////////////////////////////////// +// TARGETS +////////////////////////////////////////////////// + +Task("Read-Build") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + Information(build.BuildId); +}); + +Task("Read-BuildChanges") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var changes = build.GetChanges(); + if (!changes.Any()) + { + Information("No changes found."); + } + else + { + foreach (var change in changes) + { + Information("{0}: {1} by {2}", change.Id, change.Message, change.Author); + } + } +}); + +Task("Read-BuildTimelineRecords") + .Does(() => +{ + var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + var timelineRecords = build.GetTimelineRecords(); + if (!timelineRecords.Any()) + { + Information("No timeline records found."); + } + else + { + foreach (var timelineRecord in timelineRecords) + { + Information("{0}: {1}", timelineRecord.Id, timelineRecord.Name); + } + } +}); + +Task("Read-BuildArtifacts") + .Does(() => +{ + // var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var artifacts = build.GetArtifacts(); + // if (!artifacts.Any()) + // { + // Information("No artifacts found."); + // } + // else + // { + // foreach (var artifact in artifacts) + // { + // Information("{0}: {1}", artifact.Id, artifact.Name); + // } + // } +}); + +Task("Read-BuildTestRuns") + .Does(() => +{ + // var build = AzureDevOpsBuildUsingAzurePipelinesOAuthToken(); + + // var testRuns = build.GetTestRuns(); + // if (!testRuns.Any()) + // { + // Information("No test runs found."); + // } + // else + // { + // foreach (var testRun in testRuns) + // { + // Information("{0}", testRun.RunId); + // } + // } +}); + +Task("Read-PullRequest") + .WithCriteria((context) => context.BuildSystem().IsPullRequest, "Only supported for pull request builds.") + .Does(() => +{ + // var pullRequest = + // AzureDevOpsPullRequestUsingAzurePipelinesOAuthToken(); + + // Information(pullRequest.TargetRefName); +}); + + +Task("Default") + .IsDependentOn("Read-Build") + .IsDependentOn("Read-BuildChanges") + .IsDependentOn("Read-BuildTimelineRecords") + .IsDependentOn("Read-BuildArtifacts") + .IsDependentOn("Read-BuildTestRuns") + .IsDependentOn("Read-PullRequest"); + +////////////////////////////////////////////////// +// EXECUTION +////////////////////////////////////////////////// + +RunTarget(target); \ No newline at end of file diff --git a/tests/script-runner/netcoreapp3.1/build.ps1 b/tests/script-runner/netcoreapp3.1/build.ps1 new file mode 100644 index 00000000..fe602768 --- /dev/null +++ b/tests/script-runner/netcoreapp3.1/build.ps1 @@ -0,0 +1,15 @@ +$ErrorActionPreference = 'Stop' + +$SCRIPT_NAME = "build.cake" + +Write-Host "Restoring .NET Core tools" +dotnet tool restore +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "Bootstrapping Cake" +dotnet cake $SCRIPT_NAME --bootstrap +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } + +Write-Host "Running Build" +dotnet cake $SCRIPT_NAME @args +if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } \ No newline at end of file diff --git a/tests/script-runner/netcoreapp3.1/build.sh b/tests/script-runner/netcoreapp3.1/build.sh new file mode 100755 index 00000000..921a3241 --- /dev/null +++ b/tests/script-runner/netcoreapp3.1/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +SCRIPT_NAME="build.cake" + +echo "Restoring .NET Core tools" +dotnet tool restore + +echo "Bootstrapping Cake" +dotnet cake $SCRIPT_NAME --bootstrap + +echo "Running Build" +dotnet cake $SCRIPT_NAME "$@" \ No newline at end of file diff --git a/tests/script-runner/netcoreapp3.1/global.json b/tests/script-runner/netcoreapp3.1/global.json new file mode 100644 index 00000000..9fa4eda5 --- /dev/null +++ b/tests/script-runner/netcoreapp3.1/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "allowPrerelease": true, + "version": "3.1.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/tests/script-runner/netcoreapp3.1/nuget.config b/tests/script-runner/netcoreapp3.1/nuget.config new file mode 100644 index 00000000..62768c3d --- /dev/null +++ b/tests/script-runner/netcoreapp3.1/nuget.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file