Repository navigation
Split ServerAdapter into a CI platform, a Git host, and a release publisher - #409
Merged
Merged
Conversation
buildvana.jsonc names no repository for bv release, so a release goes to whatever repository the origin remote of the clone names. repository.url and repository.host name the target repository and its kind of Git host. hosts.github replaces the top-level github section, so that each kind of host gets a section of its own. release.allowUnknownHost and --allow-unknown-host let a release proceed on a host that bv does not know. Nothing reads repository.url, repository.host, or release.allowUnknownHost yet. buildvana.jsonc drops its github section, whose value is the default, so that buildvana.next.jsonc can state hosts.github and still pass RepositoryConfigFilesTests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DotNetService took ServerAdapter, whose GitHubServerAdapter opens the Git repository in its constructor. On GitHub Actions, bv build then failed in a home directory without an origin remote. ServerAdapter.IsCloudBuild also knew GitHub Actions and GitLab CI alone. CIPlatform reads the environment and nothing else. Its GenericCIPlatform detector follows CIEnvironmentDetectorForTelemetry of the .NET CLI, so that bv and dotnet agree on what a CI runner is. GitHubActionsStepOutputWriter writes the GITHUB_OUTPUT file without a byte order mark, and writes a multi-line value in the delimiter form GitHub documents. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Push always went to the remote HEAD tracks, and there was no way to create, push, or fetch a tag. Each operation now takes the remote name. A push the remote rejects now fails, where it used to pass as a success. CommitterIdentity no longer writes to .git/config. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
bv release ran on GitHub Actions alone, pushed to the remote HEAD tracks, and left the tag to GitHub. It now runs on any CI platform, finds the release remote from repository.url, and creates and pushes the tag itself. An unknown host releases without a release page when release.allowUnknownHost is true. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Before bv release, a user had no way to see which CI platform, Git host, release remote, token variable, and committer identity bv detected. A wrong guess showed up as a failed release. InfoCommand prints them, and states why a value cannot be resolved. ReleaseCommitterIdentity resolves the committer identity for both InfoCommand and ReleaseCommand, so that the two cannot disagree. InfoCommand writes to IAnsiConsole.Profile.Out.Writer, because a redirected console wraps text at 80 columns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ReSharper reported UseAwaitUsing on the synchronous disposal of the ServiceProvider in InfoCommandTests.RunAsync. The test now disposes it with await using, in the form DotNetServiceResolutionTests and Program already use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A blank line separated the two breaking-change bullets about bv release from the other bullets of "Changes to existing features". GitHub then rendered the whole list with paragraph spacing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #409 +/- ##
==========================================
+ Coverage 83.37% 86.41% +3.04%
==========================================
Files 285 299 +14
Lines 8473 8744 +271
Branches 1654 1707 +53
==========================================
+ Hits 7064 7556 +492
+ Misses 1132 904 -228
- Partials 277 284 +7 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
rdeago
commented
Oct 23, 2026
rdeago
left a comment
Member
Author
There was a problem hiding this comment.
Review by Claude. Self-review.
The code has no blocking findings. Three documentation findings follow, each in a code-anchored comment.
Non-blocking
CHANGELOG.md: the bullet on the rejected push links a section that no longer describes the push of the commits.terminology.md: "Git host" and "release remote" have no row in the table, and one changelog bullet calls the Git host a server.release.md, "Rollback": the opening sentence ties the rollback to a draft release, which an unknown host never creates.
The changelog bullet on a rejected push linked "Publishing" in release.md, which describes the NuGet push alone. The repository.host row of configuration-file.md linked the same section, which does not describe the host detection. The links now point at "The release tag" and "Preconditions", where those facts are stated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two CHANGELOG.md bullets and "The release commit" of release.md called a Git host a server. The other pages call it a Git host. terminology.md had no row for the Git host or the release remote, so nothing stated which name to use. The two rows now state both names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Rollback" said that bv release undoes its steps when one fails after the draft release exists. On an unknown host there is no draft, yet ServerRelease.BeginAsync registers every rollback action all the same. A reader on such a host would expect a failed release to leave its commits and its tag in place. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rdeago
commented
Oct 23, 2026
rdeago
left a comment
Member
Author
There was a problem hiding this comment.
Review by Claude. Self-review.
👍 Approved.
No blocking findings. The three findings of the first round are fixed at the head commit, and the commits that fix them introduce no new problem.
No test reached the OnPushStatusError handler of GitService.Push. PushBranch_WhenNotFastForward_Throws passes without the handler, because LibGit2Sharp refuses a non-fast-forward push before the remote sees it. TempGitRepo.LockRemoteReference creates the lock file of a reference in a remote that TempGitRepo.AddBareRemote created. The remote then fails to update the reference, and reports it through OnPushStatusError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When a push was not a fast-forward, LibGit2Sharp threw NonFastForwardException before the remote saw the push. Program catches only BuildFailedException, so bv release ended with a stack trace. Any other LibGit2SharpException of GitService.Push, ListRemoteReferences, or FetchTags did the same, such as a failed authentication or an unreachable remote. GitService wraps each one in a BuildFailedException that names the remote, and keeps the original as the inner exception. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Checklist of related issues / discussions
Proposed changes
bv releaseran on GitHub Actions alone, pushed to the remote thatHEADtracks, and left the release tag to GitHub.ServerAdapterheld three things at once: the CI runner, the Git host of the repository, and the release on that host.GitHubServerAdapteralso opened the Git repository in its constructor, sobv buildfailed on GitHub Actions in a home directory without anoriginremote.Three types replace
ServerAdapter, as #402 designs them:CIPlatform, inServices/Platforms/, reads the environment and nothing else.GitHubActionsPlatform,GitLabCIPlatform,GenericCIPlatform, andLocalPlatformimplement it.GitHost, inServices/Hosts/, describes the Git host ofrepository.url.GitHubHostandUnknownHostimplement it.ReleasePublishercreates the release on the host.GitHubReleasePublisherworks over theGitHubApiClientof bv release talks to GitHub over HttpClient, and Octokit goes #403.UnknownHostReleasePublishercreates nothing.The behavior of
bvchanges as follows:bv releaseruns on any machine. It needsrepository.url, a remote of the clone that names that repository, a committer identity, and the token of the host.UrlMatchingReleaseRemoteResolverfinds the release remote. It compares the host name and the path segments, case-insensitive, and ignores the scheme, the port, the user, and a.gitsuffix.bv releasefetches the tags of the release remote before it checks the versions.ServerReleasecreates the release tag at the release commit and pushes it before publication. On a rollback, it deletes the tag, locally and on the remote.GitService.PushTagrefuses to move a remote tag, as Git does.bv releasefails unlessrelease.allowUnknownHostistrue. With the setting, it tags and pushes without a release page.CIPlatformalone, and no longer opens the Git repository. It passesContinuousIntegrationBuild=trueon every CI platform, and the pipeline run ofbv releasepasses it on every platform.ChangelogServicehandles a host without a release URL. The section title gets no link, a relative file link stays as it is, and each case writes a notice.bv infoprints the CI platform, the Git host, the release remote, the token variable, and the committer identity.ReleaseCommitterIdentityresolves the identity for bothbv infoandbv release.buildvana.jsoncgets arepositorysection andrelease.allowUnknownHost.hosts.githubreplaces thegithubsection.buildvana.next.jsoncof this repository statesrepository.urlandhosts.github. The pinnedbvreads neither. Thebvthat this PR releases reads the file that the post-release hook copies over.Where the code differs from the issue
GenericCIPlatformrecognizes a CI runner as the .NET CLI does, where the issue says "CIis set and notfalse". The variables and the values that read as true come fromCIEnvironmentDetectorForTelemetryandEnvironmentVariableParser.ParseBoolin dotnet/sdk. The changelog bullet linksenvironment-variables.md#variables-that-mark-a-ci-runner.github. Thegithubsection never reached a stable release. The unreleased bullet that introducedgithub.tokenEnvnameshosts.github.tokenEnvinstead.HostKind. It holdsAutoandGitHubalone. Publish releases on GitLab #405 and Publish releases on Gitea and Forgejo, and run on Gitea Actions #406 add their values.CIPlatformisFakeCIPlatform, because it records nothing.RecordingHostandRecordingReleasePublisherreplace the two other fakes the issue names.release.md#the-release-tag, and the fetch bullet linksrelease.md#the-release-remote. Both sections are new on the page.The SSH refusal stays
The issue asks to measure whether LibGit2Sharp exposes an anonymous remote. The source of LibGit2Sharp
0.32.0says:ListReferencesalone.Commands.Fetchfalls back togit_remote_create_anonymouswhen its argument names no remote.Network.Pushoverload takes a URL.Remotecomes fromRemoteCollection, whoseAddwrites the repository configuration.bvcannot push torepository.urlwithout adding a remote to.git/config.UrlMatchingReleaseRemoteResolverkeeps refusing a matched remote with an SSH URL.Risks
repository.urlis a new required setting ofbv release. A repository that does not state it cannot release until it does.pushevent fires before the release exists, where it used to fire at publication. A workflow that a tag push triggers runs before the release page exists.Additional changes
GitServicefails when the remote rejects a push. A rejected push used to pass as a success. A push that is not a fast-forward, and any other failure of a push, a fetch, or a reference listing, used to endbvwith an unhandled exception.GitServiceturns each one into aBuildFailedExceptionthat names the remote. The changelog has a bullet for each fix under "Bugs fixed in this release".bv releasekeeps its committer identity in memory, and no longer writesuser.nameanduser.emailto.git/config. The changelog has a bullet under "Changes to existing features".GITHUB_OUTPUT.GitHubActionsStepOutputWriterwrites the file without a byte order mark, and writes a multi-line value in the delimiter form GitHub documents.GitHubServerAdapterappended withEncoding.UTF8, which writes a byte order mark into an empty file.terminology.md. The table names the CI platform, the Git host, and the release remote, with the names they replace. Three older sentences ofCHANGELOG.mdandrelease.mdcalled a Git host a server, and say "Git host" now.Types of changes
This pull request introduces the following types of changes:
.gitattributes,.gitignore)Breaking changes
This pull request introduces breaking changes:
Checklist
🤖 Generated with Claude Code