DotnetVersionReader 1.4.2
dotnet tool install --global DotnetVersionReader --version 1.4.2
dotnet new tool-manifest
dotnet tool install --local DotnetVersionReader --version 1.4.2
#tool dotnet:?package=DotnetVersionReader&version=1.4.2
nuke :add-package DotnetVersionReader --version 1.4.2
DotnetVersionReader
A .NET global tool for reading version information from .csproj, .sln, and .slnx files,
for enforcing version bumps in pull requests, and for showing which projects had their version changed.
Installation
dotnet tool install --global DotnetVersionReader
Or from a local build:
dotnet pack src/DotnetVersionReader -c Release
dotnet tool install --global DotnetVersionReader --version <version> --add-source ./src/DotnetVersionReader/bin/Release
Usage
Note that dotnet version and dotnet-version can both be used to invoke the application.
The first one is through the dotnet CLI, while the second one directly invokes this programm.
dotnet version [command] [options]
Commands:
read Reads and displays version information from .csproj files. (default)
check Checks that every project whose source files have changed has had its version bumped.
diff Shows projects whose version has changed (or that are new) relative to a base branch.
dotnet version read — read versions (default)
Reads and displays version information. This is the default command: running
dotnet version with no subcommand is equivalent to dotnet version read.
# Both forms are equivalent:
dotnet version [--input <path>] [options]
dotnet version read [--input <path>] [options]
Options
| Option | Short | Description |
|---|---|---|
--input |
-i |
Path to a .csproj, .sln, .slnx file or a folder. Defaults to the current directory. |
--output |
-o |
Output format: json (default), table, list, or version (single project only). |
--filter |
-f |
Filter in the form XmlNode=Value. Value can be a regex. Repeatable. |
--schema |
Print the JSON schema for --output json and exit. Defaults to false. |
Version resolution
The tool follows MSBuild semantics:
- If
<Version>is set, it is used as-is. - Otherwise the version is
<VersionPrefix>(default1.0.0) optionally followed by-<VersionSuffix>.
Examples
# Current directory – JSON output (default, both forms are equivalent)
dotnet version
dotnet version read
# Specific solution file – table output
dotnet version read --input MySolution.slnx --output table
dotnet version read -i MySolution.slnx -o table
# Only projects that generate a NuGet package
dotnet version read --filter "GeneratePackageOnBuild=true"
# Combine multiple filters (all must match)
dotnet version read -i MySolution.slnx -f "TargetFramework=^net10\.0$" -f "GeneratePackageOnBuild=true"
Sample JSON output
[
{
"Name": "MyLibrary",
"Version": "2.1.0-rc.1",
"Major": 2,
"Minor": 1,
"Patch": 0,
"Suffix": "rc.1"
},
{
"Name": "MyApp",
"Version": "1.0.0",
"Major": 1,
"Minor": 0,
"Patch": 0,
"Suffix": null
}
]
Sample table output
| Name | Version | Major | Minor | Patch | Suffix |
| --------- | ---------- | ----- | ----- | ----- | ------ |
| MyLibrary | 2.1.0-rc.1 | 2 | 1 | 0 | rc.1 |
| MyApp | 1.0.0 | 1 | 0 | 0 | |
Sample list output
MyLibrary 2.1.0-rc.1
MyApp 1.0.0
Sample version output
2.1.0-rc.1
dotnet version check — enforce version bumps in PRs
Checks that every project whose source files have changed (compared to a base branch) has had its version bumped. Designed to run as a CI gate on pull requests.
dotnet version check [--base <ref>] [--input <path>] [--head <ref>] [--output <format>] [--filter <XmlNode=Value>]...
# Short aliases (--base defaults to origin/main):
dotnet version check [-b <ref>] [-i <path>] [--head <ref>] [-o <format>] [-f <XmlNode=Value>]...
Options
| Option | Short | Required | Description |
|---|---|---|---|
--input |
-i |
Path to a .csproj, .sln, .slnx file or a folder. Defaults to the current directory. |
|
--base |
-b |
The git ref to compare against. Defaults to origin/main. |
|
--head |
The git ref for the current state. Defaults to HEAD. |
||
--output |
-o |
Output format: json (default), table, or version (single project only). |
|
--filter |
-f |
Filter in the form XmlNode=Value. Only matching projects are checked. Value can be a regex. Repeatable. |
Exit codes
| Code | Meaning |
|---|---|
0 |
All affected projects have been version-bumped (or no relevant files changed). |
1 |
At least one affected project has not been bumped — the check failed. |
2 |
Usage or argument error (bad input path, git not found, etc.). |
How it works
- Locates all
.csprojfiles from<input>. - Builds a dependency graph: for each project, which files it owns and which other projects it references via
<ProjectReference>. - Collects changed files by unioning: committed diff (
<base>...<head>), staged changes, unstaged tracked changes, and untracked new files — so it works both in a PR context and with local uncommitted modifications. - Determines affected projects transitively: if a library changes, every project that depends on it (directly or indirectly) is also considered affected.
- For each affected project, reads the version on
<base>(viagit show) and compares it to the version in the working tree. - Reports the result and exits with code
1if any version was not bumped.
Examples
# Check current directory against origin/main (default, both are equivalent)
dotnet version check
dotnet version check --base origin/main
# Scope to a specific solution file
dotnet version check --input MySolution.slnx --base origin/main
dotnet version check -i MySolution.slnx -b origin/main
# Table output
dotnet version check --input MySolution.slnx --base origin/main --output table
# Single project, bare version output (useful for scripts)
dotnet version check --input src/MyLib/MyLib.csproj --base origin/main --output version
# Only check projects that produce a NuGet package
dotnet version check --input MySolution.slnx --base origin/main --filter "GeneratePackageOnBuild=true"
Sample JSON output
[
{
"Name": "MyLib",
"FilePath": "src/MyLib/MyLib.csproj",
"HeadVersion": "2.0.0",
"BaseVersion": "1.0.0",
"Status": "Ok"
},
{
"Name": "MyApp",
"FilePath": "src/MyApp/MyApp.csproj",
"HeadVersion": "3.1.0",
"BaseVersion": "3.1.0",
"Status": "BumpRequired"
}
]
Possible Status values:
| Value | Meaning |
|---|---|
Ok |
No relevant files changed, or the version was bumped. |
BumpRequired |
Files changed but the version is the same as on the base branch. |
NewProject |
The project did not exist on the base branch — no bump required. |
Sample table output
| Name | HeadVersion | BaseVersion | Status |
|-------|-------------|-------------|--------------|
| MyLib | 2.0.0 | 1.0.0 | Ok |
| MyApp | 3.1.0 | 3.1.0 | BumpRequired |
GitHub Actions integration
name: Check version bumps
on:
pull_request:
branches: [main]
jobs:
check-versions:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # full history is required for git diff
- uses: actions/setup-dotnet@v4
with:
dotnet version: 10.0.x
- name: Install dotnet version
run: dotnet tool install --global DotnetVersionReader
- name: Check version bumps
run: dotnet version check --input MySolution.slnx --base origin/main
Important:
fetch-depth: 0(or at least enough history to reach the base branch) is required; a shallow clone will causegit diffto fail.
dotnet version diff — show version changes relative to a base branch
Shows all projects whose version has changed (or that are brand-new) compared to a base branch,
or whose <PackageReference>/<ProjectReference> entries changed while the project's own
version stayed the same — in which case a new <VersionPrefix>/<VersionSuffix> is suggested
based on semantic versioning.
Unlike check, this command never exits with a non-zero code based on results — it is a
pure informational diff, useful for release notes, changelogs, or scripting.
dotnet version diff [--base <ref>] [--input <path>] [--head <ref>] [--output <format>] [--filter <XmlNode=Value>]... [--bump]
# Short aliases (--base defaults to origin/main):
dotnet version diff [-b <ref>] [-i <path>] [--head <ref>] [-o <format>] [-f <XmlNode=Value>]... [--fix]
Options
| Option | Short | Description |
|---|---|---|
--input |
-i |
Path to a .csproj, .sln, .slnx file or a folder. Defaults to the current directory. |
--base |
-b |
The git ref to compare against. Defaults to origin/main. |
--head |
The git ref for the current state. Defaults to HEAD. |
|
--output |
-o |
Output format: json (default), table, list, or version (single project only). |
--filter |
-f |
Filter in the form XmlNode=Value. Only matching projects are considered. Value can be a regex. Repeatable. |
--bump |
--fix |
Automatically write the suggested version into each affected .csproj file (only for projects with status DependenciesChanged). Defaults to false. |
Exit codes
| Code | Meaning |
|---|---|
0 |
Command completed successfully (regardless of how many projects changed). |
2 |
Usage or argument error (bad input path, git not found, etc.). |
How it works
Uses the same git/dependency-graph pipeline as check (steps 1–4 are identical), with one
addition: a project is also considered "affected" (step 4) when the nearest
Directory.Packages.props file that applies to it (NuGet Central Package Management, found by
walking up from the project's directory — it usually lives several levels above, shared by many
projects) is among the changed files, even though that file is not physically inside the
project's own directory. This is what makes a CPM-only package bump (i.e. only
Directory.Packages.props changed, no .csproj touched) show up in diff for every project
that references an affected package — and bubbles up transitively to their consumers too.
At step 5, for each affected project:
- If the project is brand-new (didn't exist on
<base>), it is included with statusNewProject. - Otherwise, its resolved version on
<head>is compared to<base>. If they differ, it is included with statusBumped. - Otherwise (the version is unchanged), its
<PackageReference>and<ProjectReference>entries are compared between<head>and<base>:- A
<PackageReference>version bump is classified as a major, minor, or patch semantic-versioning change (based on which component of the dependency's version changed). Package versions that are not declared inline are resolved from the nearestDirectory.Packages.propsfile (NuGet Central Package Management) — read from the same git ref being compared, so history is respected even if the props file has since changed. - An added
<PackageReference>or<ProjectReference>counts as a minor change; a removed one counts as a major change. - If any dependency changed, the project is included with status
DependenciesChangedandSuggestedVersionPrefix/SuggestedVersionSuffixare computed by bumping the project's current version according to the most severe change found (major > minor > patch). The suggested suffix is always empty, since a suggested bump drops any pre-release suffix.
- A
- Projects with no version change and no dependency changes are silently omitted.
When --bump (alias --fix) is passed, every project reported with status DependenciesChanged
has its suggested version written directly into its .csproj file before the result is printed:
- If the project already has a
<Version>element, it is updated in place with the combinedSuggestedVersionPrefix/SuggestedVersionSuffixstring. - Otherwise
<VersionPrefix>is created or updated, and<VersionSuffix>is created/updated (or removed, since a suggested bump always has an empty suffix) alongside it, in the project's first<PropertyGroup>(a new one is created if none exists). - Projects with status
BumpedorNewProjectare left untouched — the author already handled those (or there is nothing to bump for a brand-new project).
Tip: Run
diffwithout--bumpfirst to review the suggested versions, then re-run with--bump(or--fix) once you're happy with them — e.g. as a pre-commit step or a dedicated "auto-bump" CI job that commits the result back to the PR branch.
Examples
# Show changed versions against origin/main (default)
dotnet version diff
dotnet version diff --base origin/main
# Scope to a specific solution file
dotnet version diff --input MySolution.slnx --base origin/main
dotnet version diff -i MySolution.slnx -b origin/main
# Table output
dotnet version diff --input MySolution.slnx --base origin/main --output table
# Simple list output – handy for release notes
dotnet version diff --input MySolution.slnx --base origin/main --output list
# Only projects that produce a NuGet package
dotnet version diff --input MySolution.slnx --base origin/main --filter "GeneratePackageOnBuild=true"
# Automatically apply the suggested version bump to affected .csproj files
dotnet version diff --input MySolution.slnx --base origin/main --bump
dotnet version diff --input MySolution.slnx --base origin/main --fix
Sample JSON output
[
{
"Name": "MyLib",
"FilePath": "src/MyLib/MyLib.csproj",
"HeadVersion": "2.0.0",
"BaseVersion": "1.0.0",
"Status": "Bumped",
"DependencyChanges": [],
"SuggestedVersionPrefix": null,
"SuggestedVersionSuffix": null,
"SuggestedVersion": null
},
{
"Name": "MyNewLib",
"FilePath": "src/MyNewLib/MyNewLib.csproj",
"HeadVersion": "1.0.0",
"BaseVersion": null,
"Status": "NewProject",
"DependencyChanges": [],
"SuggestedVersionPrefix": null,
"SuggestedVersionSuffix": null,
"SuggestedVersion": null
},
{
"Name": "MyApp",
"FilePath": "src/MyApp/MyApp.csproj",
"HeadVersion": "1.2.3",
"BaseVersion": "1.2.3",
"Status": "DependenciesChanged",
"DependencyChanges": [
{
"Kind": "Package",
"Name": "Newtonsoft.Json",
"BaseVersion": "13.0.1",
"HeadVersion": "13.0.2",
"BumpType": "Patch"
}
],
"SuggestedVersionPrefix": "1.2.4",
"SuggestedVersionSuffix": "",
"SuggestedVersion": "1.2.4"
}
]
Possible Status values:
| Value | Meaning |
|---|---|
Bumped |
The project's own version was bumped relative to the base branch. |
NewProject |
The project did not exist on the base branch. |
DependenciesChanged |
The project's own version is unchanged, but a <PackageReference> or <ProjectReference> changed. See SuggestedVersionPrefix/SuggestedVersionSuffix/SuggestedVersion for the recommended new version. |
Each entry in DependencyChanges has a Kind (Package or Project) and a BumpType
(Major, Minor, or Patch) describing the semantic-versioning severity of that single change:
| BumpType | When |
|---|---|
Major |
A dependency was removed, or its major version component changed. |
Minor |
A dependency was added, or its minor version component changed. |
Patch |
A dependency's patch version (or pre-release suffix) changed. |
Sample table output
| Name | HeadVersion | BaseVersion | Status | SuggestedVersion |
|-------|-------------|-------------|----------------------|------------------|
| MyLib | 2.0.0 | 1.0.0 | Bumped | |
| MyApp | 1.2.3 | 1.2.3 | DependenciesChanged | 1.2.4 |
Sample list output
MyLib 2.0.0
MyApp 1.2.4
Note: In
listandversionoutput, a project with statusDependenciesChangedshows the suggested version rather than its (unchanged) current version.
Development
# Restore & build
dotnet build DotnetVersionReader.slnx
# Run tests
dotnet test DotnetVersionReader.slnx
# Pack
dotnet pack DotnetVersionReader.slnx -c Release
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
This package has no dependencies.