Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pull-request-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
if: >-
vars.SONAR_CI_ENABLED == 'true'
&& github.event.pull_request.head.repo.full_name == github.repository
&& github.actor != 'dependabot[bot]'
&& github.event.pull_request.user.login != 'dependabot[bot]'
uses: ./.github/workflows/sonar.yml
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
Expand Down
102 changes: 76 additions & 26 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@ on:
description: SonarQube Cloud organization key.
type: string
default: cheatenginenet
wait-quality-gate:
description: Fail the job when the quality gate fails.
type: boolean
default: false
secrets:
SONAR_TOKEN:
description: SonarQube Cloud token. The analysis is skipped when it is empty.
required: false
description: SonarQube Cloud token for protected, same-repository analysis.
required: true

permissions:
contents: read
Expand All @@ -38,22 +34,21 @@ jobs:
name: Analyze
if: github.event_name != 'merge_group'
runs-on: windows-latest
timeout-minutes: 30
timeout-minutes: 40
env:
SONAR_ENABLED: ${{ secrets.SONAR_TOKEN != '' }}
SONAR_PROJECT_KEY: ${{ inputs.project-key }}
SONAR_ORGANIZATION: ${{ inputs.organization }}
SONAR_WAIT_QUALITY_GATE: ${{ inputs.wait-quality-gate }}

steps:
- name: Skip without token
if: env.SONAR_ENABLED != 'true'
run: >-
Write-Host '::notice title=Sonar analysis skipped::SONAR_TOKEN is empty. Fork and Dependabot pull requests
receive no repository secret; configure SONAR_TOKEN to enable analysis.'
- name: Require Sonar token
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
if ([string]::IsNullOrWhiteSpace($env:SONAR_TOKEN)) {
throw 'SONAR_TOKEN is required when SONAR_CI_ENABLED is true.'
}

- name: Check analysis method
if: env.SONAR_ENABLED == 'true'
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: |
Expand All @@ -63,8 +58,7 @@ jobs:
-TimeoutSec 30 -MaximumRetryCount 2 -RetryIntervalSec 3
}
catch {
Write-Host "::warning::Could not read the analysis method of $env:SONAR_PROJECT_KEY. $($_.Exception.Message)"
return
throw "Could not verify the analysis method of $env:SONAR_PROJECT_KEY. $($_.Exception.Message)"
}

if (@($response.settings | Where-Object { $_.key -eq 'sonar.autoscan.enabled' -and $_.value -eq 'true' })) {
Expand All @@ -73,21 +67,18 @@ jobs:
}

- name: Checkout
if: env.SONAR_ENABLED == 'true'
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
fetch-depth: 0
persist-credentials: false

- name: Set up JDK 21
if: env.SONAR_ENABLED == 'true'
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: zulu
java-version: '21'

- name: Install pinned .NET SDK
if: env.SONAR_ENABLED == 'true'
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
global-json-file: global.json
Expand All @@ -97,17 +88,14 @@ jobs:
Directory.Packages.props

- name: Install scanner
if: env.SONAR_ENABLED == 'true'
run: >-
dotnet tool install dotnet-sonarscanner --tool-path "$env:RUNNER_TEMP/sonar-scanner"
--version $env:SONAR_SCANNER_VERSION

- name: Restore locked dependency graph
if: env.SONAR_ENABLED == 'true'
run: dotnet restore CheatEngine.Client.slnx --locked-mode

