forked from pardeike/Harmony
-
Notifications
You must be signed in to change notification settings - Fork 0
/
azure-pipelines-job-template.yml
167 lines (153 loc) · 9.9 KB
/
azure-pipelines-job-template.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
parameters:
- name: dependsOn
type: string
default: ''
- name: os
type: string
values: [windows, ubuntu, macOS]
- name: runtimeType
type: string
values: [dotnet, mono]
default: dotnet
- name: architecture
type: string
values: [x86, x64]
- name: frameworks
type: object
- name: buildConfiguration
type: string
default: Release
- name: publishBuild
type: boolean
default: false
jobs:
- job: ${{parameters.os}}_${{parameters.runtimeType}}_${{parameters.architecture}}
dependsOn: ${{parameters.DependsOn}}
displayName: ${{replace(format('{0} {1} {2}', parameters.os, replace(parameters.runtimeType, 'dotnet', ''), parameters.architecture), ' ', ' ')}}
pool:
vmImage: ${{parameters.os}}-latest
# "variables" is misleading here - these are actually constants.
# These must be declared here, since templates don't allow combining a root jobs (or steps) section and a root variables section.
variables:
# Note: Azure Pipelines currently has an issue where if the test name is in the fully qualified name format "namespace.classname.methodname(args)",
# it only shows "methodname" rather than the fully qualified name (something do with more than 2 periods in the test name).
# Workaround is to use the format: "namespace.classname:methodname(args)" (note the colon)
# Also, Azure Pipelines command line argument parser tends to treat single quotes within argument strings literally, so using double quotes instead.
runSettingsArgs: "NUnit.DefaultTestNamePattern=\"{C}:{m}{a}\" RunConfiguration.TargetPlatform=${{parameters.architecture}}"
# Blame mode helps isolate tests that cause crashes (https://github.com/Microsoft/vstest-docs/blob/master/docs/extensions/blame-datacollector.md),
# although it somewhat ironically also makes such unstable tests crash less often, and makes the test suite run slightly slower.
blameArg: --blame
steps:
- checkout: self
submodules: recursive
# For each .NET Core (and .NET 5+) version, ensure corresponding `dotnet` is installed.
# Relies on all .NET Core (and .NET 5+) framework having a dot in their TFM.
# Assumes that .NET Standard is never used.
- ${{ each framework in parameters.frameworks }}:
# .NET Core (and .NET 5+) detection is done by taking advantage of TFMs for such frameworks always having a period, which .NET Framework TFMs lack.
- ${{ if contains(framework, '.') }}:
# Assume x64 agents lack installed x86 .NET Core runtimes (which is the case for MS-hosted agents), so install them as needed.
# However, such x86 runtimes can't be installed via the UseDotNet task, so we need to use the official dotnet-install.ps1 script.
- ${{ if and(eq(parameters.architecture, 'x86'), eq(parameters.os, 'windows')) }}:
- pwsh: |
Invoke-WebRequest 'https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1' -OutFile dotnet-install.ps1
.\dotnet-install.ps1 -Channel ${{replace(replace(framework, 'coreapp', ''), 'net', '')}} -Architecture ${{parameters.architecture}} -Runtime dotnet -InstallDir "${Env:ProgramFiles(x86)}\dotnet" -NoPath
displayName: "Install latest .NET ${{replace(replace(framework, 'coreapp', 'Core '), 'net', '')}} ${{parameters.architecture}} Runtime"
- ${{ if or(ne(parameters.architecture, 'x86'), ne(parameters.os, 'windows')) }}: # currently no `else` construct in Azure Pipelines
- task: UseDotNet@2
displayName: "Install latest .NET ${{replace(replace(framework, 'coreapp', 'Core '), 'net', '')}} Runtime"
inputs:
version: ${{replace(replace(framework, 'coreapp', ''), 'net', '')}}.x
packageType: sdk
- bash: "dotnet clean --configuration ${{parameters.buildConfiguration}} && dotnet nuget locals all --clear"
displayName: 'Clean'
- task: DotNetCoreCLI@2
displayName: 'Build'
inputs:
command: build # `dotnet build` implicitly runs `dotnet restore` as well
projects: Harmony.sln
arguments: "--configuration ${{parameters.buildConfiguration}}"
# For some reason, if mono is installed, above solution restore/build fails when restoring nugets (doesn't fail when only building a project).
# Since mono is only needed for running tests in 'mono' runtimeType mode, only install mono after the restore/build.
- ${{ if eq(parameters.runtimeType, 'mono') }}:
- bash: "choco install mono --yes --no-progress --${{parameters.architecture}}"
displayName: 'Install Mono'
# 'dotnet' runtimeType means using `dotnet test`.
- ${{ each framework in parameters.frameworks }}:
- ${{ if eq(parameters.runtimeType, 'dotnet') }}:
# dotnet usage notes:
# - `dotnet test` does work for .NET Framework targets, but they still implicitly require mono on non-Windows.
# `dotnet test` for .NET Core targets do not require mono (.NET Core is cross-platform).
# - `dotnet test HarmonyTests`, even with --framework net35, won't run .NET Framework 3.5 tests unless IsTestPlatform is explicitly set
# (since Microsoft.NET.Test.Sdk only sets IsTestProject property for net40+). HarmonyTests.csproj works around this by setting IsTestPlatform for net35.
# - `dotnet test path/to/<tfm>/HarmonyTests.dll --framework <tfm>` does work, but there's currently a bug where inline runSettings don't work
# (this is apparently fixed in .NET 5.0+), requiring a runSettings file (which would need to be generated beforehand). So not doing this.
# - Even with the above workaround for running .NET Framework 3.5 tests, there is no CLR 2.0 runtime (or mono equivalent) available to properly test it.
# Both `dotnet test path/to/net35/HarmonyTests.dll --framework net35` and `mono path/to/nunit3-console.exe path/to/net35/HarmonyTests.dll`
# run the tests in 'CLR 4.0 "compatibility mode"' (or the mono equivalent of this).
- task: DotNetCoreCLI@2
displayName: "Test ${{replace(framework, 'net35', 'net35 in CLR 4.0 \"compatibility mode\"')}}"
condition: succeededOrFailed()
inputs:
command: test
projects: HarmonyTests
arguments: "--no-build --configuration ${{parameters.buildConfiguration}} --framework ${{framework}} --logger \"console;verbosity=normal\" $(blameArg) -- $(runSettingsArgs)"
publishTestResults: true
# .NET Framework on non-Windows implicitly always uses mono.
${{ if and(ne(parameters.os, 'windows'), not(contains(framework, '.'))) }}:
testRunTitle: "${{framework}} (${{parameters.os}} mono ${{parameters.architecture}})"
${{ if or(eq(parameters.os, 'windows'), contains(framework, '.')) }}: # currently no `else` construct in Azure Pipelines
testRunTitle: "${{framework}} (${{parameters.os}} ${{parameters.architecture}})"
# 'mono' runtimeType is for running .NET Framework tests on Windows (non-Windows will always run .NET Framework tests on mono even via `dotnet test`).
- ${{ if eq(parameters.runtimeType, 'mono') }}:
# Following attempts to replicate the behavior of DotNetCoreCLI test with publishTestResults=true, the main difference being:
# - the usage of mono + vstest.console (since mono + dotnet test doesn't work)
# - the actual test publishing delegated to a followup PublishTestResults task
- bash: |
shopt -s failglob globstar
set -x
# Remove any existing test result files.
for testResultFile in "$(Agent.TempDirectory)"/**/*.trx; do
rm "$testResultFile"
# If there are test attachments, they are in a directory with same name as trx file, excluding the .trx extension.
testResultDir="${testResultFile%.trx}"
if [ -e "$testResultDir" ]; then
rm -r "$testResultDir"
fi
done
# Execute the tests via mono + vstest.console.
mono="$(eval $programFilesCmd)/Mono/bin/mono.exe"
vstest="$(vswhere.exe -latest -property installationPath)/Common7/IDE/CommonExtensions/Microsoft/TestWindow/vstest.console.exe"
"$mono" "$vstest" "HarmonyTests/bin/${{parameters.buildConfiguration}}/${{framework}}/HarmonyTests.dll" --Framework:${{framework}} --logger:trx --ResultsDirectory:"$(Agent.TempDirectory)" $(blameArg) -- $(runSettingsArgs)
displayName: "Test ${{replace(framework, 'net35', 'net35 in CLR 4.0 \"compatibility mode\"')}}"
condition: succeededOrFailed()
env:
${{ if eq(parameters.architecture, 'x86') }}:
programFilesCmd: "cmd //c 'echo %ProgramFiles(x86)%'" # no direct way to access ProgramFiles(x86) env var from within bash
${{ if ne(parameters.architecture, 'x86') }}: # currently no `else` construct in Azure Pipelines
programFilesCmd: "echo $PROGRAMFILES"
- ${{ if eq(parameters.runtimeType, 'mono') }}:
- task: PublishTestResults@2
displayName: "Publish ${{framework}} test results"
condition: succeededOrFailed()
inputs:
buildPlatform: ${{parameters.architecture}}
buildConfiguration: ${{parameters.buildConfiguration}}
testResultsFormat: VSTest
testResultsFiles: '**/*.trx'
searchFolder: $(Agent.TempDirectory)
testRunTitle: "${{framework}} (${{parameters.os}} mono ${{parameters.architecture}})"
- ${{ if parameters.publishBuild }}:
# publish task doesn't support globbing, so have to find the file and output it as a variable. Yes, this is clunky.
- bash: |
shopt -s failglob
# Expecting only one zip file, but this allows script to error if no such zip file is found.
for buildZipFile in Harmony/bin/Harmony*.zip; do
echo "##vso[task.setvariable variable=buildZipFile]$buildZipFile"
done
name: FindBuildArtifact
displayName: "Find Harmony zip for build artifact publishing"
condition: succeededOrFailed()
- publish: $(buildZipFile)
displayName: "Publish Harmony zip as build artifact"
artifact: ${{parameters.buildConfiguration}}