-
-
Notifications
You must be signed in to change notification settings - Fork 2
237 lines (228 loc) · 13.3 KB
/
build-test-package.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
name: Build, test and package
on:
workflow_dispatch:
push:
branches: [ main ]
paths-ignore:
- "**.md"
pull_request:
branches: [ main ]
schedule:
- cron: '0 0 * * 0' # Once a week: "At 00:00 on Sunday."
defaults:
run:
shell: pwsh
jobs:
main:
name: Build, test and package
permissions:
contents: read
strategy:
fail-fast: false # don't fail if one of the matrix jobs fails. Example: try to run the windows matrix even if the ubuntu matrix fails.
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
env:
SLN_FILEPATH: ${{github.workspace}}/DotNet.Sdk.Extensions.sln
EXTENSIONS_BIN_FOLDER : ${{ github.workspace }}/src/DotNet.Sdk.Extensions/bin/Release
TESTING_EXTENSIONS_BIN_FOLDER : ${{ github.workspace }}/src/DotNet.Sdk.Extensions.Testing/bin/Release
TEST_RESULTS_ARTIFACT_NAME: test-results-${{ matrix.os }}
CODE_COVERAGE_ARTIFACT_NAME: code-coverage-report-${{ matrix.os }}
NUGET_ARTIFACT_NAME : nuget-packages-and-symbols
steps:
- name: Dump github context for debug purposes
env:
GITHUB_CONTEXT: ${{ toJSON(github) }}
run: $env:GITHUB_CONTEXT
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
7.0.x
global-json-file: global.json
- name: Cache/Restore NuGets
uses: actions/cache@v4
with:
path:
~/.nuget/packages
key: ${{ runner.os }}-nuget
restore-keys: |
${{ runner.os }}-nuget-
- name: Install reportgenerator dotnet tool
run: dotnet tool install --global dotnet-reportgenerator-globaltool
- name: Restore dependencies
run: dotnet restore ${{ env.SLN_FILEPATH }} -warnaserror
- name: Build
run: dotnet build ${{ env.SLN_FILEPATH }} -c Release -warnaserror --no-restore --no-incremental
- name: Test and code coverage
id: dotnet-test
env:
# default is 90 seconds. Increasing this because sometimes running on Windows I get:
# "vstest.console process failed to connect to testhost process after 90 seconds. This may occur due to machine slowness,
# please set environment variable VSTEST_CONNECTION_TIMEOUT to increase timeout."
# Don't know why this occurs though
VSTEST_CONNECTION_TIMEOUT: 270
run: |
$os = $PSVersionTable.OS
$testResultsDir = $(Join-Path -Path (Get-Location) -ChildPath "tests/${{ matrix.os }}/test-results")
$testCoverageDir = $(Join-Path -Path (Get-Location) -ChildPath "tests/${{ matrix.os }}/coverage-results/")
$testCoverageFile = $(Join-Path -Path $testCoverageDir -ChildPath "coverage.net8.0.opencover.xml")
Write-Output "test-results-dir=$testResultsDir" >> $env:GITHUB_OUTPUT
Write-Output "test-coverage-dir=$testCoverageDir" >> $env:GITHUB_OUTPUT
Write-Output "test-coverage-file=$testCoverageFile" >> $env:GITHUB_OUTPUT
Write-Output "::group::Test output directories."
Write-Output "test-results-dir is set to $testResultsDir"
Write-Output "test-coverage-dir is set to $testCoverageDir"
Write-Output "test-coverage-file is set to $testCoverageFile"
Write-Output "::endgroup::"
$downloadArtifactMessage = "You can inspect the test results by downloading the workflow artifact named: ${{ env.TEST_RESULTS_ARTIFACT_NAME }}."
$frameworkMonikers = @('net6.0','net7.0','net8.0')
foreach($frameworkMoniker in $frameworkMonikers)
{
Write-Output "::group::Running dotnet test for target framework $frameworkMoniker."
$testCoverageMergeFile = $(Join-Path -Path $testCoverageDir -ChildPath "coverage.$frameworkMoniker.json")
dotnet test ${{ env.SLN_FILEPATH }} `
-c Release `
--no-build `
--framework $frameworkMoniker `
--logger "trx;LogFilePrefix=framework" `
--logger GitHubActions `
--logger "liquid.custom;Template=${{github.workspace}}/tests/liquid-test-logger-template.md;runnerOS=${{ matrix.os }};os=$os;LogFilePrefix=framework" `
--results-directory "$testResultsDir" `
/p:CollectCoverage=true `
/p:CoverletOutput="$testCoverageDir" `
/p:MergeWith="$testCoverageMergeFile" `
/p:CoverletOutputFormat="json%2copencover" `
-m:1 `
-- RunConfiguration.TreatNoTestsAsError=true # this must be the last parameter and there must be a space between the -- and the RunConfiguration.TreatNoTestsAsError. See https://github.com/microsoft/vstest/pull/2610#issuecomment-942113882
Write-Output "::endgroup::"
if($LASTEXITCODE -ne 0)
{
Write-Output "::error title=Tests (${{ matrix.os }})::Tests failed on ${{ matrix.os }}. $downloadArtifactMessage"
Exit 1
}
}
Write-Output "::notice title=Tests (${{ matrix.os }})::Tests passed on ${{ matrix.os }}. $downloadArtifactMessage"
- name: Set run even if tests fail conditions
id: even-if-tests-fail
if: always()
run: |
# Some of the steps below provide feedback on the test run and I want to run them even if
# some of the previous steps failed. For that I need:
# - the 'always()' condition: without it the step only runs if the job is successful, it's like the 'if' condition on any step always has a hidden '&& success()' clause.
# - the '(steps.<step-id>.conclusion == 'success' || steps.<step-id>.conclusion == 'failure')' condition: to run the steps only if the <step-id> step has ran, regardless
# if it failed or not. It won't run if the <step-id> step has been skipped or cancelled.
#
# As such, the output from this step is meant to be used on the 'if' property of steps as follows:
# if: steps.even-if-tests-fail.outputs.condition == 'true' && always()
$condition = '${{ (steps.dotnet-test.conclusion == 'success' || steps.dotnet-test.conclusion == 'failure') }}'
Write-Output "condition=$condition" >> $env:GITHUB_OUTPUT
Write-Output "condition is set to $condition"
- name: Upload opencover test coverage file to artifacts
if: matrix.os == 'ubuntu-latest'
uses: actions/upload-artifact@v4
with:
name: opencover-test-coverage-file
path: ${{ steps.dotnet-test.outputs.test-coverage-file }}
- name: Generate code coverage report
id: code-coverage-report-generator
if: steps.even-if-tests-fail.outputs.condition == 'true' && always()
run: |
$testCoverageReportDir = $(Join-Path -Path ${{ steps.dotnet-test.outputs.test-coverage-dir }} -ChildPath "report")
Write-Output "test-coverage-report-dir=$testCoverageReportDir" >> $env:GITHUB_OUTPUT
reportgenerator `
"-reports:${{ steps.dotnet-test.outputs.test-coverage-file }}" `
"-targetdir:$testCoverageReportDir" `
-reportTypes:htmlInline
- name: Upload code coverage report to artifacts
if: steps.even-if-tests-fail.outputs.condition == 'true' && always()
uses: actions/upload-artifact@v4
with:
name: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }}
path: ${{ steps.code-coverage-report-generator.outputs.test-coverage-report-dir }}
- name: Rename test results files
if: steps.even-if-tests-fail.outputs.condition == 'true' && always()
run: |
# I have another workflow that will get all test result markdown files and add them as comments to a Pull Request.
# If I don't rename the test results file there is an edge case that will cause the test results comments on the Pull Request to be incorrect.
#
# The edge case is when this job is re-run. When the job is re-run, it will upload the test results artifact multiple times.
# The artifacts work as a share so if the files have the same name they get overwritten, if not they get added to the existing artifact.
# By default, all the test results files will be unique due to a timestamp being part of their name so when re-running the job I will get the test results
# from ALL runs in the artifact with name env.TEST_RESULTS_ARTIFACT_NAME.
# Then when the workflow to add the test results to the Pull Request runs it will get ALL the markdown files in the env.TEST_RESULTS_ARTIFACT_NAME and
# add them as a Pull Request. The end result being I will have more test results showing on the PR than intended. Only the test results from the last run should be displayed
# on the Pull Request.
#
# By running this step, I make sure the test result filenames are deterministic so that on job reruns I will only get the latest test results on the the artifact with name env.TEST_RESULTS_ARTIFACT_NAME.
# Example:
# As of writing this I have 2 test projects and after running tests I will have one markdown file and one trx file per test project and framework. Let's consider the files for .net7.0:
# - one md and trx file for DotNet.Sdk.Extensions.Tests.csproj and another md and trx file for DotNet.Sdk.Extensions.Testing.Tests.csproj. Which for this example let's assume would be named:
# - framework_net7.0_20230226152259.md
# - framework_net7.0_20230226152312.md
# - framework_net7.0_20230226152259.trx
# - framework_net7.0_20230226152312.trx
#
# The filenames contain only the framework and a timestamp. Unfortunately the assembly name is not part of the filename so it's not possible to know which test project the file belongs without viewing its content.
# After renaming we would have the following filenames:
# - framework_net7.0_0.md
# - framework_net7.0_1.md
# - framework_net7.0_0.trx
# - framework_net7.0_1.trx
#
# This way when re-running the job the test result filenames are deterministic and will always override existing files in the artifact with name env.TEST_RESULTS_ARTIFACT_NAME.
# This does mean that the artifact with name env.TEST_RESULTS_ARTIFACT_NAME will always only contain the test results from the latest run.
#
$testResultsDir = '${{ steps.dotnet-test.outputs.test-results-dir }}'
# rename test result files for all frameworks
$frameworkFilters = @('framework_net6.0*','framework_net7.0*','framework_net8.0*')
foreach($frameworkFilter in $frameworkFilters)
{
# for each framework group the files by extension. There will be .md and .trx file groups
$frameworkFiles = Get-ChildItem -Path $testResultsDir -Recurse -Filter $frameworkFilter | Group-Object -Property Extension
foreach($extensionFilesGroup in $frameworkFiles)
{
# rename all the files in each group by adding a deterministic suffix. Since the count of the files in each group does not change
# between workflow runs we can use that as a deterministic suffix.
for ($i = 0; $i -lt $extensionFilesGroup.Count; $i++)
{
$file = $extensionFilesGroup.Group[$i]
$extension = $file.Extension
$frameworkPrefix = $frameworkFilter -replace '\*', ''
$newFilename = "$frameworkPrefix`_$i$extension"
Write-Output "Renaming $file to $newFilename"
Rename-Item -Path $file -NewName "$newFilename"
}
}
}
- name: Upload test results to artifacts
if: steps.even-if-tests-fail.outputs.condition == 'true' && always()
uses: actions/upload-artifact@v4
with:
name: ${{ env.TEST_RESULTS_ARTIFACT_NAME }}
path: ${{ steps.dotnet-test.outputs.test-results-dir }}
- name: Package DotNet.Sdk.Extensions
if: matrix.os == 'ubuntu-latest' # this job is on a matrix run but I only want to build the NuGet once
run: dotnet pack src/DotNet.Sdk.Extensions/DotNet.Sdk.Extensions.csproj -c Release --no-build
- name: Package DotNet.Sdk.Extensions.Testing
if: matrix.os == 'ubuntu-latest' # this job is on a matrix run but I only want to build the NuGet once
run: dotnet pack src/DotNet.Sdk.Extensions.Testing/DotNet.Sdk.Extensions.Testing.csproj -c Release --no-build
- name: Upload NuGets and symbols to artifacts
id: upload-nuget-artifacts
uses: actions/upload-artifact@v4
if: matrix.os == 'ubuntu-latest' # this job is on a matrix run but I only want to upload NuGets as artifacts once
with:
name: ${{ env.NUGET_ARTIFACT_NAME }}
path: |
${{ env.EXTENSIONS_BIN_FOLDER }}/*.nupkg
${{ env.EXTENSIONS_BIN_FOLDER }}/*.snupkg
${{ env.TESTING_EXTENSIONS_BIN_FOLDER }}/*.nupkg
${{ env.TESTING_EXTENSIONS_BIN_FOLDER }}/*.snupkg
- name: Log artifacts info
if: matrix.os == 'ubuntu-latest' # this job is on a matrix run but I only want to log this messages to the build summary once
run: |
Write-Output "::notice title=Code coverage report::You can download the code coverage report from the workflow artifact named: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }}."
Write-Output "::notice title=NuGets::You can download the NuGet packages and symbols from the workflow artifact named: ${{ env.NUGET_ARTIFACT_NAME }}."