Reference version
Commit c45d508 (latest release: 2.1.41-preview)
Background and motivation
bv, Buildvana.Sdk, and Buildvana.Runtime are a lockstep-versioned group; a repository pins its bv in .config/dotnet-tools.json and its SDK in global.json. Today the SDK version check refuses to run usesSdk commands on a mismatch — a guard, not a remedy: a user running a global or dnx-launched bv gets an error and instructions, when what they want is for the right version to just run.
The harm isn't limited to usesSdk commands either: buildvana.jsonc is written to the schema generated from the pinned version's typed model (schemas/buildvana.schema.json), so a bv of any other version deserializes it with a different Buildvana.Runtime model — silent drift or spurious validation errors on commands the SDK check doesn't even gate.
The Angular CLI solved this structurally: the global ng is a launcher that always delegates to a project-local CLI when one exists, printing "The local Angular CLI version is used." — the user types ng anywhere and the repo wins.
Proposed enhancement
- bv detects how it was launched — repo-local (tool manifest), global, or
dnx — or at minimum local vs. non-local.
- When a non-local bv runs in a repository whose tool manifest pins bv, it delegates the entire invocation to the pinned version: ensures it is available (
dotnet tool restore if not yet restored), then runs it with the original arguments, inherited stdio, and forwarded exit code, printing an ng-style info line naming the version that ran when versions differ.
- When delegating to a local version, neither command-line arguments nor the configuration file shall be checked, as they could be compatible with the local
bv version but not necessarily with the delegating one.
- A new subcommand fully updates the repository's Buildvana surface in one operation: tool manifest pin,
global.json SDK pin, config schema reference / buildvana.jsonc migration — everything (name open, e.g. bv update).
Implementation proposals
AppContext.BaseDirectory distinguishes install modes (global tools execute from ~/.dotnet/tools/.store/…, manifest-run tools from the NuGet global packages folder, dnx from its own cache); these layouts are not contractual SDK surface, so detection must degrade gracefully to local-vs-non-local.
Delegation is dotnet tool restore (when needed) + dotnet tool run bv -- <args> in the home directory. A recursion guard (e.g. a BV_DELEGATED env marker on the child) prevents loops on mis-detection; an opt-out flag mirroring --skip-sdk-check covers the rare "run this exact binary" need.
Once delegation lands, the post-update re-run advice and much of WarnIfToolManifestDisagrees simplify or become moot. Evaluate whether the sync-sdk command is still useful: if not, remove it in the same PR. It's been in bv for a very short time and no one is going to miss it if there is a viable or even better alternative, as the full-update command probably is.
Usage examples
$ bv build # global bv 2.2.0; repo pins 2.1.58-preview
Delegating to bv 2.1.58-preview from this repository's tool manifest.
[build proceeds with the pinned version]
$ bv update # name TBD
bv: 2.1.58-preview -> 2.2.0 (tool manifest)
Buildvana.Sdk: 2.1.58-preview -> 2.2.0 (global.json)
buildvana.jsonc: schema reference updated - check
Risks
- Observable behavior change: plain
bv in a pinned repo no longer runs the global binary — that's the point, but it needs docs and changelog.
- One extra process hop per delegated invocation.
dotnet tool restore may touch the network and must fail as a clean BuildFailedException.
- Detection heuristics can misfire (mitigated by the fallback to local-vs-non-local plus the recursion guard).
- No reliance on CI/runner conveniences, per project policy.
Additional information
Grew out of PR #321 review round 4, finding 3.
Acceptance criteria: delegation behavior and the full-update subcommand implemented with tests, documentation updated, and CHANGELOG.md entries under "Unreleased changes" for every public-facing change.
Reference version
Commit c45d508 (latest release: 2.1.41-preview)
Background and motivation
bv, Buildvana.Sdk, and Buildvana.Runtime are a lockstep-versioned group; a repository pins its bv in
.config/dotnet-tools.jsonand its SDK inglobal.json. Today the SDK version check refuses to runusesSdkcommands on a mismatch — a guard, not a remedy: a user running a global ordnx-launched bv gets an error and instructions, when what they want is for the right version to just run.The harm isn't limited to
usesSdkcommands either:buildvana.jsoncis written to the schema generated from the pinned version's typed model (schemas/buildvana.schema.json), so a bv of any other version deserializes it with a differentBuildvana.Runtimemodel — silent drift or spurious validation errors on commands the SDK check doesn't even gate.The Angular CLI solved this structurally: the global
ngis a launcher that always delegates to a project-local CLI when one exists, printing "The local Angular CLI version is used." — the user typesnganywhere and the repo wins.Proposed enhancement
dnx— or at minimum local vs. non-local.dotnet tool restoreif not yet restored), then runs it with the original arguments, inherited stdio, and forwarded exit code, printing an ng-style info line naming the version that ran when versions differ.bvversion but not necessarily with the delegating one.global.jsonSDK pin, config schema reference /buildvana.jsoncmigration — everything (name open, e.g.bv update).Implementation proposals
AppContext.BaseDirectorydistinguishes install modes (global tools execute from~/.dotnet/tools/.store/…, manifest-run tools from the NuGet global packages folder,dnxfrom its own cache); these layouts are not contractual SDK surface, so detection must degrade gracefully to local-vs-non-local.Delegation is
dotnet tool restore(when needed) +dotnet tool run bv -- <args>in the home directory. A recursion guard (e.g. aBV_DELEGATEDenv marker on the child) prevents loops on mis-detection; an opt-out flag mirroring--skip-sdk-checkcovers the rare "run this exact binary" need.Once delegation lands, the post-update re-run advice and much of
WarnIfToolManifestDisagreessimplify or become moot. Evaluate whether thesync-sdkcommand is still useful: if not, remove it in the same PR. It's been inbvfor a very short time and no one is going to miss it if there is a viable or even better alternative, as the full-update command probably is.Usage examples
Risks
bvin a pinned repo no longer runs the global binary — that's the point, but it needs docs and changelog.dotnet tool restoremay touch the network and must fail as a cleanBuildFailedException.Additional information
Grew out of PR #321 review round 4, finding 3.
Acceptance criteria: delegation behavior and the full-update subcommand implemented with tests, documentation updated, and
CHANGELOG.mdentries under "Unreleased changes" for every public-facing change.