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
23 changes: 23 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,29 @@
never bit here; PackagingConventionTests keeps it out.
-->

<!--
Every pack is checked against the last release for API breaks.

The README has claimed "no breaking changes" for two majors running; this makes the claim
mechanical. At pack time the SDK restores each package at the baseline version from
nuget.org and runs ApiCompat over the public surface — a removed or changed member fails the
pack (CP0001/CP0002/...), in CI's Pack job and in the release gate.

The baseline is the last release. When Version is bumped for the next one it stays as the
release being superseded, and PackagingConventionTests requires it to be that release or
the shipped version — never older — so it moves with every bump instead of fossilising.
For a release that breaks on purpose: run
`dotnet pack -c Release /p:ApiCompatGenerateSuppressionFile=true`, read the generated
CompatibilitySuppressions.xml — each entry is one breaking change and belongs in the
changelog's migration notes — commit it, and delete it after the release once the baseline
moves past it. A new package with no release yet sets PackageValidationBaselineVersion
empty in its own csproj until it has one.
-->
<PropertyGroup>
<EnablePackageValidation>true</EnablePackageValidation>
<PackageValidationBaselineVersion>6.2.0</PackageValidationBaselineVersion>
</PropertyGroup>

<!-- Provenance: a consumer stepping into this code gets the exact source it is running.
Source Link ships in the SDK, so no PackageReference is needed. -->
<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace CodoMetis.ValueRanges.Conventions.Tests;
Expand Down Expand Up @@ -136,6 +137,44 @@ public void SharedBuildProperties_DoNotSetGeneratePackageOnBuild()
+ "first and pack with --no-build instead, as the workflows do.");
}

/// <summary>
/// Package validation compares each pack against <c>PackageValidationBaselineVersion</c>. A
/// baseline left behind stops seeing API added since it — a member introduced in 6.3 and
/// removed in 6.4 is invisible to a 6.2 baseline — so it may be the version being shipped
/// (between releases, when <c>Version</c> is the last release) or the release directly before
/// it (once <c>Version</c> is bumped for the next one), and never older. That lags by at most
/// one release and forces the move at every version bump.
/// </summary>
[TestMethod]
public void PackageValidationBaseline_IsTheShippedVersionOrTheReleaseBeforeIt()
{
var properties = SharedBuildProperties();

Assert.AreEqual(
"true", properties.GetValueOrDefault("EnablePackageValidation"),
"Directory.Build.props does not enable package validation, so a removed public member "
+ "packs cleanly and the 'no breaking changes' claim rests on reading the diff.");

var shipped = Version.Parse(RepoLayout.ShippedVersion);
var previous = ChangelogVersions().Where(version => version < shipped).DefaultIfEmpty().Max();
var baseline = properties.GetValueOrDefault("PackageValidationBaselineVersion");

Assert.IsNotNull(baseline, "Directory.Build.props sets no PackageValidationBaselineVersion.");

var allowed = new[] { shipped, previous }.Where(version => version is not null).Distinct().ToList();

Assert.IsTrue(
allowed.Contains(Version.Parse(baseline)),
$"PackageValidationBaselineVersion is {baseline}, but Directory.Build.props ships {shipped} "
+ $"and the release before it is {previous?.ToString() ?? "none"}. The baseline must be one of "
+ "those two — move it to the release just superseded when bumping Version, or API added "
+ "since the old baseline goes unvalidated.");
}

private static IEnumerable<Version> ChangelogVersions() =>
Regex.Matches(File.ReadAllText(RepoLayout.RootChangelog.FullName), @"^##\s*\[(?<version>\d+\.\d+\.\d+)\]", RegexOptions.Multiline)
.Select(match => Version.Parse(match.Groups["version"].Value));

private static Dictionary<string, string> SharedBuildProperties()
{
var document = XDocument.Load(Path.Combine(RepoLayout.Root.FullName, "Directory.Build.props"));
Expand Down