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
9 changes: 9 additions & 0 deletions .github/actions/run-tests/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ runs:
8.0.x
10.0.x

# Restore is ~80s of the build step on a cold runner, and the package set changes rarely.
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('Directory.Packages.props', 'global.json', '**/*.csproj') }}
restore-keys: ${{ runner.os }}-nuget-

- name: .NET Build
shell: bash
run: dotnet build Build.csproj -c Release /p:CI=true
Expand All @@ -65,6 +73,7 @@ runs:
shell: bash
run: >-
dotnet test tests/StackExchange.Redis.Tests/StackExchange.Redis.Tests.csproj
--no-build
-c Release
-f net10.0
--logger trx
Expand Down
9 changes: 8 additions & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ jobs:
redis-cli -p 26381 INFO SERVER | grep redis_version || echo "Failed to get version for port 26381"
continue-on-error: true

# Restore is ~80s of the build step on a cold runner, and the package set changes rarely.
- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('Directory.Packages.props', 'global.json', '**/*.csproj') }}
restore-keys: ${{ runner.os }}-nuget-
- name: .NET Build
run: dotnet build Build.csproj -c Release /p:CI=true
# The analyzer and the props that configures it reach consumers only through the package, and a
Expand Down Expand Up @@ -156,7 +163,7 @@ jobs:
run: |
$exitCode = 0
foreach ($tfm in @("net10.0", "net481")) {
dotnet test tests/StackExchange.Redis.Tests/StackExchange.Redis.Tests.csproj -c Release -f $tfm --logger trx --logger GitHubActions --results-directory ./test-results/ /p:CI=true
dotnet test tests/StackExchange.Redis.Tests/StackExchange.Redis.Tests.csproj --no-build -c Release -f $tfm --logger trx --logger GitHubActions --results-directory ./test-results/ /p:CI=true
if ($LASTEXITCODE -ne 0) {
$exitCode = $LASTEXITCODE
}
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dotnet pack src/StackExchange.Redis/StackExchange.Redis.csproj --no-build -c Rel

- SDK is pinned (`global.json`, `allowPrerelease: false`); CI installs the 6/8/10 runtimes. `LangVersion` is 14.
- `TreatWarningsAsErrors=true` everywhere and `Features=strict` — warnings fail the build. Analyzers (StyleCop + the custom `eng` analyzer + PublicApiAnalyzers) run as part of the build.
- Analyzers run on **one TFM per project** — the newest it builds (see `Directory.Build.targets`), because they cost ~40% of a clean build otherwise and every rule is TFM-agnostic. Source generators still run on every TFM. If you touch code inside a down-level `#if`, build it with `/p:RunAnalyzers=true` to get the full per-TFM sweep. StyleCop applies to `src/` only, not `tests/` or `toys/`.
- Library multi-targets `net461;netstandard2.0;net472;net6.0;net8.0;net10.0`. Conditional compile symbols: `VECTOR_SAFE` (all but net461), `UNIX_SOCKET` (net6.0+). The test project targets `net481;net8.0;net10.0`; `BUILD_CURRENT` is defined on the newest TFM (disables some parallelism for brittle tests).

## Public API tracking (important — easy to trip over)
Expand Down Expand Up @@ -113,6 +114,6 @@ Be consise and direct where possible, but with as much detail as is necessary; n

## Conventions

- Code style is enforced via `.editorconfig` + `Shared.ruleset` + StyleCop; 4-space indent, BOM + final newline on `.cs`, `System.*` usings first, no redundant `this.`. Build will fail on violations.
- Code style is enforced via `.editorconfig` + `Shared.ruleset` + StyleCop (StyleCop in `src/` only); 4-space indent, BOM + final newline on `.cs`, `System.*` usings first, no redundant `this.`. Build will fail on violations. Match the surrounding style in `tests/`/`toys/` too — the rules are simply not enforced there.
- `InternalsVisibleTo` exposes internals to the test/benchmark/server projects, so tests reach into internal types directly.
- `docs/` markdown is the user-facing documentation; update it for user-visible behavior changes.
1 change: 0 additions & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@

<ItemGroup Condition="'$(MSBuildProjectName)' != 'StackExchange.Redis.Build' and '$(MSBuildProjectName)' != 'docker' and '$(MSBuildProjectName)' != 'docs' and '$(MSBuildProjectName)' != '.github'">
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" PrivateAssets="all" />
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
</ItemGroup>

<ItemGroup Condition="'$(MSBuildProjectName)' != 'Build' and '$(MSBuildProjectName)' != 'StackExchange.Redis.Build' and '$(MSBuildProjectName)' != 'docker' and '$(MSBuildProjectName)' != 'docs' and '$(MSBuildProjectName)' != '.github'">
Expand Down
28 changes: 27 additions & 1 deletion Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,30 @@
<ItemGroup>
<EmbeddedFiles Include="$(GeneratedAssemblyInfoFile)"/>
</ItemGroup>
</Project>

<!--
Analyzers are around 40% of a clean build's wall time, and every rule we run is TFM-agnostic: the same
file is analysed once per TargetFramework, so a six-TFM library pays for the same diagnostics six times.
Run them on one TFM per project instead - the newest the project builds, which is the branch of any #if
with the most code behind it. Source generators are unaffected: RunAnalyzers=false only suppresses
DiagnosticAnalyzers, so the generated code is identical on every TFM.

What this gives up: a diagnostic that only fires in down-level conditional code, and (for
PublicApiAnalyzers) public API that exists *only* on an older TFM. Pass /p:RunAnalyzers=true to get the
full per-TFM sweep back - worth doing after touching code inside a down-level #if.

Note this lives in .targets, not .props: TargetFrameworks is only known once the project body has been
evaluated. Single-TFM projects leave TargetFrameworks empty and are untouched.
-->
<PropertyGroup Condition="'$(RunAnalyzers)' == '' and '$(TargetFramework)' != '' and '$(TargetFrameworks)' != ''">
<!-- first match wins, so this list is newest-first rather than a sort we cannot express in MSBuild -->
<_AnalyzedTargetFramework Condition="'$(_AnalyzedTargetFramework)' == '' and $(TargetFrameworks.Contains('net10.0'))">net10.0</_AnalyzedTargetFramework>
<_AnalyzedTargetFramework Condition="'$(_AnalyzedTargetFramework)' == '' and $(TargetFrameworks.Contains('net8.0'))">net8.0</_AnalyzedTargetFramework>
<_AnalyzedTargetFramework Condition="'$(_AnalyzedTargetFramework)' == '' and $(TargetFrameworks.Contains('net6.0'))">net6.0</_AnalyzedTargetFramework>
<_AnalyzedTargetFramework Condition="'$(_AnalyzedTargetFramework)' == '' and $(TargetFrameworks.Contains('netstandard2.0'))">netstandard2.0</_AnalyzedTargetFramework>
<_AnalyzedTargetFramework Condition="'$(_AnalyzedTargetFramework)' == '' and $(TargetFrameworks.Contains('net481'))">net481</_AnalyzedTargetFramework>
<_AnalyzedTargetFramework Condition="'$(_AnalyzedTargetFramework)' == '' and $(TargetFrameworks.Contains('net472'))">net472</_AnalyzedTargetFramework>
<_AnalyzedTargetFramework Condition="'$(_AnalyzedTargetFramework)' == '' and $(TargetFrameworks.Contains('net461'))">net461</_AnalyzedTargetFramework>
<RunAnalyzers Condition="'$(_AnalyzedTargetFramework)' != '' and '$(TargetFramework)' != '$(_AnalyzedTargetFramework)'">false</RunAnalyzers>
</PropertyGroup>
</Project>
4 changes: 4 additions & 0 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
<PackageIcon>seredis.png</PackageIcon>
</PropertyGroup>
<ItemGroup>
<!-- The shipped code only. StyleCop is by far the most expensive analyzer we run (two thirds of all
analyzer time in a clean build), and it was buying nothing in tests/ and toys/ that review does not:
those are not part of the published surface and have no docs requirements. -->
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="all" />
<PackageReference Include="Nerdbank.GitVersioning" PrivateAssets="all" Condition=" '$(DEVCONTAINER)' != 'true' " />
Expand Down
3 changes: 2 additions & 1 deletion src/RESPite.Benchmark/RESPite.Benchmark.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
<ToolCommandName>resp-benchmark</ToolCommandName>
<PackAsTool>true</PackAsTool>
<Title>command-line "RESP" benchmark client, comparable to redis-benchmark</Title>
<GeneratePackageOnBuild Condition="'$(Configuration)'=='Release'">True</GeneratePackageOnBuild>
<!-- no GeneratePackageOnBuild: that packed a nupkg on every Release build (including all of CI, for
nobody's benefit). The tool is still published - the pack step packs Build.csproj, which includes us. -->
<PackageReadmeFile>readme.md</PackageReadmeFile>
<GenerateDocumentationFile>False</GenerateDocumentationFile>
<WarningsAsErrors Condition="'$(Configuration)'=='Debug'">false</WarningsAsErrors>
Expand Down
Loading