Fallout-build / Fallout-build/Fallout
Add a NuGet dependency analyzer (redundant & conflicting package references) — CLI + build
- Dominant language
- C#
- Stars
- 154
- Forks
- 19
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 15
Description
## Summary
Add a NuGet dependency analyzer to Fallout that flags **redundant and conflicting package references** across a solution — available both as a CLI command (`dotnet fallout :analyze packages`) and as a build-consumable check with configurable severity.
This is conceptually a "snitch" ([spectresystems/snitch](https://github.com/spectresystems/snitch)) reimagined on top of machinery Fallout already owns — and positioned to do strictly more than snitch did.
## Motivation
Large solutions accumulate `` entries that are no longer necessary:
- the package is already provided by a **referenced project**, or
- the package is already pulled in as a **transitive dependency** of another package you reference, or
- the same package resolves to **different versions** across projects/paths (NuGet silently unifies to the highest, which hides real drift).
Cleaning these up reduces csproj noise, makes the real dependency surface legible, and catches version drift early. Snitch solved part of this for the .NET ecosystem but is unmaintained and, notably, **only ever detected project-reference redundancy** despite its README claiming package-transitive detection — it never walked the NuGet package graph.
Fallout already has the pieces to do the full job (see "Why Fallout is well-positioned").
## Proposed scope (first iteration)
Three detections:
1. **Project-reference redundancy** — a direct `PackageReference` already provided by a referenced project (snitch's actual behavior).
2. **True package-transitive redundancy** — a direct `PackageReference` that is already a transitive dependency of another package you reference (the thing snitch only claimed to do).
3. **Version conflict / divergence** — the same package resolved at different versions across projects or transitive paths.
Out of scope for v1 (candidate follow-ups): pre-release flagging, dependency-graph export (dot/json), auto-fix/csproj rewriting.
## CLI surface
```
dotnet fallout :analyze packages [] [--tfm ] [--severity ] [--exclude ...]
```
- `` — a `.slnx`/`.sln`/`.csproj`; defaults to the solution/project in the working directory.
- `--tfm` — restrict to one target framework (multi-targeted projects analyzed per-TFM otherwise).
- `--severity` — `none|trace|normal|warning|error` — controls the log level findings are emitted at and whether the command returns a non-zero exit code (see below).
- `--exclude` — package IDs to ignore (e.g. analyzers, intentionally-pinned transitives).
`:analyze` is introduced as a command namespace so future analyzers (`:analyze deadcode`, etc.) slot in alongside.
## Build integration & configurable severity
The same analyzer is consumable from a build so a target can enforce hygiene in CI. Severity must be **configurable as a log level**, not just warn-vs-error:
- emit findings at a chosen `LogLevel` (`Trace`/`Normal`/`Warning`/`Error`), and
- optionally fail the build (treat `Error` as build-failing; `Warning` as advisory).
This maps onto the existing `Fallout.Build` `LogLevel` enum + `Host.Warning`/`Host.Error` and the build's success determination.
## Why Fallout is well-positioned (reuse, not greenfield)
| Need | Existing asset |
| --- | --- |
| Resolved transitive package graph (per TFM) | `Fallout.Common.Tooling.NuGetPackageResolver` — already reads `project.assets.json` and walks transitive deps via `NuspecReader.GetDependencyGroups` |
| Declared direct refs + `PrivateAssets`/`AutoReferenced` intent | `Fallout.ProjectModel` (MSBuild evaluation) |
| Project→project edges | `Fallout.Persistence.Solution` (`SolutionProjectModel.Dependencies`) |
| Graph + cycle/topo utilities | `Fallout.Core/Planning/Graph` (`Vertex`, Tarjan SCC, `TopoSort`) |
| Severity/logging | `Fallout.Build` `LogLevel` + `Host` |
| Shared-lib-consumed-by-CLI-and-build precedent | `Fallout.Migrate` + `Fallout.Migrate.Analyzers` |
### Data sources (both)
- **`project.assets.json`** (post-restore) → authoritative resolved transitive graph + unified versions. Required for detections 2 & 3. Implies the target must have been restored; the command will say so if assets are missing.
- **MSBuild evaluation** → declared direct refs + `PrivateAssets`/`AutoReferenced` so we don't recommend removing things that can't be removed or don't flow transitively.
## Correctness guardrails (learned from snitch's edges)
- Exclude `PrivateAssets`-suppressed refs from the "provider" set (they don't flow to consumers).
- Exclude `AutoReferenced` / `SuppressParent=All` (SDK implicit refs) — can't be removed from csproj.
- Analyze **per-TFM**; only recommend removal if redundant across all target frameworks.
- Central Package Management aware (`Directory.Packages.props`, no inline versions) — versions come from the resolved graph.
- Distinguish "safe to remove" from "removable but changes resolved version" (snitch's *can*- vs *might*-be-removed) — removing a higher pin silently downgrades.
## Proposed layout
- `src/Fallout.NuGet.Analysis/` — core analyzer (project graph build + three detections + result model). No console/build coupling.
- `src/Fallout.Cli/Program.Analyze.cs` — `:analyze packages` command surface.
- build target wiring (follow-up) — consume the core lib with a configurable `LogLevel`.
- `tests/Fallout.NuGet.Analysis.Tests/` — fixtures mirroring the snitch fixture style (chained project refs + transitive-providing packages).
## Acceptance criteria (v1)
- [ ] Core analyzer library with the three detections, unit-tested against fixtures.
- [ ] `dotnet fallout :analyze packages` runs against a `.slnx`/`.csproj`, prints a categorized report, returns a severity-driven exit code.
- [ ] Guardrails above implemented (PrivateAssets / AutoReferenced / per-TFM / CPM).
- [ ] Clear messaging when restore is missing.
- [ ] Build-integration hook designed (target wiring may land in a follow-up).
---
> Implementation is being prototyped on a fork first and will be refined before landing here.
Contributor guide
Assessment
This issue has not been assessed yet.