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
18 changes: 18 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

[*.cs]
indent_style = space
indent_size = 4
tab_width = 4
dotnet_diagnostic.CA1031.severity = warning

[FolderDiffIL4DotNet.Tests/**/*.cs]
dotnet_diagnostic.CA1031.severity = none

[*.{g,g.i,generated,designer}.cs]
generated_code = true

[**/Generated/**/*.cs]
generated_code = true

[*.{bat,cmd}]
end_of_line = crlf
11 changes: 11 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
* text=auto eol=lf

*.bat text eol=crlf
*.cmd text eol=crlf

*.gif binary
*.ico binary
*.jpg binary
*.jpeg binary
*.png binary
*.zip binary
3 changes: 3 additions & 0 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ jobs:
- name: Restore dependencies
run: dotnet restore FolderDiffIL4DotNet.sln

- name: Verify formatting
run: dotnet format FolderDiffIL4DotNet.sln --verify-no-changes --no-restore --verbosity minimal

- name: Test NuGet audit gate
run: python3 -m unittest discover -s scripts/tests -p 'test_*.py'

Expand Down
34 changes: 34 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,23 @@ Before opening a pull request:
If you are reporting a bug, include the exact command, OS, .NET SDK version, and sanitized output.
Do not paste private paths, customer data, proprietary binaries, or exploit details into public issues.

### Formatting

[`global.json`](global.json) pins the .NET SDK used by developers and CI, and [`.editorconfig`](.editorconfig) is the formatting source of truth. Install the pinned SDK, then run:

```shell
dotnet restore FolderDiffIL4DotNet.sln
dotnet format FolderDiffIL4DotNet.sln --verify-no-changes --no-restore
```

To fix formatting drift locally, replace the verification command with:

```shell
dotnet format FolderDiffIL4DotNet.sln --no-restore
```

Tracked text files use LF through [`.gitattributes`](.gitattributes), except Windows command scripts (`*.bat` and `*.cmd`), which use CRLF. Binary assets are marked as binary. Build output under `bin/` and `obj/`, files recognized by the SDK as generated, and conventional generated C# filenames are not part of the hand-maintained formatting baseline.

## 日本語

`FolderDiffIL4DotNet` の改善に協力していただきありがとうございます。
Expand All @@ -31,3 +48,20 @@ Do not paste private paths, customer data, proprietary binaries, or exploit deta

不具合報告には、実行したコマンド、OS、.NET SDK バージョン、匿名化した出力を含めてください。
public issue に private なパス、顧客データ、プロプライエタリなバイナリ、攻撃詳細を貼らないでください。

### フォーマット

開発環境と CI で使う .NET SDK は [`global.json`](global.json) で固定し、フォーマット規則は [`.editorconfig`](.editorconfig) を正本とします。固定された SDK をインストールしてから、次を実行してください。

```shell
dotnet restore FolderDiffIL4DotNet.sln
dotnet format FolderDiffIL4DotNet.sln --verify-no-changes --no-restore
```

手元でフォーマットのずれを修正するには、検証コマンドの代わりに次を実行します。

```shell
dotnet format FolderDiffIL4DotNet.sln --no-restore
```

追跡対象のテキストファイルは [`.gitattributes`](.gitattributes) により LF に統一します。ただし、Windows コマンドスクリプト(`*.bat` と `*.cmd`)は CRLF とします。バイナリアセットは binary として指定します。`bin/` と `obj/` 配下のビルド出力、SDK が生成済みと認識するファイル、および一般的な生成 C# ファイル名は、手作業で保守するフォーマット基準の対象外です。
14 changes: 7 additions & 7 deletions FolderDiffIL4DotNet.Core/Console/ConsoleBanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,11 @@ public static void PrintGreeting(int hour)
/// </summary>
public static string GetGreeting(int hour) => hour switch
{
>= 0 and < 3 => "I hope you're enjoying hobby time, not working.",
>= 3 and < 5 => "I hope tomorrow is a day you can sleep in...",
>= 5 and < 7 => "You're up early! Did you sleep well?",
>= 7 and < 8 => "Leave the diff to me and go have breakfast!",
>= 8 and < 10 => "Good morning! Have you had breakfast?",
>= 0 and < 3 => "I hope you're enjoying hobby time, not working.",
>= 3 and < 5 => "I hope tomorrow is a day you can sleep in...",
>= 5 and < 7 => "You're up early! Did you sleep well?",
>= 7 and < 8 => "Leave the diff to me and go have breakfast!",
>= 8 and < 10 => "Good morning! Have you had breakfast?",
>= 10 and < 11 => "Breaks matter. How about a coffee?",
>= 11 and < 12 => "Almost lunchtime!",
>= 12 and < 13 => "Leave the diff to me and go have lunch!",
Expand All @@ -80,8 +80,8 @@ public static void PrintGreeting(int hour)
>= 20 and < 21 => "Still have tasks left today? Thank you for your hard work.",
>= 21 and < 22 => "Leave the diff to me and go take a shower!",
>= 22 and < 23 => "Working late. Have you taken a shower?",
>= 23 => "The day is almost over. Take care of your health!",
_ => "Hello!",
>= 23 => "The day is almost over. Take care of your health!",
_ => "Hello!",
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,43 @@ public void DotNetWorkflow_DoesNotSkipDocumentationOnlyChanges()
Assert.DoesNotContain("'**.md'", workflow, StringComparison.Ordinal);
}

