Skip to content

Commit db9a4c7

Browse files
authored
Merge pull request #2741 from Widthdom/fix-issue2729-2730
Fix git fixture signing and stderr tee failures
2 parents 1976c5c + c01099a commit db9a4c7

9 files changed

Lines changed: 152 additions & 8 deletions

File tree

TESTING_GUIDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ For boundary tests, use the smallest fixture that still crosses the boundary. If
170170

171171
- Never assume global git identity exists.
172172
- Configure repo-local `user.name` and `user.email` inside the test setup.
173+
- Disable repo-local commit/tag signing for fixture repositories so global signing settings cannot prompt or fail non-interactively.
173174
- Use helper methods or `ProcessStartInfo.ArgumentList`; do not depend on shell-specific quoting behavior.
174175

175176
### Database tests
@@ -366,6 +367,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
366367

367368
- global の git identity がある前提にしない。
368369
- テストセットアップ内で repo-local の `user.name``user.email` を設定する。
370+
- fixture リポジトリでは repo-local の commit/tag signing を無効化し、global signing 設定が非対話実行でプロンプトや失敗を起こさないようにする。
369371
- shell 依存の quoting ではなく、ヘルパーや `ProcessStartInfo.ArgumentList` を使う。
370372

371373
### DB 系テスト
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
category: fixed
3+
issues:
4+
- 2729
5+
affected:
6+
- tests/CodeIndex.Tests/TestProjectHelper.cs
7+
- tests/CodeIndex.Tests/GitHelperTests.cs
8+
- tests/CodeIndex.Tests/IndexCommandRunnerTests.cs
9+
- tests/CodeIndex.Tests/GitTestProjectHelperTests.cs
10+
- TESTING_GUIDE.md
11+
---
12+
13+
## English
14+
15+
- **Git-backed test fixtures no longer inherit commit signing (#2729)** — temporary repositories created by test helpers now disable commit and tag signing locally so fixture commits do not prompt for signing-key passphrases.
16+
17+
## 日本語
18+
19+
- **Git を使うテスト fixture が commit signing を引き継がなくなりました (#2729)** — テストヘルパーが作る一時リポジトリでは commit / tag signing を repo-local に無効化し、fixture commit が署名キーのパスフレーズ入力を要求しないようにしました。
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
category: fixed
3+
issues:
4+
- 2730
5+
affected:
6+
- src/CodeIndex/Cli/GlobalToolLog.cs
7+
- tests/CodeIndex.Tests/GlobalToolLogTests.cs
8+
---
9+
10+
## English
11+
12+
- **Persistent stderr mirroring no longer cascades disposed-writer failures (#2730)** — the global tool log tee now treats closed console/log writers as best-effort failures instead of throwing `ObjectDisposedException` back into test or CLI callers.
13+
14+
## 日本語
15+
16+
- **永続 stderr mirror が閉じた writer の失敗を連鎖させなくなりました (#2730)** — global tool log の tee は、閉じた console/log writer をベストエフォートの失敗として扱い、`ObjectDisposedException` をテストや CLI 呼び出し元へ投げ返さないようになりました。

src/CodeIndex/Cli/GlobalToolLog.cs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -638,26 +638,39 @@ private sealed class TeeTextWriter(TextWriter primary, TextWriter secondary) : T
638638

639639
public override void Flush()
640640
{
641-
primary.Flush();
642-
secondary.Flush();
641+
TryWrite(primary.Flush);
642+
TryWrite(secondary.Flush);
643643
}
644644

645645
public override void Write(char value)
646646
{
647-
primary.Write(value);
648-
secondary.Write(value);
647+
TryWrite(() => primary.Write(value));
648+
TryWrite(() => secondary.Write(value));
649649
}
650650

651651
public override void Write(string? value)
652652
{
653-
primary.Write(value);
654-
secondary.Write(value);
653+
TryWrite(() => primary.Write(value));
654+
TryWrite(() => secondary.Write(value));
655655
}
656656

657657
public override void WriteLine(string? value)
658658
{
659-
primary.WriteLine(value);
660-
secondary.WriteLine(value);
659+
TryWrite(() => primary.WriteLine(value));
660+
TryWrite(() => secondary.WriteLine(value));
661+
}
662+
663+
private static void TryWrite(Action write)
664+
{
665+
try
666+
{
667+
write();
668+
}
669+
catch (Exception ex) when (ex is IOException or ObjectDisposedException)
670+
{
671+
// Best-effort mirror: a disposed console writer must not cascade into callers.
672+
// mirror はベストエフォート。閉じた console writer が呼び出し側へ波及しないようにする。
673+
}
661674
}
662675
}
663676
}

tests/CodeIndex.Tests/GitHelperTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,8 @@ private string CreateGitRepo()
699699
RunGit(repoDir, "init");
700700
RunGit(repoDir, "config", "user.name", "CodeIndex Tests");
701701
RunGit(repoDir, "config", "user.email", "tests@example.com");
702+
RunGit(repoDir, "config", "commit.gpgsign", "false");
703+
RunGit(repoDir, "config", "tag.gpgsign", "false");
702704

703705
return repoDir;
704706
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
namespace CodeIndex.Tests;
2+
3+
public class GitTestProjectHelperTests
4+
{
5+
[Fact]
6+
public void InitializeGitRepo_DisablesCommitSigningForFixtureCommits()
7+
{
8+
var projectRoot = TestProjectHelper.CreateTempProject("cdidx_git_signing");
9+
var globalConfig = Path.Combine(projectRoot, "global-gitconfig");
10+
try
11+
{
12+
File.WriteAllText(
13+
globalConfig,
14+
"""
15+
[commit]
16+
gpgsign = true
17+
[gpg]
18+
format = ssh
19+
[user]
20+
signingkey = /definitely/missing/signing-key
21+
""");
22+
using var env = EnvironmentVariableScope.Capture("GIT_CONFIG_GLOBAL");
23+
env.Set("GIT_CONFIG_GLOBAL", globalConfig);
24+
25+
TestProjectHelper.InitializeGitRepo(projectRoot);
26+
File.WriteAllText(Path.Combine(projectRoot, "app.cs"), "class App {}\n");
27+
28+
TestProjectHelper.RunGit(projectRoot, "add", "app.cs");
29+
var commitSigning = TestProjectHelper.RunGit(projectRoot, "config", "--get", "commit.gpgsign").Trim();
30+
var tagSigning = TestProjectHelper.RunGit(projectRoot, "config", "--get", "tag.gpgsign").Trim();
31+
TestProjectHelper.RunGit(projectRoot, "commit", "-m", "fixture");
32+
33+
Assert.Equal("false", commitSigning);
34+
Assert.Equal("false", tagSigning);
35+
}
36+
finally
37+
{
38+
TestProjectHelper.DeleteDirectory(projectRoot);
39+
}
40+
}
41+
}

tests/CodeIndex.Tests/GlobalToolLogTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Globalization;
2+
using System.Text;
23
using System.Text.RegularExpressions;
34
using CodeIndex.Cli;
45

@@ -117,6 +118,52 @@ public void TryStart_WritesInvariantUtcTimestampAndStackTrace()
117118
}
118119
}
119120

