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
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,16 @@ declaring one, handing consumers vulnerability alerts for code they never receiv
SBOM that misdescribes the package is worse than none. Publishing one is a courtesy: the CRA's SBOM
duty falls on commercial consumers for their own products, not on this package.

That filter test flattens the three projects into one set of ids, so it only ever asks whether a
name is *somewhere* private; a second guard asserts a reference marked `PrivateAssets=all` in one
project is marked so in **every** project. Dropping it from a single csproj is invisible to every
other check — the package still builds, the SBOM test still passes — while that one nuspec starts
declaring the dependency and its consumers restore the whole subtree behind it. That is what makes
"consumers never receive it" true, and it is the premise `NU1903` staying a warning rests on
(`Directory.Build.props`), along with the `System.Security.Cryptography.Xml` ignore under `src/` in
`.github/dependabot.yml`. Confirmed by removing the metadata from one satellite: a consumer of the
resulting package inherits all eight advisories.

`.github/workflows/release.yml` publishes on a `v*` tag. `Directory.Build.props` stays the single
source of truth: the tag is verified against it rather than driving it, packages are discovered from
the pack output (so a future satellite needs no workflow edit), and pushes use `--skip-duplicate` so
Expand Down
45 changes: 45 additions & 0 deletions test/EFCore.ComplexIndexes.Tests/PackagingConventionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,51 @@ public void Sbom_filter_covers_every_private_reference()
+ "consumers never receive.");
}

[TestMethod(DisplayName = "A reference marked build-only in one project is build-only in every project")]
public void Build_only_references_are_private_in_every_project()
{
// The test above flattens all three projects into one set of ids, so it only ever asks
// whether a name is *somewhere* private. Drop PrivateAssets=all from a single csproj and the
// other two keep that name in the set: the SBOM test stays green while that one package's
// nuspec starts declaring the dependency, and its consumers restore the whole subtree behind
// it. For Microsoft.EntityFrameworkCore.Design that is ~45 MSBuild/Roslyn components — and
// "consumers never receive it" is the stated reason NU1903 stays a warning
// (Directory.Build.props) and the reason .github/dependabot.yml ignores
// System.Security.Cryptography.Xml under src/. Nothing else checks it per project.
var references = Projects
.Select(project => (
project,
refs: XDocument.Load(project.ProjectFile)
.Descendants("PackageReference")
.Where(reference => reference.Attribute("Include") is not null)
.Select(reference => (Id: reference.Attribute("Include")!.Value,
IsBuildOnly: IsPrivate(reference)))
.ToList()))
.ToList();

var buildOnly = references.SelectMany(entry => entry.refs)
.Where(reference => reference.IsBuildOnly)
.Select(reference => reference.Id)
.ToHashSet(StringComparer.OrdinalIgnoreCase);

Assert.IsNotEmpty(buildOnly, "Expected at least one PrivateAssets=all reference — has the packaging changed?");

var shipped = references
.SelectMany(entry => entry.refs
.Where(reference => !reference.IsBuildOnly && buildOnly.Contains(reference.Id))
.Select(reference => $"{entry.project} -> {reference.Id}"))
.OrderBy(entry => entry, StringComparer.Ordinal)
.ToList();

Assert.IsEmpty(
shipped,
$"{string.Join("; ", shipped)} — marked PrivateAssets=all elsewhere under src/ but not here, "
+ "so this package's nuspec declares it and its consumers restore that dependency's entire "
+ "subtree. Mark it PrivateAssets=all here too; if a consumer genuinely needs it at runtime, "
+ "make it public in every project and drop it from the SBOM --exclude-filter, because it is "
+ "then a dependency they really do take on.");
}

/// <summary>
/// Package validation compares each pack against <c>PackageValidationBaselineVersion</c>. A
/// baseline left behind stops seeing API added since it — a member introduced in 5.1 and
Expand Down
Loading