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
20 changes: 0 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ name: Client validation

on:
workflow_call:
inputs:
collect-coverage:
description: Emit Visual Studio XML coverage for the SonarQube Cloud analysis workflow.
type: boolean
default: false

permissions:
contents: read
Expand Down Expand Up @@ -49,8 +44,6 @@ jobs:
run: dotnet build CheatEngine.Client.slnx --configuration Release --no-restore --warnaserror

- name: Run unit tests through Microsoft Testing Platform
env:
COLLECT_COVERAGE: ${{ inputs.collect-coverage }}
run: |
$projects = @(Get-ChildItem -Path tests -Filter '*.Tests.csproj' -Recurse -File | Sort-Object Name)
if ($projects.Count -eq 0) {
Expand All @@ -67,10 +60,6 @@ jobs:
'--results-directory', $results,
'--fail-skips', 'on'
)
if ($env:COLLECT_COVERAGE -eq 'true') {
$options += '--coverage', '--coverage-output-format', 'xml', '--coverage-output', 'coverage.xml'
}

dotnet test @options
}

Expand Down Expand Up @@ -106,15 +95,6 @@ jobs:
if-no-files-found: warn
retention-days: 14

- name: Upload Sonar coverage
if: ${{ inputs.collect-coverage && !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-client
path: artifacts/test-results/*/coverage.xml
if-no-files-found: error
retention-days: 7

- name: Pack and validate public package APIs
run: dotnet pack CheatEngine.Client.slnx --configuration Release --no-build --no-restore

Expand Down
4 changes: 1 addition & 3 deletions .github/workflows/main-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,12 @@ jobs:
ci:
name: CI
uses: ./.github/workflows/ci.yml
with:
collect-coverage: ${{ github.ref == 'refs/heads/main' }}

# Main analysis is the SonarQube Cloud baseline for pull-request new-code comparisons.
sonar:
name: Sonar
needs: ci
if: github.ref == 'refs/heads/main'
if: vars.SONAR_CI_ENABLED == 'true' && github.ref == 'refs/heads/main'
uses: ./.github/workflows/sonar.yml
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
9 changes: 3 additions & 6 deletions .github/workflows/pull-request-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,14 @@ jobs:
name: CI
if: github.event.pull_request.draft == false
uses: ./.github/workflows/ci.yml
with:
collect-coverage: >-
${{ github.event.pull_request.head.repo.full_name == github.repository
&& github.actor != 'dependabot[bot]' }}

# Fork and Dependabot pull requests receive no repository secret. Keep this condition equal to collect-coverage.
# Fork and Dependabot pull requests receive no repository secret, so Sonar skips them.
sonar:
name: Sonar
needs: ci
if: >-
github.event.pull_request.head.repo.full_name == github.repository
vars.SONAR_CI_ENABLED == 'true'
&& github.event.pull_request.head.repo.full_name == github.repository
&& github.actor != 'dependabot[bot]'
uses: ./.github/workflows/sonar.yml
secrets:
Expand Down
55 changes: 33 additions & 22 deletions .github/workflows/sonar.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ on:
type: string
default: cheatenginenet
wait-quality-gate:
description: Fail the analysis job when SonarQube Cloud reports a failing Quality Gate.
description: Fail the job when the quality gate fails.
type: boolean
default: false
secrets:
SONAR_TOKEN:
description: SonarQube Cloud token. Analysis is skipped when it is unavailable.
description: SonarQube Cloud token. The analysis is skipped when it is empty.
required: false

permissions:
Expand Down Expand Up @@ -96,13 +96,6 @@ jobs:
**/packages.lock.json
Directory.Packages.props

- name: Download coverage
if: env.SONAR_ENABLED == 'true'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: coverage-client
path: ${{ runner.temp }}/coverage

- name: Install scanner
if: env.SONAR_ENABLED == 'true'
run: >-
Expand All @@ -118,22 +111,40 @@ jobs:
env:
SONARQUBE_SCANNER_PARAMS: '{"sonar.token":"${{ secrets.SONAR_TOKEN }}"}'
run: |
$coverage = "$env:RUNNER_TEMP/coverage"
$reports = @(Get-ChildItem -Path $coverage -Filter *.xml -Recurse -File -ErrorAction SilentlyContinue)
if ($reports.Count -eq 0) {
Write-Host '::error::No coverage report was downloaded. Call ci.yml with collect-coverage set to true.'
exit 1
}

Write-Host "Coverage reports: $($reports.Count)"
# These findings conflict with deliberate repository contracts. Keep them in the scanner configuration so
# production and test sources do not need Sonar-only attributes or code changes.
$ignoredIssues = @(
@{ Key = 'noLinq'; Rule = 'csharpsquid:S3267'; Resource = '**/*.cs' }
@{ Key = 'unsafeInterop'; Rule = 'csharpsquid:S6640'; Resource = '**/*.cs' }
@{ Key = 'nullableFlow'; Rule = 'csharpsquid:S8970'; Resource = '**/*.cs' }
@{ Key = 'apiOverloadLayout'; Rule = 'csharpsquid:S4136'; Resource = 'libs/**' }
@{ Key = 'generatorInstances'; Rule = 'csharpsquid:S2325'; Resource = 'source-generators/**' }
@{ Key = 'emittedFragments'; Rule = 'csharpsquid:S1192'; Resource = 'source-generators/**' }
@{ Key = 'generatorComments'; Rule = 'csharpsquid:S125'; Resource = 'source-generators/**' }
@{ Key = 'testReflection'; Rule = 'csharpsquid:S3011'; Resource = 'tests/**' }
@{ Key = 'testLiterals'; Rule = 'csharpsquid:S1192'; Resource = 'tests/**' }
@{ Key = 'testGc'; Rule = 'csharpsquid:S1215'; Resource = 'tests/**' }
@{ Key = 'testStaticHooks'; Rule = 'csharpsquid:S2696'; Resource = 'tests/**' }
@{ Key = 'testDoubleDispose'; Rule = 'csharpsquid:S3966'; Resource = 'tests/**' }
@{ Key = 'testBooleanTables'; Rule = 'csharpsquid:S1125'; Resource = 'tests/**' }
@{ Key = 'testComplexity'; Rule = 'csharpsquid:S3776'; Resource = 'tests/**' }
@{ Key = 'testParameters'; Rule = 'csharpsquid:S107'; Resource = 'tests/**' }
@{ Key = 'testMarkerClasses'; Rule = 'csharpsquid:S1118'; Resource = 'tests/**' }
@{ Key = 'testDuplicateScenarios'; Rule = 'csharpsquid:S4144'; Resource = 'tests/**' }
@{ Key = 'testFixedDoubles'; Rule = 'csharpsquid:S3400'; Resource = 'tests/**' }
@{ Key = 'testComments'; Rule = 'csharpsquid:S125'; Resource = 'tests/**' }
)
$arguments = @(
"/k:$env:SONAR_PROJECT_KEY",
"/o:$env:SONAR_ORGANIZATION",
'/d:sonar.exclusions=artifacts/**',
'/d:sonar.coverage.exclusions=tests/**',
"/d:sonar.cs.vscoveragexml.reportsPaths=$coverage/**/*.xml",
"/k:$env:SONAR_PROJECT_KEY"
"/o:$env:SONAR_ORGANIZATION"
'/d:sonar.exclusions=artifacts/**,tests/CheatEngine.Client.Benchmarks/**'
"/d:sonar.issue.ignore.multicriteria=$(($ignoredIssues.Key) -join ',')"
"/d:sonar.qualitygate.wait=$env:SONAR_WAIT_QUALITY_GATE"
)
foreach ($issue in $ignoredIssues) {
$arguments += "/d:sonar.issue.ignore.multicriteria.$($issue.Key).ruleKey=$($issue.Rule)"
$arguments += "/d:sonar.issue.ignore.multicriteria.$($issue.Key).resourceKey=$($issue.Resource)"
}
& "$env:RUNNER_TEMP/sonar-scanner/dotnet-sonarscanner.exe" begin @arguments

- name: Build
Expand Down
7 changes: 7 additions & 0 deletions CheatEngine.Client.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,21 @@
<Folder Name="/src/">
<Project Path="src/CheatEngine.Client/CheatEngine.Client.csproj"/>
</Folder>
<Folder Name="/source-generators/">
<Project
Path="source-generators/CheatEngine.Client.SourceGenerators.Lua/CheatEngine.Client.SourceGenerators.Lua.csproj"/>
</Folder>
<Folder Name="/tests/">
<Project Path="tests/CheatEngine.Client.Abstractions.Tests/CheatEngine.Client.Abstractions.Tests.csproj"/>
<Project Path="tests/CheatEngine.Client.AotProbe/CheatEngine.Client.AotProbe.csproj"/>
<Project Path="tests/CheatEngine.Client.Benchmarks/CheatEngine.Client.Benchmarks.csproj"/>
<Project Path="tests/CheatEngine.Client.Core.Tests/CheatEngine.Client.Core.Tests.csproj"/>
<Project
Path="tests/CheatEngine.Client.Extensions.DependencyInjection.Tests/CheatEngine.Client.Extensions.DependencyInjection.Tests.csproj"/>
<Project Path="tests/CheatEngine.Client.Fluent.Tests/CheatEngine.Client.Fluent.Tests.csproj"/>
<Project Path="tests/CheatEngine.Client.Hosting.Tests/CheatEngine.Client.Hosting.Tests.csproj"/>
<Project
Path="tests/CheatEngine.Client.SourceGenerators.Lua.Tests/CheatEngine.Client.SourceGenerators.Lua.Tests.csproj"/>
<Project Path="tests/CheatEngine.Client.Tests/CheatEngine.Client.Tests.csproj"/>
</Folder>
<Folder Name="/templates/">
Expand Down
3 changes: 2 additions & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
BeforeTargets="Pack">
<ItemGroup>
<!-- Pack augments ProjectReference with transitive projects. Keep only references declared by this project. -->
<!-- Compiler-only project references are analyzer assets, not runtime Client-layer dependencies. -->
<_CheatEngineClientDirectProjectReference Include="@(ProjectReference)"
Condition="'%(ProjectReference.DefiningProjectName)' == '$(MSBuildProjectName)'"/>
Condition="'%(ProjectReference.DefiningProjectName)' == '$(MSBuildProjectName)' and '%(ProjectReference.ReferenceOutputAssembly)' != 'false'"/>
<_CheatEngineClientSdkReference Include="@(PackageReference)"
Condition="'%(PackageReference.Identity)' == 'CheatEngine.SDK' and '%(PackageReference.DefiningProjectName)' == '$(MSBuildProjectName)'"/>
<_CheatEngineClientSdkRangeReference Include="@(_CheatEngineClientSdkReference)"
Expand Down
4 changes: 3 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
</ItemGroup>

<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>

<ItemGroup Label="Build tooling">
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="5.9.0"/>
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="5.9.0"/>
<GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.401"/>
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="5.6.0"/>
</ItemGroup>
Expand Down
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,12 @@ following table is a delivery statement, not a substitute for a live host check.
| Value scanning | **Capability-gated** | The public state machine exists, but Client session creation stays unavailable until the internal `MemScan`/`FoundList` ownership path passes its Cheat Engine 7.7 x64 live gate |
| Arbitrary Lua source | Policy-gated and off by default | Requires explicit unsafe opt-in; raw Lua state remains hidden |

IPC, remote clients, UI/forms, debugger and breakpoints, Auto Assembler, injection, remote allocations, structures,
hotkeys/timers, speedhack, DBVM, Mono/IL2CPP, and advanced ABI hooks are outside v0.1. They have no placeholder
public API. The full current-state rationale is in [ADR 0004](docs/adr/0004-capability-matrix.md).
Target allocations; assembly and Auto Assembler; remote execution and DLL injection; debugger and breakpoints;
hotkeys and timers; speed; hashing; and DBVM now have public Client API contracts. Each remains
`Unknown` or `Unavailable` until its primitive, ownership, and lifecycle behavior passes the corresponding Cheat
Engine 7.7 x64 live gate. IPC, remote clients, UI/forms, structures, Mono/IL2CPP, and advanced ABI hooks remain
outside v0.1 and have no placeholder public API. The full current-state rationale is in
[ADR 0004](docs/adr/0004-capability-matrix.md).

## AOT, trimming, and deployment

Expand Down Expand Up @@ -258,7 +261,9 @@ dotnet publish tests/CheatEngine.Client.AotProbe/CheatEngine.Client.AotProbe.csp
The [Windows CI workflow](.github/workflows/ci.yml) runs the locked restore, Release build, Microsoft Testing Platform
tests, package API validation, isolated package smoke test, template smoke test, and Native AOT graph probe. The
Cheat Engine 7.7 x64 live suite is opt-in and intentionally excluded from ordinary CI; no CI result should be read as
proof that an untested live-host feature is available.
proof that an untested live-host feature is available. The required success, failure, cleanup, disable, re-enable, and
target-change evidence for every advanced capability is defined in the
[live-capability gate protocol](docs/live-capability-gates.md).

## Security and scope

Expand Down
Loading