121+
[Fact]
122+
public void TryStart_ErrorMirrorIgnoresDisposedOriginalConsoleWriter()
123+
{
124+
var logRoot = Path.Combine(Path.GetTempPath(), $"cdidx_global_log_disposed_{Guid.NewGuid():N}");
125+
var originalError = Console.Error;
126+
try
127+
{
128+
using var env = EnvironmentVariableScope.Capture(
129+
"CDIDX_FORCE_GLOBAL_TOOL_LOG",
130+
"CDIDX_DISABLE_PERSISTENT_LOG",
131+
"CDIDX_GLOBAL_TOOL_LOG_DIR");
132+
env.Set("CDIDX_FORCE_GLOBAL_TOOL_LOG", "1");
133+
env.Set("CDIDX_DISABLE_PERSISTENT_LOG", null);
134+
env.Set("CDIDX_GLOBAL_TOOL_LOG_DIR", logRoot);
135+
136+
using var session = GlobalToolLog.TryStartForTesting(
137+
["status"],
138+
"test",
139+
afterWriterCreated: () => Console.SetError(new ThrowingTextWriter()));
140+
141+
var exception = Record.Exception(() => Console.Error.WriteLine("mirrored error"));
142+
143+
Assert.NotNull(session);
144+
Assert.Null(exception);
145+
}
146+
finally
147+
{
148+
Console.SetError(originalError);
149+
if (Directory.Exists(logRoot))
150+
Directory.Delete(logRoot, recursive: true);
151+
}
152+
}
153+
120154
private static void ThrowForGlobalToolLogTest() =>
121155
throw new InvalidOperationException("global log stack trace test");
156+
157+
private sealed class ThrowingTextWriter : TextWriter
158+
{
159+
public override Encoding Encoding => Encoding.UTF8;
160+
161+
public override void Flush() => throw new ObjectDisposedException(nameof(ThrowingTextWriter));
162+
163+
public override void Write(char value) => throw new ObjectDisposedException(nameof(ThrowingTextWriter));
164+
165+
public override void Write(string? value) => throw new ObjectDisposedException(nameof(ThrowingTextWriter));
166+
167+
public override void WriteLine(string? value) => throw new ObjectDisposedException(nameof(ThrowingTextWriter));
168+
}
122169
}

tests/CodeIndex.Tests/IndexCommandRunnerTests.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8849,6 +8849,8 @@ private static void RunGit(string workDir, params string[] args)
88498849
{
88508850
RunGit(workDir, "config", "user.name", "CodeIndex Tests");
88518851
RunGit(workDir, "config", "user.email", "tests@codeindex.local");
8852+
RunGit(workDir, "config", "commit.gpgsign", "false");
8853+
RunGit(workDir, "config", "tag.gpgsign", "false");
88528854
}
88538855
}
88548856

tests/CodeIndex.Tests/TestProjectHelper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ internal static void InitializeGitRepo(string projectRoot)
2121
RunGit(projectRoot, "init");
2222
RunGit(projectRoot, "config", "user.name", "CodeIndex Tests");
2323
RunGit(projectRoot, "config", "user.email", "tests@codeindex.local");
24+
RunGit(projectRoot, "config", "commit.gpgsign", "false");
25+
RunGit(projectRoot, "config", "tag.gpgsign", "false");
2426

2527
var excludePath = Path.Combine(projectRoot, ".git", "info", "exclude");
2628
Directory.CreateDirectory(Path.GetDirectoryName(excludePath)!);

0 commit comments

Comments
 (0)