diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f520958..1f8af8d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
#### Fixed
+- **Public version and documentation metadata now match the actual build** — `nildiff --version`, logs, and generated reports now use the same three-part SemVer as `version.json`, GitHub releases, and NuGet, while `nildiff --doctor` retains the detailed build/commit version for diagnostics. C# badges now reflect the .NET 8 build's C# 12 language version, source-build instructions use the real repository URL, and committed report samples have current public metadata. Affected: `FolderDiffIL4DotNet.Core/Diagnostics/SystemInfo.cs`, `ProgramRunner.cs`, `Runner/ProgramRunner.HelpText.cs`, `README.md`, `USER_GUIDE.md`, `PACKAGE_README.md`, `doc/DEVELOPER_GUIDE.md`, `doc/TESTING_GUIDE.md`, `doc/samples/*`. Tests: `SystemInfoTests`, `ProgramRunnerTests`, `CiAutomationConfigurationTests`.
+
- **Git-based tests no longer inherit user signing configuration** — Temporary repositories now use a shared helper that isolates global/system Git configuration, disables commit/tag signing and hooks locally, and forces non-interactive execution without modifying user configuration. Regression coverage verifies that commits and annotated tags succeed even when the isolated global configuration enables signing. Affected: `FolderDiffIL4DotNet.Tests/Helpers/TestGitRepository.cs`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`, `FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj`, `doc/TESTING_GUIDE.md`. Tests: `TestGitRepositoryTests`, `CiAutomationConfigurationTests`.
### [1.21.0] - 2026-07-22
@@ -1700,6 +1702,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
#### 修正
+- **公開バージョンと文書メタデータを実際のビルドへ統一** — `nildiff --version`、ログ、生成レポートは `version.json`、GitHub リリース、NuGet と同じ 3 要素 SemVer を使い、`nildiff --doctor` では診断用の詳細なビルド/コミットバージョンを維持します。C# バッジを .NET 8 ビルドの C# 12 に合わせ、ソースビルド手順を実際のリポジトリ URL に変更し、コミット済みレポートサンプルの公開メタデータも更新しました。対象: `FolderDiffIL4DotNet.Core/Diagnostics/SystemInfo.cs`, `ProgramRunner.cs`, `Runner/ProgramRunner.HelpText.cs`, `README.md`, `USER_GUIDE.md`, `PACKAGE_README.md`, `doc/DEVELOPER_GUIDE.md`, `doc/TESTING_GUIDE.md`, `doc/samples/*`。テスト: `SystemInfoTests`, `ProgramRunnerTests`, `CiAutomationConfigurationTests`。
+
- **Git ベースのテストがユーザーの署名設定を継承しないよう修正** — 一時リポジトリは、グローバル/システム Git 設定を分離し、commit/tag 署名と hook をローカルで無効化して、ユーザー設定を変更せず非対話実行する共通 helper を使うようになりました。分離済みグローバル設定で署名を有効化しても commit と annotated tag が成功することを回帰テストで検証します。対象: `FolderDiffIL4DotNet.Tests/Helpers/TestGitRepository.cs`, `FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs`, `FolderDiffIL4DotNet.Tests/FolderDiffIL4DotNet.Tests.csproj`, `doc/TESTING_GUIDE.md`。テスト: `TestGitRepositoryTests`, `CiAutomationConfigurationTests`。
### [1.21.0] - 2026-07-22
diff --git a/FolderDiffIL4DotNet.Core/Diagnostics/SystemInfo.cs b/FolderDiffIL4DotNet.Core/Diagnostics/SystemInfo.cs
index 1a3fb939..f1ef5017 100644
--- a/FolderDiffIL4DotNet.Core/Diagnostics/SystemInfo.cs
+++ b/FolderDiffIL4DotNet.Core/Diagnostics/SystemInfo.cs
@@ -47,12 +47,46 @@ public static string GetComputerName()
}
///
- /// Returns the user-facing version string for the assembly containing the given type.
- /// 実行アセンブリの表示用バージョン文字列を取得します。
+ /// Returns the public three-part SemVer for the assembly containing the given type.
+ /// 指定した型を含むアセンブリの公開用 3 要素 SemVer を返します。
///
- /// どのバージョン情報も取得できなかった場合。
+ /// is .
+ /// が の場合。
+ /// No usable public version is available.
+ /// 公開用バージョンを取得できない場合。
public static string GetAppVersion(Type programType)
{
+ ArgumentNullException.ThrowIfNull(programType);
+
+ var assembly = programType.Assembly;
+ var fileVersionAttribute = System.Reflection.CustomAttributeExtensions
+ .GetCustomAttribute(assembly);
+ if (Version.TryParse(fileVersionAttribute?.Version, out var fileVersion) && fileVersion.Build >= 0)
+ {
+ return $"{fileVersion.Major}.{fileVersion.Minor}.{fileVersion.Build}";
+ }
+
+ var fallbackVersion = assembly.GetName().Version;
+ if (fallbackVersion == null || fallbackVersion.Build < 0)
+ {
+ throw new InvalidOperationException(ERROR_VERSION_STRING_EMPTY);
+ }
+
+ return $"{fallbackVersion.Major}.{fallbackVersion.Minor}.{fallbackVersion.Build}";
+ }
+
+ ///
+ /// Returns the detailed build version for diagnostics, including commit metadata when available.
+ /// コミットメタデータを利用できる場合はそれを含む、診断用の詳細ビルドバージョンを返します。
+ ///
+ /// is .
+ /// が の場合。
+ /// No diagnostic version is available.
+ /// 診断用バージョンを取得できない場合。
+ public static string GetDiagnosticAppVersion(Type programType)
+ {
+ ArgumentNullException.ThrowIfNull(programType);
+
var assembly = programType.Assembly;
var infoAttr = System.Reflection.CustomAttributeExtensions
.GetCustomAttribute(assembly);
@@ -63,8 +97,10 @@ public static string GetAppVersion(Type programType)
{
throw new InvalidOperationException(ERROR_VERSION_STRING_EMPTY);
}
+
return verToShow;
}
+
private static string? TryGetEnvironmentMachineName()
{
try
diff --git a/FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs b/FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs
index 96c7bd55..cbdb8143 100644
--- a/FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs
+++ b/FolderDiffIL4DotNet.Tests/Architecture/CiAutomationConfigurationTests.cs
@@ -152,6 +152,50 @@ public void PackageJson_MetadataMatchesRepository()
Assert.Contains("nildiff", packageJson.GetProperty("description").GetString(), StringComparison.OrdinalIgnoreCase);
}
+ ///
+ /// Verifies that public version, language, repository, and sample metadata stay aligned.
+ /// 公開バージョン・言語・リポジトリ・サンプルのメタデータが一致し続けることを検証します。
+ ///
+ [Fact]
+ [Trait("Category", "Unit")]
+ public void PublicMetadata_MatchesVersionJsonAndCurrentBuild()
+ {
+ using var versionDocument = JsonDocument.Parse(File.ReadAllText(GetRepositoryFilePath("version.json")));
+ var publicVersion = versionDocument.RootElement.GetProperty("version").GetString();
+ Assert.False(string.IsNullOrWhiteSpace(publicVersion));
+ Assert.Equal(3, publicVersion!.Split('.').Length);
+
+ var readme = File.ReadAllText(GetRepositoryFilePath("README.md"));
+ var userGuide = File.ReadAllText(GetRepositoryFilePath("USER_GUIDE.md"));
+ var packageReadme = File.ReadAllText(GetRepositoryFilePath("PACKAGE_README.md"));
+ string[] userDocuments = { readme, userGuide, packageReadme };
+
+ foreach (var document in userDocuments)
+ {
+ Assert.DoesNotContain("", document, StringComparison.Ordinal);
+ Assert.DoesNotContain("<リポジトリURL>", document, StringComparison.Ordinal);
+ Assert.Contains("--version", document, StringComparison.Ordinal);
+ }
+
+ const string cloneCommand = "git clone https://github.com/Widthdom/FolderDiffIL4DotNet.git";
+ Assert.Contains(cloneCommand, readme, StringComparison.Ordinal);
+ Assert.Contains(cloneCommand, userGuide, StringComparison.Ordinal);
+ Assert.Contains("C%23-12-", readme, StringComparison.Ordinal);
+ Assert.Contains("C%23-12-", userGuide, StringComparison.Ordinal);
+ Assert.DoesNotContain("C%23-13-", readme, StringComparison.Ordinal);
+ Assert.DoesNotContain("C%23-13-", userGuide, StringComparison.Ordinal);
+ Assert.Contains("C# 12", packageReadme, StringComparison.Ordinal);
+
+ var markdownSample = File.ReadAllText(GetRepositoryFilePath("doc", "samples", "diff_report.md"));
+ var htmlSample = File.ReadAllText(GetRepositoryFilePath("doc", "samples", "diff_report.html"));
+ using var auditSample = JsonDocument.Parse(
+ File.ReadAllText(GetRepositoryFilePath("doc", "samples", "audit_log.json")));
+
+ Assert.Contains($"nildiff {publicVersion}", markdownSample, StringComparison.Ordinal);
+ Assert.Contains($"nildiff {publicVersion}", htmlSample, StringComparison.Ordinal);
+ Assert.Equal(publicVersion, auditSample.RootElement.GetProperty("appVersion").GetString());
+ }
+
///
/// Verifies that CI restores the locked npm dependency graph, runs every Jest test, and blocks high-severity audit findings on a pinned Node.js version.
/// CI が固定 Node.js バージョンで npm のロック済み依存グラフを復元し、全 Jest テストを実行して High 以上の監査検出をブロックすることを検証します。
diff --git a/FolderDiffIL4DotNet.Tests/Core/Diagnostics/SystemInfoTests.cs b/FolderDiffIL4DotNet.Tests/Core/Diagnostics/SystemInfoTests.cs
index e1e01894..84c08903 100644
--- a/FolderDiffIL4DotNet.Tests/Core/Diagnostics/SystemInfoTests.cs
+++ b/FolderDiffIL4DotNet.Tests/Core/Diagnostics/SystemInfoTests.cs
@@ -1,4 +1,6 @@
using System;
+using System.Reflection;
+using System.Reflection.Emit;
using FolderDiffIL4DotNet.Core.Diagnostics;
using Xunit;
@@ -21,22 +23,60 @@ public void GetComputerName_ReturnsNonEmptyString()
[Fact]
public void GetAppVersion_WithValidType_ReturnsNonEmptyString()
{
- // Use the Program class type / Program クラス型を使用
+ var fileVersionAttribute = typeof(FolderDiffIL4DotNet.Program).Assembly
+ .GetCustomAttribute();
+ Assert.NotNull(fileVersionAttribute);
+ Assert.True(Version.TryParse(fileVersionAttribute.Version, out var fileVersion));
+ var expectedVersion = $"{fileVersion.Major}.{fileVersion.Minor}.{fileVersion.Build}";
+
var version = SystemInfo.GetAppVersion(typeof(FolderDiffIL4DotNet.Program));
- Assert.False(string.IsNullOrWhiteSpace(version));
+
+ Assert.Equal(expectedVersion, version);
+ Assert.Equal(3, version.Split('.').Length);
}
[Fact]
- public void GetAppVersion_WithValidType_DoesNotContainPlusGitMetadata()
+ public void GetAppVersion_WithNonZeroPatchVersion_PreservesPatchComponent()
{
- // If informational version contains '+' git hash suffix, verify it's still returned
- // (the method does not strip metadata — this test documents that behavior).
- // InformationalVersion に '+' git ハッシュサフィックスが含まれる場合も
- // そのまま返されることを文書化するテスト。
- var version = SystemInfo.GetAppVersion(typeof(FolderDiffIL4DotNet.Program));
- // The version should be non-null regardless of metadata presence
- // メタデータの有無にかかわらず、バージョンは null であってはならない
- Assert.NotNull(version);
+ var assemblyName = new AssemblyName($"SystemInfoVersionTest_{Guid.NewGuid():N}");
+ var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(
+ assemblyName,
+ AssemblyBuilderAccess.Run);
+ var fileVersionConstructor = typeof(AssemblyFileVersionAttribute)
+ .GetConstructor(new[] { typeof(string) });
+ Assert.NotNull(fileVersionConstructor);
+ assemblyBuilder.SetCustomAttribute(
+ new CustomAttributeBuilder(fileVersionConstructor, new object[] { "1.20.6.42" }));
+ var testType = assemblyBuilder
+ .DefineDynamicModule(assemblyName.Name!)
+ .DefineType("VersionedProgram")
+ .CreateType();
+ Assert.NotNull(testType);
+
+ var version = SystemInfo.GetAppVersion(testType);
+
+ Assert.Equal("1.20.6", version);
+ }
+
+ [Fact]
+ public void GetDiagnosticAppVersion_WithValidType_PreservesDetailedVersion()
+ {
+ var assembly = typeof(FolderDiffIL4DotNet.Program).Assembly;
+ var informationalVersion = System.Reflection.CustomAttributeExtensions
+ .GetCustomAttribute(assembly)
+ ?.InformationalVersion;
+
+ var diagnosticVersion = SystemInfo.GetDiagnosticAppVersion(typeof(FolderDiffIL4DotNet.Program));
+
+ Assert.False(string.IsNullOrWhiteSpace(diagnosticVersion));
+ Assert.StartsWith(
+ SystemInfo.GetAppVersion(typeof(FolderDiffIL4DotNet.Program)),
+ diagnosticVersion,
+ StringComparison.Ordinal);
+ if (!string.IsNullOrWhiteSpace(informationalVersion))
+ {
+ Assert.Equal(informationalVersion, diagnosticVersion);
+ }
}
[Fact]
@@ -76,11 +116,13 @@ public void GetComputerName_ConsecutiveCalls_ReturnsSameValue()
[Fact]
public void GetAppVersion_ConsecutiveCalls_ReturnsSameValue()
{
- // GetAppVersion should return deterministic results across calls.
- // GetAppVersion は呼び出しごとに同じ結果を返すこと。
var version1 = SystemInfo.GetAppVersion(typeof(FolderDiffIL4DotNet.Program));
var version2 = SystemInfo.GetAppVersion(typeof(FolderDiffIL4DotNet.Program));
+ var diagnosticVersion1 = SystemInfo.GetDiagnosticAppVersion(typeof(FolderDiffIL4DotNet.Program));
+ var diagnosticVersion2 = SystemInfo.GetDiagnosticAppVersion(typeof(FolderDiffIL4DotNet.Program));
+
Assert.Equal(version1, version2);
+ Assert.Equal(diagnosticVersion1, diagnosticVersion2);
}
}
}
diff --git a/FolderDiffIL4DotNet.Tests/ProgramRunnerTests.HelpVersion.cs b/FolderDiffIL4DotNet.Tests/ProgramRunnerTests.HelpVersion.cs
index d7ed1920..3979daca 100644
--- a/FolderDiffIL4DotNet.Tests/ProgramRunnerTests.HelpVersion.cs
+++ b/FolderDiffIL4DotNet.Tests/ProgramRunnerTests.HelpVersion.cs
@@ -14,6 +14,7 @@
using FolderDiffIL4DotNet.Services;
using FolderDiffIL4DotNet.Tests.Helpers;
using Xunit;
+using SystemInfo = FolderDiffIL4DotNet.Core.Diagnostics.SystemInfo;
namespace FolderDiffIL4DotNet.Tests
{
@@ -283,6 +284,10 @@ public void WriteDoctorReport_WhenNoDisassemblerAvailable_ReturnsExecutionFailed
Assert.Equal(4, exitCode);
Assert.Contains("nildiff doctor", stdout.ToString(), StringComparison.Ordinal);
+ Assert.Contains(
+ $"Version: {SystemInfo.GetDiagnosticAppVersion(typeof(Program))}",
+ stdout.ToString(),
+ StringComparison.Ordinal);
Assert.Contains("Unavailable", stdout.ToString(), StringComparison.Ordinal);
Assert.Contains("No disassembler tool was detected", stderr.ToString(), StringComparison.Ordinal);
}
@@ -426,7 +431,9 @@ public async Task RunAsync_VersionFlag_ExitsZeroWithVersionOutput()
Assert.Equal(0, exitCode);
var output = sw.ToString().Trim();
- Assert.False(string.IsNullOrWhiteSpace(output), "Version output should not be empty.");
+ Assert.Equal(SystemInfo.GetAppVersion(typeof(Program)), output);
+ Assert.Equal(3, output.Split('.').Length);
+ Assert.DoesNotContain("+", output, StringComparison.Ordinal);
// Logger should NOT have been initialized
// ロガーは初期化されていないはず
Assert.Empty(logger.Messages);
diff --git a/PACKAGE_README.md b/PACKAGE_README.md
index 438472c0..6fa2f7a9 100644
--- a/PACKAGE_README.md
+++ b/PACKAGE_README.md
@@ -23,7 +23,10 @@ nildiff --wizard
# Show help
nildiff --help
-# Check IL disassembler detection
+# Show the public SemVer used by GitHub releases and NuGet
+nildiff --version
+
+# Check the diagnostic build/commit version and IL disassembler detection
nildiff --doctor
```
@@ -77,6 +80,7 @@ Run `nildiff --doctor` to verify whether `dotnet-ildasm` or `ilspycmd` is visibl
## Requirements
- [.NET SDK 8.0](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) or later
+- C# 12 when building from source
## License
diff --git a/ProgramRunner.cs b/ProgramRunner.cs
index 46dd395a..af4e7a34 100644
--- a/ProgramRunner.cs
+++ b/ProgramRunner.cs
@@ -467,7 +467,7 @@ internal static int WriteDoctorReport(
ArgumentNullException.ThrowIfNull(error);
output.WriteLine("nildiff doctor");
- output.WriteLine($"Version: {SystemInfo.GetAppVersion(typeof(Program))}");
+ output.WriteLine($"Version: {SystemInfo.GetDiagnosticAppVersion(typeof(Program))}");
output.WriteLine();
output.WriteLine("IL disassembler probes:");
output.WriteLine(" Tool Status Version Path");
diff --git a/README.md b/README.md
index 2d29dadf..8bdfaa22 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
[](https://github.com/Widthdom/FolderDiffIL4DotNet/actions/workflows/benchmark-regression.yml)

-
+


@@ -49,10 +49,12 @@ Check IL disassembler detection:
nildiff --doctor
```
+`nildiff --version` prints the same public SemVer used by the GitHub release and NuGet package. `nildiff --doctor` retains the detailed build/commit version for diagnostics.
+
Build from source:
```bash
-git clone
+git clone https://github.com/Widthdom/FolderDiffIL4DotNet.git
cd FolderDiffIL4DotNet
dotnet build
dotnet run -- "/path/to/old-folder" "/path/to/new-folder" "my-comparison" --no-pause
@@ -77,7 +79,8 @@ Common options:
| `--print-config` | Print the effective builder state after env-var and supported CLI overrides without semantic validation. |
| `--validate-config` | Validate `config.json` plus `FOLDERDIFF_*` environment-variable overrides before runtime CLI overrides are applied. |
| `--open-reports` | Open the reports folder and exit. |
-| `--doctor` | Probe `dotnet-ildasm` / `ilspycmd` availability and print install guidance. |
+| `--version` | Print the public release/NuGet SemVer and exit. |
+| `--doctor` | Print build/commit diagnostics, probe `dotnet-ildasm` / `ilspycmd` availability, and show install guidance. |
Full CLI behavior, HTML review workflow, integrity checks, semantic-change tables, and configuration details live in [USER_GUIDE.md](USER_GUIDE.md#readme-en-usage).
@@ -165,10 +168,12 @@ IL 逆アセンブラ検出を確認する:
nildiff --doctor
```
+`nildiff --version` は GitHub リリースおよび NuGet パッケージと同じ公開 SemVer を表示します。`nildiff --doctor` では診断用の詳細なビルド/コミットバージョンを引き続き確認できます。
+
ソースからビルドする:
```bash
-git clone
+git clone https://github.com/Widthdom/FolderDiffIL4DotNet.git
cd FolderDiffIL4DotNet
dotnet build
dotnet run -- "/path/to/old-folder" "/path/to/new-folder" "my-comparison" --no-pause
@@ -193,7 +198,8 @@ nildiff [report-label] [options]
| `--print-config` | 環境変数と対応 CLI オーバーライドを適用した builder 状態を、セマンティック検証なしでそのまま出力するため、範囲外を含む effective config の診断にも使えます。 |
| `--validate-config` | [`config.json`](config.json) に `FOLDERDIFF_*` 環境変数オーバーライドを適用した状態を、実行時 CLI オーバーライド適用前に検証します。 |
| `--open-reports` | レポートフォルダを開いて終了します。 |
-| `--doctor` | `dotnet-ildasm` / `ilspycmd` の利用可否を確認し、インストール案内を出します。 |
+| `--version` | 公開リリース/NuGet と同じ SemVer を表示して終了します。 |
+| `--doctor` | ビルド/コミット診断を表示し、`dotnet-ildasm` / `ilspycmd` の利用可否確認とインストール案内を行います。 |
CLI の詳細、HTML レビュー手順、整合性検証、セマンティック変更テーブル、設定詳細は [USER_GUIDE.md](USER_GUIDE.md#readme-ja-usage) を参照してください。
diff --git a/Runner/ProgramRunner.HelpText.cs b/Runner/ProgramRunner.HelpText.cs
index c1141741..a1ea402d 100644
--- a/Runner/ProgramRunner.HelpText.cs
+++ b/Runner/ProgramRunner.HelpText.cs
@@ -16,10 +16,10 @@ public sealed partial class ProgramRunner
" When omitted, a high-resolution timestamp label is auto-generated.\n\n" +
"Options:\n" +
" --help, -h Show this help message and exit.\n" +
- " --version Show the application version and exit.\n" +
+ " --version Show the public SemVer version and exit.\n" +
" --banner Show the ASCII-art banner and exit.\n" +
" --no-banner Suppress the startup banner during normal diff runs.\n" +
- " --doctor Probe IL disassembler availability and print install guidance.\n" +
+ " --doctor Print build/commit diagnostics and probe IL disassembler availability.\n" +
" --print-config Diagnostic: print env+supported CLI overrides as indented JSON without semantic validation.\n" +
" --validate-config Validate config.json + env overrides before runtime CLI overrides (0=valid, 3=invalid).\n" +
" --no-pause Skip key-wait at process end.\n" +
diff --git a/USER_GUIDE.md b/USER_GUIDE.md
index bed03e72..371835cf 100644
--- a/USER_GUIDE.md
+++ b/USER_GUIDE.md
@@ -8,7 +8,7 @@
[](https://github.com/Widthdom/FolderDiffIL4DotNet/actions/workflows/benchmark-regression.yml)

-
+


@@ -34,7 +34,8 @@ nildiff "/path/to/old-folder" "/path/to/new-folder" "my-comparison" --no-pause
```
If you want to inspect the current configuration or open the reports folder, use `nildiff --print-config` or `nildiff --open-reports`.
-If IL comparison falls back to SHA256, run `nildiff --doctor` to check `dotnet-ildasm` / `ilspycmd` detection.
+`nildiff --version` prints the same public SemVer used by the GitHub release and NuGet package.
+If IL comparison falls back to SHA256, run `nildiff --doctor` to inspect the detailed build/commit version and check `dotnet-ildasm` / `ilspycmd` detection.
Generated artifacts are written under `Reports/