Guard the public API surface with Verify snapshots (alternative to #530)#540
Guard the public API surface with Verify snapshots (alternative to #530)#540ChrisonSimtian wants to merge 2 commits into
Conversation
The tooling generator now emits [System.CodeDom.Compiler.GeneratedCode] alongside the existing [ExcludeFromCodeCoverage] on every wrapper class, settings/data class, extension class and enumeration it produces. This gives a reflection-visible marker that a metadata-based public-API snapshot can filter on, so the ~60k generated tool symbols can be excluded without the file-level knowledge a Roslyn analyzer has. All 63 *.Generated.cs wrappers regenerated via `./build.sh GenerateTools`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds tests/Fallout.PublicApi.Specs: one Verify snapshot of each consumer-facing assembly's public surface, produced by PublicApiGenerator. A surface change fails the matching case until its .verified.txt is re-accepted, so an accidental break fails CI and an intentional one lands as a reviewable snapshot diff. This is the leaner counterpart to the Roslyn PublicApiAnalyzers guard (Fallout-build#530): one test file plus snapshots under tests/, reusing the mandated xUnit + Verify stack, in place of per-assembly PublicAPI.*.txt in src/ with editorconfig/WarningsAsErrors plumbing. Covers the same 11 assemblies Fallout-build#530 opted in. Generated tool wrappers are excluded by their [GeneratedCode] marker. A metadata-based tool can only exclude whole types, so a class split across a generated and a hand-written partial (the 20 <Tool>Tasks classes) is dropped entirely — ~25 hand-written members that Fallout-build#530's file-level exclusion keeps. That residual gap is the precision ceiling of the approach and the main axis for the two PRs' comparison. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
@dennisdoomen thats another approach... |
Oh, that's quite a disappointment indeed. Although I'm not sure how the approach in FluentAssertions deals with that. |
|
Puh.. either one is a suprisingly "high" effort... For me: either one is fine... Edit: I don't get, though, why the tool wrappers do not count as "public API" here? Since this also ships to the consumer (and at worst break things - I remember times where "suddenly" tool extension were gone or renamed). |
TLDR; I'd like to have something that keeps an eye on our public facing methods and tells us when we break them to:
|
Alternative to #530 for public-API break detection — the leaner, Verify-snapshot approach (the path taken in the TvdbApi modernisation). Raised so the two can be compared side by side. Pick one — not for merge alongside #530. Non-breaking / dev-infra only.
Approach
tests/Fallout.PublicApi.Specs: one Verify snapshot of each consumer-facing assembly's public surface, produced byPublicApiGenerator. A surface change → snapshot mismatch → CI fails until the.verified.txtis re-accepted.PublicApiGenerator); everything else is the mandated xUnit + Verify stack already in the repo.vs #530 (Roslyn PublicApiAnalyzers)
PublicAPI.*.txtinsrc/(×11).verified.txtintests/.editorconfig+Directory.Build.props+WarningsAsErrorsPackageVersion+ one test file.txtdiff.verified.txtdiffGenerated tool wrappers
The ~60k generated tool symbols are excluded via a
[GeneratedCode]marker now stamped byFallout.Tooling.Generator(all 63*.Generated.csregenerated).PublicApiGeneratorreads compiled metadata, so it can only exclude whole types — #530 excludes by file (*.Generated.cs).<Tool>Tasksclasses are split partials (generated half + hand-written half merged into one type). The merged type carries[GeneratedCode], so it is dropped whole — ~25 hand-written members that #530 keeps go unguarded here. Member-level file precision is not reachable by a metadata-based tool. This is the cost of "leaner".Cost of the generated-code stamp
The
[GeneratedCode]attribute is the bulk of the diff: 63 regenerated wrappers (~1.2k lines of pure churn) + the 4 one-line generator edits. Without needing generated exclusion this approach would be ~150 lines; #530's file-level exclusion avoids touching the generator at all.Verified
dotnet buildgreen; specs 11/11 pass (baseline round-trips clean)..verified.txtdiff.AzureKeyVault*,MSBuildProject,DotNetRuntimeIdentifier, …) kept.[GeneratedCode]stamp round-trips throughGenerateTools(theVerifyGeneratedToolsgit-clean gate stays satisfied).Decision
Two viable guards for the same goal. #530 = file-precise, zero gap, more config + a heavier analyzer dep. This = leaner, reuses the existing test stack, but a ~25-member precision gap on split-partial tool tasks and a generator change to stamp
[GeneratedCode].