/// <summary>
/// Verifies that developers and CI share an exact SDK, formatting rules, and a blocking format gate.
/// 開発環境と CI が同一の SDK・フォーマット規則・ブロッキング形式検証を共有することを検証します。
/// </summary>
[Fact]
[Trait("Category", "Unit")]
public void FormattingBaseline_PinsSdkAndIsEnforcedByCi()
{
var workflow = File.ReadAllText(GetRepositoryFilePath(".github", "workflows", "dotnet.yml"));
var editorConfig = File.ReadAllText(GetRepositoryFilePath(".editorconfig"));
var gitAttributes = File.ReadAllText(GetRepositoryFilePath(".gitattributes"));
var contributing = File.ReadAllText(GetRepositoryFilePath("CONTRIBUTING.md"));
var globalJson = JsonDocument.Parse(File.ReadAllText(GetRepositoryFilePath("global.json"))).RootElement;
var sdk = globalJson.GetProperty("sdk");

Assert.Equal("8.0.423", sdk.GetProperty("version").GetString());
Assert.Equal("disable", sdk.GetProperty("rollForward").GetString());
Assert.False(sdk.GetProperty("allowPrerelease").GetBoolean());
Assert.Contains("global-json-file: global.json", workflow, StringComparison.Ordinal);
Assert.Contains("name: Verify formatting", workflow, StringComparison.Ordinal);
Assert.Contains(
"dotnet format FolderDiffIL4DotNet.sln --verify-no-changes --no-restore --verbosity minimal",
workflow,
StringComparison.Ordinal);
Assert.DoesNotContain("continue-on-error: true\n run: dotnet format", workflow, StringComparison.Ordinal);
Assert.Contains("end_of_line = lf", editorConfig, StringComparison.Ordinal);
Assert.Contains("indent_size = 4", editorConfig, StringComparison.Ordinal);
Assert.Contains("generated_code = true", editorConfig, StringComparison.Ordinal);
Assert.Contains("[*.{bat,cmd}]\nend_of_line = crlf", editorConfig, StringComparison.Ordinal);
Assert.Contains("* text=auto eol=lf", gitAttributes, StringComparison.Ordinal);
Assert.Contains("*.bat text eol=crlf", gitAttributes, StringComparison.Ordinal);
Assert.Contains(
"dotnet format FolderDiffIL4DotNet.sln --verify-no-changes --no-restore",
contributing,
StringComparison.Ordinal);
}

/// <summary>
/// Verifies that npm metadata used for JavaScript tests does not drift from repository metadata.
/// JavaScript テスト用 npm メタデータがリポジトリメタデータからずれないことを検証します。
Expand Down
4 changes: 2 additions & 2 deletions FolderDiffIL4DotNet.Tests/Core/Text/TextDifferTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public void Compute_VeryLargeFilesWithTinyDiff_ProducesInlineDiff()
var result = TextDiffer.Compute(old, @new, contextLines: 0);

