Add the Buildvana.Core.Versioning library - #303
Conversation
- Add VersionSpec and VersionSpecChange definitions to Buildvana.Core.Abstractions (namespace Buildvana.Core.Versioning). - Add Buildvana.Core.Versioning: VersionSpec.Parse/TryParse as C# 14 static extension members, the single LibGit2Sharp git-height calculator (NBGV-parity rules), VersioningSettings, and VersioningService exposing the version string forms needed by builds and releases. - Relocate the prereleaseTag configuration key from release to versioning and regenerate the schema. - Cover parsing, branch matching, precision, height, and string forms with TUnit tests on temporary Git repositories. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Drop redundant `using Buildvana.Core;` directives: the files' namespace is Buildvana.Core.Versioning, so the parent namespace is already in scope. - Replace the lambda-based Catch test helper with CatchCreate taking the repository and configuration directly, avoiding closures over a variable disposed in the outer scope (AccessToDisposedClosure). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds the new Buildvana.Core.Versioning library and associated abstractions/config/schema updates as Phase 4 of native versioning work (introducing VERSION parsing, git-height computation, and a settings/service surface), with a dedicated TUnit test suite.
Changes:
- Introduces
Buildvana.Core.Versioning(VERSION parsing via C# 14 extension members,GitHeightCalculator,VersioningSettings,VersioningService). - Adds versioning primitives to
Buildvana.Core.Abstractions(VersionSpec,VersionSpecChange) and relocatesprereleaseTagconfig fromrelease→versioning(including schema regen). - Adds comprehensive TUnit coverage for parsing, settings matching, version string forms, and git-height rules using temporary LibGit2Sharp repos.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Buildvana.Core.Versioning.Tests/Buildvana.Core.Versioning.Tests.csproj | Adds new test project for the versioning library. |
| tests/Buildvana.Core.Versioning.Tests/FixedHomeDirectoryProvider.cs | Test helper to provide a fixed repo root. |
| tests/Buildvana.Core.Versioning.Tests/GitHeightCalculatorTests.cs | Tests git-height computation semantics (unborn HEAD, merges, resets, etc.). |
| tests/Buildvana.Core.Versioning.Tests/TempGitRepo.cs | Test helper for creating and manipulating temp LibGit2Sharp repos. |
| tests/Buildvana.Core.Versioning.Tests/VersioningServiceTests.cs | Tests VersioningService version strings, prerelease handling, and precision. |
| tests/Buildvana.Core.Versioning.Tests/VersioningSettingsTests.cs | Tests branch regex matching behavior and config defaults/errors. |
| tests/Buildvana.Core.Versioning.Tests/VersionSpecParseTests.cs | Tests VersionSpec.Parse/TryParse grammar and error cases. |
| tests/Buildvana.Core.Versioning.Tests/VersionSpecTests.cs | Tests VersionSpec canonical ToString() and equality. |
| src/Buildvana.Core.Versioning/Buildvana.Core.Versioning.csproj | New library project and dependencies. |
| src/Buildvana.Core.Versioning/GitHeightCalculator.cs | Implements LibGit2Sharp-based height calculation with NBGV-parity semantics. |
| src/Buildvana.Core.Versioning/GitHeightResult.cs | Public result record for git-height facts (height/branch/commit). |
| src/Buildvana.Core.Versioning/VersioningService.cs | Reads VERSION, computes height and exposed version strings. |
| src/Buildvana.Core.Versioning/VersioningSettings.cs | Resolves release.branches + versioning config, regex matching with timeout. |
| src/Buildvana.Core.Versioning/VersionSpecExtensions.cs | Implements VersionSpec.Parse/TryParse via C# 14 extension block. |
| src/Buildvana.Core.Versioning/VersionSpecExtensions-private.cs | Holds generated regex backing for parsing. |
| src/Buildvana.Core.Configuration/ReleaseConfig.cs | Removes release.prereleaseTag. |
| src/Buildvana.Core.Configuration/VersioningConfig.cs | Adds versioning.prereleaseTag. |
| src/Buildvana.Core.Abstractions/Versioning/VersionSpec.cs | Adds shared VersionSpec record with canonical ToString(). |
| src/Buildvana.Core.Abstractions/Versioning/VersionSpecChange.cs | Adds VersionSpecChange enum. |
| schemas/buildvana.schema.json | Moves prereleaseTag schema from release → versioning. |
| Buildvana.slnx | Adds the new library and test project to the solution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The version regex does not tolerate leading whitespace. Using Trim() in the error message would be misleading in that case; we'd better be more tolerant by trimming beforehand.
|
@claude please review this PR. |
|
Claude finished @rdeago's task in 5m 25s —— View job Claude finished reviewing this PR
I read the full diff against Re: the disputed Copilot commentsBoth of your rebuttals hold up:
Findings1.
private static string ComputeAssemblyVersion(VersionSpec spec, int height, AssemblyVersionPrecision precision)
{
var minor = precision >= AssemblyVersionPrecision.Minor ? spec.Minor : 0;
var build = precision >= AssemblyVersionPrecision.Build ? height : 0;
return FormattableString.Invariant($"{spec.Major}.{minor}.{build}.0");
}There's no branch that changes behavior for [Arguments(AssemblyVersionPrecision.Build, "2.3.1.0")]
[Arguments(AssemblyVersionPrecision.Revision, "2.3.1.0")]But the enum's XML doc says otherwise:
2. No test exercises the BOM-tolerance path in var content = ((Blob)entry.Target).GetContentText();
var hasBom = content.Length > 0 && content[0] == 0xFEFF;
if (hasBom)
{
content = content[1..];
}This is hand-rolled (unlike the Everything else checks out
Nothing here is a blocker — both are worth a look before this lands, but neither is a correctness bug in the sense of "wrong output for documented behavior" except the |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
src/Buildvana.Core.Versioning/VersioningService.cs:158
ComputeAssemblyVersioncurrently ignoresAssemblyVersionPrecision.Revision(it always emits...build.0), which makes theRevisionsetting ineffective and contradicts the enum's intent (all 4 components significant). Consider encoding the height across build+revision forRevision, and validate component ranges so MSBuild doesn't later fail with an invalid assembly version.
private static string ComputeAssemblyVersion(VersionSpec spec, int height, AssemblyVersionPrecision precision)
{
var minor = precision >= AssemblyVersionPrecision.Minor ? spec.Minor : 0;
var build = precision >= AssemblyVersionPrecision.Build ? height : 0;
return FormattableString.Invariant($"{spec.Major}.{minor}.{build}.0");
The value was a no-op: versions are computed as major.minor.height, so there is no revision component and the fourth part of the assembly version is always 0. Configuring revision produced the same result as build. Build is now the maximum precision. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Pins the behavior of a committed version file with a leading UTF-8 BOM: the commit stays in the version line and the height keeps incrementing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Both findings addressed:
@claude please take another look. |
|
Claude finished @rdeago's task in 2m 9s —— View job Claude finished re-reviewing @rdeago's changes
Both findings from my previous pass check out. 1.
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/Buildvana.Core.Versioning/VersioningService.cs:137
ReadVersionFilecurrently usesTrim(), which also removes leading whitespace. That makes the version file accept inputs like " 1.0" even though the parsing grammar (andVersionSpec.TryParsetests) only tolerate trailing whitespace. UsingTrimEnd()keeps the intended rule: trailing whitespace/newlines are OK, but leading whitespace still fails loudly.
text = text.Trim();
BuildFailedException.ThrowIfNot(
VersionSpec.TryParse(text, out var spec),
$"{path} contains an invalid version specification '{text}'.");
TempGitRepo.WriteFile now takes an optional Encoding (defaulting to BOM-less UTF-8, as before), so the BOM-tolerance test can state its intent with encoderShouldEmitUTF8Identifier: true instead of embedding a \uFEFF escape in the file content. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
01815fe to
0b58c58
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/Buildvana.Core.Versioning/VersionSpecExtensions.cs:36
VersionSpec.Parsetrims the input in the error message (str.Trim()), which can misreport the actual invalid input. For example, an invalid leading-space input like " 1.0" will throw an exception claiming "1.0" is invalid (even though that trimmed value is valid). Consider trimming only trailing whitespace (or not trimming at all) when formatting the message.
Guard.IsNotNull(str);
BuildFailedException.ThrowIfNot(
VersionSpec.TryParse(str, out var result),
$"Invalid version specification '{str.Trim()}'.");
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/Buildvana.Core.Versioning/VersionSpecExtensions.cs:36
- In VersionSpec.Parse, the exception message trims both leading and trailing whitespace via
str.Trim(). This can hide the actual reason parsing failed (e.g., the invalid input " 1.0" would be reported as "1.0", which looks valid and makes debugging harder). Consider trimming only trailing whitespace (newlines) or leaving the input unmodified in the message.
BuildFailedException.ThrowIfNot(
VersionSpec.TryParse(str, out var result),
$"Invalid version specification '{str.Trim()}'.");
Wrap each configured pattern in ^(?:...)$ before matching, so a pattern like main no longer matches domain or main2. Error messages keep quoting the pattern as the user wrote it. Addresses Tenacom#303 (comment) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apply the same ^(?:...)$ wrapping to release.generateDocsFrom matching in the bv tool, simplify its defaults to main/master, and update the release.branches / release.generateDocsFrom config descriptions (and the regenerated schema) to state that patterns must match the whole short branch name. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@claude please review this PR again. |
|
Claude finished @rdeago's task in 1m 4s —— View job Claude finished re-reviewing this PR
Four commits landed since my last pass, and they clean up everything that was still open. 1. Branch-pattern anchoring — resolved (
|
Wrap each configured pattern in ^(?:...)$ before matching, so a pattern like main no longer matches domain or main2. Error messages keep quoting the pattern as the user wrote it. Addresses #303 (comment) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 4 of #267. Closes #293.
What's new
Buildvana.Core.Abstractionsgains the versioning definitions (namespaceBuildvana.Core.Versioning): theVersionSpec(Major, Minor, Prerelease)record with canonicalToString(), and theVersionSpecChangeenum.Buildvana.Core.Versioninglibrary (not packaged standalone; no live code wired to it yet):VersionSpec.Parse/TryParseas C# 14 static extension members implementing theMAJOR.MINOR[-[tag]]grammar (tag text informational; trailing whitespace tolerated).GitHeightCalculator— the single LibGit2Sharp git-height calculator, NBGV-parity rules verified against NBGV's source: a commit belongs to the version line when its committedVERSIONparses to the sameMAJOR.MINOR; each such commit contributes 1 + max(parent heights), so the bump commit has height 1 and merges take the longest path; prerelease-only changes never reset; 0 is reserved for lines with no committed history (uncommittedVERSIONor bump, unborn HEAD).LibGit2Sharp/NuGet.Versioningappear in no public signature.VersioningSettings— resolvesrelease.branches,versioning.prereleaseTag, andversioning.assemblyVersionPrecision(defaultmajor); branch matching with anchored, compiled, culture-invariant regexes and a match timeout.VersioningService— readsVERSION(fails loudly when absent or malformed, and when a prerelease line has no configured tag), computes the height, and exposesSemVer(never carries Git metadata),SimpleVersion,AssemblyVersion(precision-masked),InformationalVersion(short commit id appended on non-public builds, mirroring today's NpmPackageVersion behavior), plusIsPublicRelease,IsPrerelease, andCommitId.prereleaseTagrelocated fromreleasetoversioning(config is unreleased, so no migration concern); schema regenerated.release.branchesmatching (VersioningSettings) andrelease.generateDocsFrommatching (bv'sReleaseSettings) wrap each configured pattern in^(?:…)$, so a pattern must match the whole short branch name —mainno longer matchesdomainormain2. ThegenerateDocsFromdefaults simplify tomain/master, and the config descriptions (and regenerated schema) now document whole-name matching.AssemblyVersionPrecision.Revisionremoved.The value was a no-op: versions are computed as
major.minor.height, so there is no revision component andComputeAssemblyVersionalways emits0as the fourth part of the assembly version. Configuring"revision"produced output identical to"build", soBuildis now the maximum precision. Removed from the enum, the JSON schema, and the corresponding test case. Breaking for config files that used"revision", which is acceptable on the 2.0 preview line.bv's docs-branch matching gains anchoring coverage too.Unchanged
bvbehavior: the tool keeps its own versioning types and NBGV; nothing consumes the new library until Phase 7 (#296). The onlybvchange is thegenerateDocsFromanchoring above. No changelog entry — internal-only, per the issue's acceptance criteria.🤖 Generated with Claude Code