Forge
csharpdeeb25a2
1using System.Diagnostics;
2using CascadeIDE.Contracts;
3
4namespace CascadeIDE.Features.Git.DataAcquisition;
5
6/// <summary>
7/// DAL: запуск внешнего <c>git</c> в рабочем каталоге (процесс, чтение stdout/stderr).
8/// </summary>
9/// <inheritdoc cref="IGitCommandRunner" />
10[IoBoundary]
11public sealed class GitCommandRunner : IGitCommandRunner
12{
13 public const int MaxOutputChars = 250_000;
14
15 public async Task<(bool Success, int ExitCode, string Output)> RunAsync(
16 IReadOnlyList<string> args,
17 string workingDirectory,
18 CancellationToken cancellationToken = default)
19 {
20 if (string.IsNullOrWhiteSpace(workingDirectory) || !Directory.Exists(workingDirectory))
21 return (false, -1, "Workspace path is not available.");
22
23 try
24 {
25 var psi = new ProcessStartInfo("git")
26 {
27 RedirectStandardOutput = true,
28 RedirectStandardError = true,
29 UseShellExecute = false,
30 CreateNoWindow = true,
31 WorkingDirectory = workingDirectory
32 };
33 foreach (var arg in args)
34 psi.ArgumentList.Add(arg);
35
36 using var process = Process.Start(psi);
37 if (process is null)
38 return (false, -1, "Failed to start git process.");
39
40 var acc = new OutputAccumulator(MaxOutputChars);
41
42 async Task PumpAsync(StreamReader reader)
43 {
44 var buffer = new char[4096];
45 while (true)
46 {
47 var read = await reader.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
48 if (read <= 0)
49 break;
50 acc.Append(buffer.AsSpan(0, read));
51 }
52 }
53
54 var pumpOut = PumpAsync(process.StandardOutput);
55 var pumpErr = PumpAsync(process.StandardError);
56
57 await Task.WhenAll(pumpOut, pumpErr).ConfigureAwait(false);
58 await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
59
60 var output = acc.ToStringAndTrim();
61 return (process.ExitCode == 0, process.ExitCode, output);
62 }
63 catch (Exception ex)
64 {
65 return (false, -1, ex.Message);
66 }
67 }
68}
69
View only · write via MCP/CIDE