Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
42 changes: 39 additions & 3 deletions FolderDiffIL4DotNet.Core/Diagnostics/SystemInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,46 @@ public static string GetComputerName()
}

/// <summary>
/// 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 を返します
/// </summary>
/// <exception cref="InvalidOperationException">どのバージョン情報も取得できなかった場合。</exception>
/// <exception cref="ArgumentNullException"><paramref name="programType"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="programType"/> が <see langword="null"/> の場合。</exception>
/// <exception cref="InvalidOperationException">No usable public version is available.</exception>
/// <exception cref="InvalidOperationException">公開用バージョンを取得できない場合。</exception>
public static string GetAppVersion(Type programType)
{
ArgumentNullException.ThrowIfNull(programType);

var assembly = programType.Assembly;
var fileVersionAttribute = System.Reflection.CustomAttributeExtensions
.GetCustomAttribute<System.Reflection.AssemblyFileVersionAttribute>(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}";
}

/// <summary>
/// Returns the detailed build version for diagnostics, including commit metadata when available.
/// コミットメタデータを利用できる場合はそれを含む、診断用の詳細ビルドバージョンを返します。
/// </summary>
/// <exception cref="ArgumentNullException"><paramref name="programType"/> is <see langword="null"/>.</exception>
/// <exception cref="ArgumentNullException"><paramref name="programType"/> が <see langword="null"/> の場合。</exception>
/// <exception cref="InvalidOperationException">No diagnostic version is available.</exception>
/// <exception cref="InvalidOperationException">診断用バージョンを取得できない場合。</exception>
public static string GetDiagnosticAppVersion(Type programType)
{
ArgumentNullException.ThrowIfNull(programType);

var assembly = programType.Assembly;
var infoAttr = System.Reflection.CustomAttributeExtensions
.GetCustomAttribute<System.Reflection.AssemblyInformationalVersionAttribute>(assembly);
Expand All @@ -63,8 +97,10 @@ public static string GetAppVersion(Type programType)
{
throw new InvalidOperationException(ERROR_VERSION_STRING_EMPTY);
}

return verToShow;
}

private static string? TryGetEnvironmentMachineName()
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,50 @@ public void PackageJson_MetadataMatchesRepository()
Assert.Contains("nildiff", packageJson.GetProperty("description").GetString(), StringComparison.OrdinalIgnoreCase);
}

/// <summary>
/// Verifies that public version, language, repository, and sample metadata stay aligned.
/// 公開バージョン・言語・リポジトリ・サンプルのメタデータが一致し続けることを検証します。
/// </summary>
[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("<repository-url>", 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());
}

/// <summary>
/// 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 以上の監査検出をブロックすることを検証します。
Expand Down
68 changes: 55 additions & 13 deletions FolderDiffIL4DotNet.Tests/Core/Diagnostics/SystemInfoTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Reflection;
using System.Reflection.Emit;
using FolderDiffIL4DotNet.Core.Diagnostics;
using Xunit;

Expand All @@ -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<AssemblyFileVersionAttribute>();
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<System.Reflection.AssemblyInformationalVersionAttribute>(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]
Expand Down Expand Up @@ -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);
}
}
}
9 changes: 8 additions & 1 deletion FolderDiffIL4DotNet.Tests/ProgramRunnerTests.HelpVersion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using FolderDiffIL4DotNet.Services;
using FolderDiffIL4DotNet.Tests.Helpers;
using Xunit;
using SystemInfo = FolderDiffIL4DotNet.Core.Diagnostics.SystemInfo;

namespace FolderDiffIL4DotNet.Tests
{
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 5 additions & 1 deletion PACKAGE_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion ProgramRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Loading
Loading