Assert.Contains(result, l => l.Kind == TextDiffer.Removed && l.Text == "old-changed");
Assert.Contains(result, l => l.Kind == TextDiffer.Added && l.Text == "new-changed");
Assert.Contains(result, l => l.Kind == TextDiffer.Added && l.Text == "new-changed");
// Should produce a proper diff, not a single Truncated entry
// 単独の Truncated で返すのではなく、正しく差分が得られること
Assert.False(result.Count == 1 && result[0].Kind == TextDiffer.Truncated);
Expand Down Expand Up @@ -302,7 +302,7 @@ public void Compute_HunkHeaderLine_HasZeroLineNumbers()
public void Compute_ContextLines_BothLineNumbersSet()
{
var old = new[] { "ctx", "changed", "ctx2" };
var @new = new[] { "ctx", "new", "ctx2" };
var @new = new[] { "ctx", "new", "ctx2" };

var result = TextDiffer.Compute(old, @new, contextLines: 1);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public async Task DisassembleAsync_WhenVersionLookupFails_UsesFingerprintAndAvoi
Assert.True(countAfterFirstRun > 0);

// Simulate tool binary update by touching mtime to change fingerprint
// ツールバイナリの更新をシミュレートし、mtime 変更でフィンガープリントを変える
// ツールバイナリの更新をシミュレートし、mtime 変更でフィンガープリントを変える
await Task.Delay(1100);
var binaryPath = GetInstalledFakeBinaryPath(binDir, "dotnet-ildasm");
File.SetLastWriteTimeUtc(binaryPath, DateTime.UtcNow);
Expand Down
28 changes: 14 additions & 14 deletions FolderDiffIL4DotNet.Tests/Services/FolderDiffServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -312,20 +312,20 @@ private static ConfigSettings CreateConfig(
int maxParallelism,
List<string> textFileExtensions = null,
bool shouldWarnWhenNewFileTimestampIsOlderThanOldFileTimestamp = true) => new ConfigSettingsBuilder()
{
IgnoredExtensions = new List<string> { ".pdb" },
TextFileExtensions = textFileExtensions ?? new List<string> { ".txt" },
ShouldIncludeUnchangedFiles = true,
ShouldIncludeIgnoredFiles = true,
ShouldOutputILText = false,
ShouldIgnoreILLinesContainingConfiguredStrings = false,
ILIgnoreLineContainingStrings = new List<string>(),
ShouldOutputFileTimestamps = false,
ShouldWarnWhenNewFileTimestampIsOlderThanOldFileTimestamp = shouldWarnWhenNewFileTimestampIsOlderThanOldFileTimestamp,
MaxParallelism = maxParallelism,
OptimizeForNetworkShares = false,
AutoDetectNetworkShares = false
}.Build();
{
IgnoredExtensions = new List<string> { ".pdb" },
TextFileExtensions = textFileExtensions ?? new List<string> { ".txt" },
ShouldIncludeUnchangedFiles = true,
ShouldIncludeIgnoredFiles = true,
ShouldOutputILText = false,
ShouldIgnoreILLinesContainingConfiguredStrings = false,
ILIgnoreLineContainingStrings = new List<string>(),
ShouldOutputFileTimestamps = false,
ShouldWarnWhenNewFileTimestampIsOlderThanOldFileTimestamp = shouldWarnWhenNewFileTimestampIsOlderThanOldFileTimestamp,
MaxParallelism = maxParallelism,
OptimizeForNetworkShares = false,
AutoDetectNetworkShares = false
}.Build();

private FolderDiffService CreateService(ConfigSettings config, ProgressReportService progressReporter, string oldDir, string newDir, string reportDir)
{
Expand Down
14 changes: 7 additions & 7 deletions ProgramRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,13 @@ public async Task<int> RunAsync(string[] args)
{
Console.Beep();
}
#pragma warning disable CA1031 // Beep may throw on platforms without audio support
#pragma warning disable CA1031 // Beep may throw on platforms without audio support
catch (PlatformNotSupportedException)
{
// Console.Beep() is not supported on some platforms (e.g. macOS, some Linux)
// BEL character fallback above should still work
}
#pragma warning restore CA1031
#pragma warning restore CA1031
}

PromptForExitKeyIfNeeded(opts);
Expand All @@ -127,7 +127,7 @@ public async Task<int> RunAsync(string[] args)
/// </summary>
private async Task<ProgramRunResult> RunWithResultAsync(CliOptions opts)
{
#pragma warning disable CA1031 // Top-level application boundary classifies unexpected failures after logging.
#pragma warning disable CA1031 // Top-level application boundary classifies unexpected failures after logging.
try
{
var appVersion = InitializeLoggerAndGetAppVersion();
Expand Down Expand Up @@ -191,7 +191,7 @@ private async Task<ProgramRunResult> RunWithResultAsync(CliOptions opts)
{
return CreateFailureResult(ProgramExitCode.UnexpectedError, ex);
}
#pragma warning restore CA1031
#pragma warning restore CA1031
}

private string InitializeLoggerAndGetAppVersion()
Expand Down Expand Up @@ -240,9 +240,9 @@ private static void OutputCompletionSummaryChart(RunCompletionState state)

Console.WriteLine();
OutputSummaryBar("Unchanged", state.UnchangedCount, total, null);
OutputSummaryBar("Added", state.AddedCount, total, ConsoleColor.Green);
OutputSummaryBar("Removed", state.RemovedCount, total, ConsoleColor.Red);
OutputSummaryBar("Modified", state.ModifiedCount, total, ConsoleColor.Cyan);
OutputSummaryBar("Added", state.AddedCount, total, ConsoleColor.Green);
OutputSummaryBar("Removed", state.RemovedCount, total, ConsoleColor.Red);
OutputSummaryBar("Modified", state.ModifiedCount, total, ConsoleColor.Cyan);
Console.WriteLine();
}

Expand Down
8 changes: 4 additions & 4 deletions Runner/ProgramRunner.OpenFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public sealed partial class ProgramRunner
/// --open-* 失敗処理向けにロガーをベストエフォートで初期化します。
/// これらのコマンドは通常、ログディレクトリに触れずに終了するため、bootstrap ログはフォルダオープン失敗後にのみ実行されます。
/// </summary>
#pragma warning disable CA1031 // Best-effort bootstrap must never leak and override the original open-folder failure.
#pragma warning disable CA1031 // Best-effort bootstrap must never leak and override the original open-folder failure.
private bool TryInitializeLoggerForFolderOpen()
{
try
Expand All @@ -39,7 +39,7 @@ private bool TryInitializeLoggerForFolderOpen()
return false;
}
}
#pragma warning restore CA1031
#pragma warning restore CA1031

