namespace marlin.core.functional;
using System.Diagnostics;
///
/// SSH auth via a private key. The key is written to a per-invocation temp file
/// (owner-read/write only) and deleted as soon as the git process that used it exits —
/// ssh has no clean way to take key material via stdin/env, so a short-lived file is
/// the least-bad option.
///
public sealed class SshKeyGitCredential : IGitCredential
{
private readonly string _privateKeyPem;
public SshKeyGitCredential(string privateKeyPem)
{
this._privateKeyPem = privateKeyPem;
}
public async Task ApplyAsync(ProcessStartInfo psi)
{
string keyPath = Path.Combine(Path.GetTempPath(), $"marlin_{Guid.NewGuid():N}.key");
await File.WriteAllTextAsync(keyPath, this._privateKeyPem);
if (!OperatingSystem.IsWindows())
File.SetUnixFileMode(keyPath, UnixFileMode.UserRead | UnixFileMode.UserWrite);
psi.Environment["GIT_SSH_COMMAND"] =
$"ssh -i \"{keyPath}\" -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new";
return new TempKeyFile(keyPath);
}
private sealed class TempKeyFile : IAsyncDisposable
{
private readonly string _path;
public TempKeyFile(string path)
{
this._path = path;
}
public ValueTask DisposeAsync()
{
try
{
File.Delete(this._path);
}
catch (IOException)
{
// best-effort cleanup; a leftover temp file is not worth failing the caller over
}
return ValueTask.CompletedTask;
}
}
}