| title | Conventions and build configuration |
|---|---|
| description | The MSBuild, packaging, versioning, and CI conventions every ES.FX package follows, centralized in Directory.Build.props and Directory.Packages.props. |
Every project in the ES.FX repository inherits a shared set of build, packaging, and quality rules. You do not configure them per project — they live in two repo-root MSBuild files (Directory.Build.props, Directory.Packages.props) that MSBuild imports automatically for every .csproj beneath them. This page documents those conventions so you know what a new ES.FX.* project gets for free and which rules you must respect.
Note
This page describes the repository's own build system — it is for contributors working on ES.FX. If you only consume the packages, see Installation.
The solution uses the XML-based .slnx format (ES.FX.slnx) at the repo root — there is no .sln. Recent dotnet SDKs pick it up automatically, so dotnet build / dotnet test from the root just work.
The tree is organized by purpose:
| Path | Contents | Packs? |
|---|---|---|
src/ |
The libraries. Every project here builds an ES.FX.* NuGet package. |
Yes |
tests/ |
xUnit test projects (*.Tests) and system-under-test hosts (*.Tests.SUT). |
No |
playground/ |
Runnable sample hosts (Playground.*) that exercise the framework end to end. |
No |
Projects are grouped into solution folders (.src/FX, .src/FX/Additions, .src/Ignite, .src/Ignite/Sparks, tests/…, playground/…) inside ES.FX.slnx. When you add a project, add its <Project Path="…" /> entry to the matching folder.
All NuGet versions are pinned centrally in Directory.Packages.props — ManagePackageVersionsCentrally is true. Individual .csproj files reference packages without a Version attribute:
<!-- In a project .csproj -->
<ItemGroup>
<PackageReference Include="StackExchange.Redis" />
</ItemGroup>The version comes from the central file:
<!-- In Directory.Packages.props -->
<PackageVersion Include="StackExchange.Redis" Version="3.0.11" />Important
Never put a Version on a <PackageReference> inside a project — with Central Package Management on, that is an error. To add or bump a dependency, edit the <PackageVersion> entry in Directory.Packages.props. Dependabot bumps land there too.
CentralPackageTransitivePinningEnabled is false, so only your direct references are pinned; transitive versions are resolved normally by NuGet.
Directory.Build.props applies these PropertyGroup defaults to every project in the repo:
| Setting | Value | Effect |
|---|---|---|
TreatWarningsAsErrors |
true |
A warning fails the build. Fix warnings; do not suppress broadly. |
Nullable |
enable |
Nullable reference types are on everywhere. |
ImplicitUsings |
enable |
Common usings are implicit. |
GenerateDocumentationFile |
true |
XML docs are produced from /// comments. |
DebugType |
embedded |
Debug symbols are embedded in the assembly. |
A curated NoWarn list suppresses the noisier XML-doc diagnostics (CS1591 and friends) and the NuGet audit warnings (NU1901–NU1903) so that missing doc comments and advisories do not break the warnings-as-errors build. JetBrains.Annotations is referenced by every project with PrivateAssets="All" (compile-time only, never flowed to consumers).
Warning
Because warnings are errors, an unused variable, an unhandled nullable case, or an obsolete API will stop the build. Run dotnet build before pushing — CI enforces the same rule.
Any project whose name starts with ES.FX and does not contain .Tests is a package project. Directory.Build.props turns these into packages automatically:
GeneratePackageOnBuildistrue— building the project also produces the.nupkg. No separatedotnet packstep is required.- Packages are written to
.artifacts/nuget(at the repo root, viaPackageOutputPath). - Every package embeds the repo-root
README.md(PackageReadmeFile) andpackage.icon.png(PackageIcon), isAuthors=emberstack/Company=EmberStack, MIT-licensed (PackageLicenseExpression=MIT), and pointsRepositoryUrlat the GitHub repo.
Test projects (*.Tests) opt out: GeneratePackageOnBuild is forced back to false, they are decorated with [ExcludeFromCodeCoverage], and they emit a per-project TRX logger to .artifacts/TestResults.
Note
The README.md and package.icon.png shipped in each package are the repo-root files, shared by every package. There is no per-project README in the .nupkg.
Namespaces mirror folder structure: a type under src/ES.FX.Ignite.StackExchange.Redis/Hosting/ lives in ES.FX.Ignite.StackExchange.Redis.Hosting. Package identity follows the layer:
| Layer | Package name | Type naming |
|---|---|---|
| Core | ES.FX |
BCL-style folders (Collections, Results, Problems, Primitives, …). |
| Addition | ES.FX.Additions.{Library} |
Augments exactly one third-party library. |
| Spark | ES.FX.Ignite.{Provider} |
{Service}Spark, {Service}SparkOptions, {Service}SparkSettings, {Service}HostingExtensions (entry points named Ignite{Service}…). |
| Tests | {Project}.Tests |
System-under-test hosts are {Project}.Tests.SUT; fixtures are {Service}Fixture. |
For the full Spark shape and the Settings-vs-Options split, see Creating a Spark. For scaffolding a whole new package, see Creating a new ES.FX library.
Versions are computed by GitVersion 6.x from GitVersion.yaml, not hand-set in any .csproj:
- Mode is
ContinuousDelivery; the tag prefix isv(matched as[vV]). - The version bumps from commit-message trailers:
+semver: major/+semver: minor/+semver: patch(aliasesbreaking/feature/fixalso work), and+semver: noneto skip a bump. - Branch labels differentiate pre-release builds (
develop,rconrelease/*, per-branch labels onfeature/*, etc.).mainproduces the clean release version.
CI passes the computed version into the build as /p:Version=…, so the number stamped on the assembly and the package comes from Git history — you do not edit version numbers by hand.
A single pipeline, .github/workflows/pipeline.yaml, runs on every branch and pull request:
- Discovery installs .NET
10.xand GitVersion6.x, computes the version, and usesdorny/paths-filterto decide what to run. The source filter (src, which also gates package pushes) matchessrc/**,*.slnx,*.sln, and*.props; the build filter addstests/**andplayground/**. - Build runs
dotnet restore→dotnet build→dotnet test, publishes the TRX test report, and uploads the.nupkgartifacts. Configuration isReleaseonmain,Debugelsewhere. On non-pull-request, non-Dependabot pushes that touched source, this job also pushes the packages to GitHub Packages. - Release runs only on
main: it pushes packages to GitHub Packages and NuGet.org and cuts a GitHub release named/taggedv{version}.
Important
Package publishing is gated. Pull requests and Dependabot-authored runs never push packages. Pushes to NuGet.org and the GitHub release happen only on main; other branches may still push to GitHub Packages when source changes.
- Creating a new ES.FX library — the step-by-step checklist for adding a package that fits these conventions.
- Application hosting — the
ProgramEntry/ProgramEntryBuilderlifecycle wrapper (note:UseSerilog()comes from the Serilog addition, not Hosting). - Testing — xUnit v3, Testcontainers, and the TRX output layout referenced above.
- Installation — consuming the published packages, from a consumer's point of view.
- Development — the contributor landing page.