Guidance for Claude Code when working in this repository.
Scrutor.Extensions.HttpClient is a tiny (single public class) companion library for
Scrutor. It adds .AsHttpClient() /
.AsHttpClient(name) registration selectors to Scrutor's assembly-scan pipeline so that
each scanned class is registered as a typed HttpClient (via IHttpClientFactory /
AddHttpClient<TInterface,TImpl>()) instead of a plain service.
It exists to solve Scrutor issue #180,
which the Scrutor maintainer declined to ship natively (it would force a
Microsoft.Extensions.Http dependency and needs MakeGenericMethod reflection). As of
Scrutor 7.0.0 / .NET 10 nothing upstream fills this gap, so the library still earns its keep.
services.AddHttpClient("MyClient", c => c.BaseAddress = new Uri("https://api.example.com"));
services.Scan(scan => scan
.FromAssemblyOf<IMyApiClient>()
.AddClasses(c => c.AssignableTo<IMyApiClient>())
.AsMatchingInterface()
.AsHttpClient("MyClient")); // ← all scanned clients become typed clients of "MyClient"CI is defined in C# via Fallout (a NUKE
fork), not hand-written pipelines. The build lives in build/Build.cs.
.github/workflows/build.ymlis GENERATED from the[GitHubActions]attribute on theBuildclass. Never hand-edit it. Regenerate by running any build (./build.cmd) or:dotnet fallout --generate-configuration GitHubActions_build --host GitHubActions.- Run the build locally with the polyglot bootstrapper (bat/sh in one file — works on the
Linux CI runner and locally):
./build.cmd Test # run the *.Specs suite ./build.cmd Pack # Test + pack the NuGet package into artifacts/packages ./build.cmd # default target (Pack)
- Fallout tooling notes (learned the hard way on the TVDB sibling repo): the tool package
Fallout.GlobalToolis singular and lags the libraries at 10.3.49 — keepFallout.Common/Fallout.Componentspinned to10.3.49to match..fallout/parameters.jsonmarks the repo root;.fallout/build.schema.jsonis generated.
.github/workflows/publish.yml is the one hand-maintained workflow. The publish flow
(OIDC trusted publishing + GitHub Packages + GitHub Release) is not expressible via the pinned
Fallout 10.3.49 attribute API, so it is written by hand — this is deliberate, not an oversight.
Publishing is tag-driven. Pushing a v* tag:
- Packs via the Fallout
Packtarget. - Pushes to nuget.org via Trusted Publishing (OIDC) — no stored API key.
- Pushes to GitHub Packages (
nuget.pkg.github.com/Chrison-dev) withGITHUB_TOKEN. - Creates a GitHub Release with the
.nupkgattached and label-categorized notes (.github/release.yml).
One-time nuget.org setup: a Trusted Publisher policy for Scrutor.Extensions.HttpClient
(owner Chrison-dev, repo Scrutor.Extensions.HttpClient, workflow publish.yml, environment
nuget.org) + repo variable NUGET_USER.
The authoritative package version is the static <Version> in Directory.Build.props. We do
not reference the GitVersion.MsBuild package, so CI packs exactly that value. (GitVersion.yml
- the
GitVersion.ToolCLI are kept for local inspection only.) This is a deliberate divergence from the TVDB sibling: GitVersion in ContinuousDelivery mode won't emit a clean prerelease from a tag — a prerelease tag likev7.0.0-preview.1becomes7.0.0-<commitcount>— and we want clean-preview.Npreviews.
Convention: MAJOR tracks the compatible Scrutor major — currently 7.x ↔ Scrutor 7.x (the old 5.x line was Scrutor 5.x; 6.x was skipped/never adopted). MINOR.PATCH is our internal counter.
Release process:
- Set
<Version>/<InformationalVersion>inDirectory.Build.propsto the target (e.g.7.0.0-preview.1for a preview,7.0.0for stable). KeepAssemblyVersion/FileVersionnumeric (7.0.0.0). Land it via PR. - Tag
mainto match and push:git tag v7.0.0-preview.1 && git push origin v7.0.0-preview.1. The tag firespublish.yml; the version comes from the props, the tag names the GitHub Release.
Tests live in tests/*.Specs and follow the Fallout/TVDB convention:
- The test stack (xUnit + FluentAssertions + Verify + PublicApiGenerator) is injected via
tests/Directory.Build.propswith per-package versions — do NOT introduce Central Package Management (deliberate preference). PublicApiSpecssnapshots the public surface with PublicApiGenerator + Verify. The surface is in theMicrosoft.Extensions.DependencyInjectionnamespace, so it must be allow-listed past PublicApiGenerator's defaultSystem/Microsoftdeny-list (AllowNamespacePrefixes). Accept intentional API changes by updating the*.verified.txtbaseline.- Functional specs exercise the real DI container + a stub
HttpMessageHandler(no Mockly / NetArchTest here — nothing to mock or architect in a single-class library).
- Targets net8.0 (Scrutor 7.x supports net8.0+). Multi-targeting is a separate modernization concern, tracked apart from routine changes.
- Scrutor pinned
[7.0.0, 8.0.0)(aligned with the package major) andMicrosoft.Extensions.Http[9.0.0, 11.0.0). The 5→7 bump was clean — the.AsHttpClient()bridge sits on Scrutor's stableRegistrationStrategyseam, so no code/test changes were needed. RootDirectory.Build.propscentralizes TFM + GitVersion; the library csproj only carries metadata. - Still-open modernization (deliberately deferred, discuss before doing): multi-target TFMs, and a
source-generator rewrite to replace the
MakeGenericMethodreflection (the one axis that isn't trim/Native-AOT friendly).