From a9bc03fb01b5d1ac41bf5592f384d22df5425aa4 Mon Sep 17 00:00:00 2001 From: Melodi <62381002+Melodi17@users.noreply.github.com> Date: Sun, 6 Sep 2026 09:02:31 +1000 Subject: feat: vcs and secret management support --- src/marlin.core/functional/SshKeyGitCredential.cs | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 src/marlin.core/functional/SshKeyGitCredential.cs (limited to 'src/marlin.core/functional/SshKeyGitCredential.cs') diff --git a/src/marlin.core/functional/SshKeyGitCredential.cs b/src/marlin.core/functional/SshKeyGitCredential.cs new file mode 100644 index 0000000..9183a66 --- /dev/null +++ b/src/marlin.core/functional/SshKeyGitCredential.cs @@ -0,0 +1,57 @@ +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; + } + } +} -- cgit v1.3.1