- name: Begin analysis
if: env.SONAR_ENABLED == 'true'
env:
SONARQUBE_SCANNER_PARAMS: '{"sonar.token":"${{ secrets.SONAR_TOKEN }}"}'
run: |
Expand Down Expand Up @@ -138,8 +126,10 @@ jobs:
"/k:$env:SONAR_PROJECT_KEY"
"/o:$env:SONAR_ORGANIZATION"
'/d:sonar.exclusions=artifacts/**,tests/CheatEngine.Client.Benchmarks/**'
'/d:sonar.cs.cobertura.reportsPaths=artifacts/sonar-test-results/*/coverage.cobertura.xml'
"/d:sonar.issue.ignore.multicriteria=$(($ignoredIssues.Key) -join ',')"
"/d:sonar.qualitygate.wait=$env:SONAR_WAIT_QUALITY_GATE"
'/d:sonar.qualitygate.wait=true'
'/d:sonar.qualitygate.timeout=300'
)
foreach ($issue in $ignoredIssues) {
$arguments += "/d:sonar.issue.ignore.multicriteria.$($issue.Key).ruleKey=$($issue.Rule)"
Expand All @@ -148,13 +138,73 @@ jobs:
& "$env:RUNNER_TEMP/sonar-scanner/dotnet-sonarscanner.exe" begin @arguments

- name: Build
if: env.SONAR_ENABLED == 'true'
run: >-
dotnet build CheatEngine.Client.slnx --configuration Release --no-restore --no-incremental
--disable-build-servers --warnaserror

- name: Run tests with Microsoft Testing Platform coverage
run: |
$projects = @(Get-ChildItem -Path tests -Filter '*.Tests.csproj' -Recurse -File | Sort-Object FullName)
if ($projects.Count -eq 0) {
throw 'No *.Tests.csproj project found under tests.'
}

foreach ($project in $projects) {
$results = Join-Path 'artifacts/sonar-test-results' $project.BaseName
$coverage = [IO.Path]::GetFullPath((Join-Path $results 'coverage.cobertura.xml'))
$options = @(
'--project', $project.FullName,
'--configuration', 'Release',
'--no-build', '--no-restore',
'--report-trx',
'--results-directory', $results,
'--coverage',
'--coverage-output', $coverage,
'--coverage-output-format', 'cobertura',
'--fail-skips', 'on'
)
dotnet test @options
if ($LASTEXITCODE -ne 0) {
throw "Microsoft Testing Platform failed for '$($project.FullName)' with exit code $LASTEXITCODE."
}
}

- name: Verify coverage reports
run: |
$projects = @(Get-ChildItem -Path tests -Filter '*.Tests.csproj' -Recurse -File)
$reports = @($projects | ForEach-Object {
Join-Path (Join-Path 'artifacts/sonar-test-results' $_.BaseName) 'coverage.cobertura.xml'
})
$missingReports = @($reports | Where-Object { -not (Test-Path -LiteralPath $_ -PathType Leaf) })
if ($missingReports.Count -gt 0) {
throw "Microsoft Testing Platform did not produce Cobertura coverage report(s): $($missingReports -join ', ')."
}

foreach ($report in $reports) {
try {
$coverage = [xml](Get-Content -LiteralPath $report -Raw)
}
catch {
throw "Coverage report '$report' is not valid XML. $($_.Exception.Message)"
}

if ((Get-Item -LiteralPath $report).Length -eq 0 -or $coverage.DocumentElement.Name -ne 'coverage') {
throw "Coverage report '$report' is not a Cobertura report."
}
}

Write-Host "Verified $($reports.Count) Cobertura coverage report(s)."

- name: End analysis
if: env.SONAR_ENABLED == 'true'
env:
SONARQUBE_SCANNER_PARAMS: '{"sonar.token":"${{ secrets.SONAR_TOKEN }}"}'
run: '& "$env:RUNNER_TEMP/sonar-scanner/dotnet-sonarscanner.exe" end'

- name: Upload Sonar coverage reports
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: sonar-coverage
path: artifacts/sonar-test-results/*/coverage.cobertura.xml
if-no-files-found: error
retention-days: 14
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<ItemGroup Label="Tests">
<PackageVersion Include="BenchmarkDotNet" Version="0.15.8"/>
<PackageVersion Include="xunit.v3.mtp-v2" Version="4.0.1"/>
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.11.2"/>
<PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="2.4.1"/>
</ItemGroup>

Expand Down
1 change: 1 addition & 0 deletions eng/Tests.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

<ItemGroup Condition="$(MSBuildProjectName.EndsWith('.Tests'))">
<PackageReference Include="xunit.v3.mtp-v2"/>
<PackageReference Include="Microsoft.Testing.Extensions.CodeCoverage"/>
<PackageReference Include="Microsoft.Testing.Extensions.TrxReport"/>
<Using Include="Xunit"/>
</ItemGroup>
Expand Down
21 changes: 21 additions & 0 deletions tests/CheatEngine.Client.Abstractions.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Direct",
"requested": "[18.11.2, )",
"resolved": "18.11.2",
"contentHash": "bT6awBEUR+fjPpeLAN++4qx5q0sgA9SeJt6QfijnFFPxNSr68BYsYFMQH8L8kY6vMJd3fuvFAFBht4JtWQcWtQ==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.10",
"Microsoft.Extensions.DependencyModel": "10.0.10",
"Microsoft.Testing.Platform": "2.4.0"
}
},
"Microsoft.Testing.Extensions.TrxReport": {
"type": "Direct",
"requested": "[2.4.1, )",
Expand Down Expand Up @@ -52,6 +63,16 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.DiaSymReader": {
"type": "Transitive",
"resolved": "2.2.10",
"contentHash": "zmGsm6b2y3STDa/Of7rdkkfTDV8VuGB8aCqIkLoJIQh5tL78K3zJ8OUyFKnfsaORXmGr9iOKwDkZfUjeXi+CwA=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "rfZA1RjR021RPqSmIPovfz2aOd79TGqJ9BengbjnzIISOVwjLmuSDnhCMmiY/1c6iYvGolQ1iNGzkav0u11XEA=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "10.0.401",
Expand Down
21 changes: 21 additions & 0 deletions tests/CheatEngine.Client.Core.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Direct",
"requested": "[18.11.2, )",
"resolved": "18.11.2",
"contentHash": "bT6awBEUR+fjPpeLAN++4qx5q0sgA9SeJt6QfijnFFPxNSr68BYsYFMQH8L8kY6vMJd3fuvFAFBht4JtWQcWtQ==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.10",
"Microsoft.Extensions.DependencyModel": "10.0.10",
"Microsoft.Testing.Platform": "2.4.0"
}
},
"Microsoft.Testing.Extensions.TrxReport": {
"type": "Direct",
"requested": "[2.4.1, )",
Expand Down Expand Up @@ -52,6 +63,16 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.DiaSymReader": {
"type": "Transitive",
"resolved": "2.2.10",
"contentHash": "zmGsm6b2y3STDa/Of7rdkkfTDV8VuGB8aCqIkLoJIQh5tL78K3zJ8OUyFKnfsaORXmGr9iOKwDkZfUjeXi+CwA=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "rfZA1RjR021RPqSmIPovfz2aOd79TGqJ9BengbjnzIISOVwjLmuSDnhCMmiY/1c6iYvGolQ1iNGzkav0u11XEA=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "10.0.401",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Direct",
"requested": "[18.11.2, )",
"resolved": "18.11.2",
"contentHash": "bT6awBEUR+fjPpeLAN++4qx5q0sgA9SeJt6QfijnFFPxNSr68BYsYFMQH8L8kY6vMJd3fuvFAFBht4JtWQcWtQ==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.10",
"Microsoft.Extensions.DependencyModel": "10.0.10",
"Microsoft.Testing.Platform": "2.4.0"
}
},
"Microsoft.Testing.Extensions.TrxReport": {
"type": "Direct",
"requested": "[2.4.1, )",
Expand Down Expand Up @@ -52,6 +63,11 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.DiaSymReader": {
"type": "Transitive",
"resolved": "2.2.10",
"contentHash": "zmGsm6b2y3STDa/Of7rdkkfTDV8VuGB8aCqIkLoJIQh5tL78K3zJ8OUyFKnfsaORXmGr9iOKwDkZfUjeXi+CwA=="
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "10.0.12",
Expand All @@ -61,6 +77,11 @@
"Microsoft.Extensions.Primitives": "10.0.12"
}
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "rfZA1RjR021RPqSmIPovfz2aOd79TGqJ9BengbjnzIISOVwjLmuSDnhCMmiY/1c6iYvGolQ1iNGzkav0u11XEA=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "10.0.12",
Expand Down
21 changes: 21 additions & 0 deletions tests/CheatEngine.Client.Fluent.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Direct",
"requested": "[18.11.2, )",
"resolved": "18.11.2",
"contentHash": "bT6awBEUR+fjPpeLAN++4qx5q0sgA9SeJt6QfijnFFPxNSr68BYsYFMQH8L8kY6vMJd3fuvFAFBht4JtWQcWtQ==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.10",
"Microsoft.Extensions.DependencyModel": "10.0.10",
"Microsoft.Testing.Platform": "2.4.0"
}
},
"Microsoft.Testing.Extensions.TrxReport": {
"type": "Direct",
"requested": "[2.4.1, )",
Expand Down Expand Up @@ -52,6 +63,16 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.DiaSymReader": {
"type": "Transitive",
"resolved": "2.2.10",
"contentHash": "zmGsm6b2y3STDa/Of7rdkkfTDV8VuGB8aCqIkLoJIQh5tL78K3zJ8OUyFKnfsaORXmGr9iOKwDkZfUjeXi+CwA=="
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "rfZA1RjR021RPqSmIPovfz2aOd79TGqJ9BengbjnzIISOVwjLmuSDnhCMmiY/1c6iYvGolQ1iNGzkav0u11XEA=="
},
"Microsoft.SourceLink.Common": {
"type": "Transitive",
"resolved": "10.0.401",
Expand Down
21 changes: 21 additions & 0 deletions tests/CheatEngine.Client.Hosting.Tests/packages.lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.Testing.Extensions.CodeCoverage": {
"type": "Direct",
"requested": "[18.11.2, )",
"resolved": "18.11.2",
"contentHash": "bT6awBEUR+fjPpeLAN++4qx5q0sgA9SeJt6QfijnFFPxNSr68BYsYFMQH8L8kY6vMJd3fuvFAFBht4JtWQcWtQ==",
"dependencies": {
"Microsoft.DiaSymReader": "2.2.10",
"Microsoft.Extensions.DependencyModel": "10.0.10",
"Microsoft.Testing.Platform": "2.4.0"
}
},
"Microsoft.Testing.Extensions.TrxReport": {
"type": "Direct",
"requested": "[2.4.1, )",
Expand Down Expand Up @@ -52,6 +63,11 @@
"System.IO.Hashing": "10.0.12"
}
},
"Microsoft.DiaSymReader": {
"type": "Transitive",
"resolved": "2.2.10",
"contentHash": "zmGsm6b2y3STDa/Of7rdkkfTDV8VuGB8aCqIkLoJIQh5tL78K3zJ8OUyFKnfsaORXmGr9iOKwDkZfUjeXi+CwA=="
},
"Microsoft.Extensions.Configuration": {
"type": "Transitive",
"resolved": "10.0.12",
Expand All @@ -61,6 +77,11 @@
"Microsoft.Extensions.Primitives": "10.0.12"
}
},
"Microsoft.Extensions.DependencyModel": {
"type": "Transitive",
"resolved": "10.0.10",
"contentHash": "rfZA1RjR021RPqSmIPovfz2aOd79TGqJ9BengbjnzIISOVwjLmuSDnhCMmiY/1c6iYvGolQ1iNGzkav0u11XEA=="
},
"Microsoft.Extensions.Options": {
"type": "Transitive",
"resolved": "10.0.12",
Expand Down
Loading