private void WriteOpenFolderBootstrapError(Exception ex)
{
Expand Down Expand Up @@ -150,7 +150,7 @@ private int OpenFolder(Func<string> resolveFolderPath)
Console.Error.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, ERROR_OPEN_FOLDER_FAILED, folderPath, openStage, ex.GetType().Name, PathShapeDiagnostics.DescribeState("TargetPath", folderPath), ex.Message));
return (int)ProgramExitCode.ExecutionFailed;
}
#pragma warning disable CA1031 // Application boundary: catch-all for platform-specific process launch failures
#pragma warning disable CA1031 // Application boundary: catch-all for platform-specific process launch failures
catch (Exception ex)
{
if (TryInitializeLoggerForFolderOpen())
Expand All @@ -161,7 +161,7 @@ private int OpenFolder(Func<string> resolveFolderPath)
Console.Error.WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, ERROR_OPEN_FOLDER_FAILED, folderPath, openStage, ex.GetType().Name, PathShapeDiagnostics.DescribeState("TargetPath", folderPath), ex.Message));
return (int)ProgramExitCode.ExecutionFailed;
}
#pragma warning restore CA1031
#pragma warning restore CA1031
}
}
}
10 changes: 5 additions & 5 deletions Runner/RunScopeBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,11 @@ internal static ServiceProvider Build(ConfigSettings config, DiffExecutionContex

// Built-in disassembler provider (.NET assemblies)
// 組み込み逆アセンブラプロバイダ(.NET アセンブリ)
services.AddScoped<IDisassemblerProvider>(sp =>
new DotNetDisassemblerProvider(
sp.GetRequiredService<IDotNetDisassembleService>(),
sp.GetRequiredService<IFileComparisonService>(),
sp.GetRequiredService<ILoggerService>()));
services.AddScoped<IDisassemblerProvider>(sp =>
new DotNetDisassemblerProvider(
sp.GetRequiredService<IDotNetDisassembleService>(),
sp.GetRequiredService<IFileComparisonService>(),
sp.GetRequiredService<ILoggerService>()));

// Register services from loaded plugins / 読み込み済みプラグインからサービスを登録
RegisterPluginServices(services, config, plugins);
Expand Down
Loading
Loading