From f3dffdb82c17daa8231e52dbd71938f062a4ee0b Mon Sep 17 00:00:00 2001 From: Eddy Moulton Date: Fri, 29 May 2026 15:04:50 +1000 Subject: [PATCH 1/7] Block SSH on windows --- .../ArgoCD/Git/AuthenticatingRepositoryFactory.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs index d24b3ee2c..0d65231bc 100644 --- a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs +++ b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.Linq; +using Calamari.Common.Commands; +using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.Logging; using Octopus.Calamari.Contracts.ArgoCD; @@ -29,6 +31,15 @@ public AuthenticatingRepositoryFactory( public RepositoryWrapper CloneRepository(string requestedUrl, string targetRevision) { var gitCredential = gitCredentials.GetValueOrDefault(requestedUrl); + + if (gitCredential is SshKeyGitCredentialDto && CalamariEnvironment.IsRunningOnWindows) + { + throw new CommandException( + $"Cannot clone '{requestedUrl}' using an SSH key credential: " + + "SSH git credentials are not supported when Calamari is running on Windows. " + + "Supply a username/password credential for this repository, or run the deployment on a Linux worker."); + } + switch (gitCredential) { case GitCredentialDto passwordCredential: @@ -61,4 +72,4 @@ public RepositoryWrapper CloneRepository(string requestedUrl, string targetRevis var anonGitConnection = new HttpsGitConnection(null, null, GitCloneSafeUrl.ConvertToUriString(requestedUrl), GitReference.CreateFromString(targetRevision)); return repositoryFactory.CloneRepository(UniqueRepoNameGenerator.Generate(), anonGitConnection); } -} \ No newline at end of file +} From 73ee33400ea20fc7b6cb456e0ccbe140e073fa7c Mon Sep 17 00:00:00 2001 From: Eddy Moulton Date: Fri, 29 May 2026 16:38:28 +1000 Subject: [PATCH 2/7] Turn off tests --- .../ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs b/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs index 3d4593717..977977c10 100644 --- a/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs +++ b/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs @@ -81,6 +81,8 @@ public void AnonymousCloneWhenNoCredentialsMatch() public class SshUrlTests : AuthenticatingRepositoryFactoryTestBase { [Test] + // SSH not currently functional on Windows + [Category(TestCategory.CompatibleOS.OnlyNixOrMac)] public void SshCredentialBranch_IsSelectedAndDispatchesSshKeyGitConnection() { // Use an ssh:// URL so the new strict validation allows it, and mock the factory @@ -108,6 +110,8 @@ public void HttpsCredentialTakesPriorityOverSshWhenBothMatchAnSshUrl() } [Test] + [Category(TestCategory.CompatibleOS.OnlyNixOrMac)] + // SSH not currently functional on Windows public void KnownHostsFromDtoAreCarriedOntoSshKeyGitConnection() { const string sshUrl = "ssh://git@github.com/org/repo.git"; From 1d18098eb993838331b7f4ad6ad39c749b16e244 Mon Sep 17 00:00:00 2001 From: Eddy Moulton Date: Fri, 29 May 2026 16:39:30 +1000 Subject: [PATCH 3/7] Comment out of order --- .../ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs b/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs index 977977c10..5d559298f 100644 --- a/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs +++ b/source/Calamari.Tests/ArgoCD/Git/AuthenticatingRepositoryFactoryTests.cs @@ -110,8 +110,8 @@ public void HttpsCredentialTakesPriorityOverSshWhenBothMatchAnSshUrl() } [Test] - [Category(TestCategory.CompatibleOS.OnlyNixOrMac)] // SSH not currently functional on Windows + [Category(TestCategory.CompatibleOS.OnlyNixOrMac)] public void KnownHostsFromDtoAreCarriedOntoSshKeyGitConnection() { const string sshUrl = "ssh://git@github.com/org/repo.git"; From 911941ae7e2d8dec1c85433f6f8231dd46c0a664 Mon Sep 17 00:00:00 2001 From: Eddy Moulton Date: Mon, 1 Jun 2026 10:49:31 +1000 Subject: [PATCH 4/7] Add log to debug --- source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs index 0d65231bc..63215e134 100644 --- a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs +++ b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs @@ -32,6 +32,9 @@ public RepositoryWrapper CloneRepository(string requestedUrl, string targetRevis { var gitCredential = gitCredentials.GetValueOrDefault(requestedUrl); + var isSsh = gitCredential is SshKeyGitCredentialDto ? "isSshKey" : "notSsh"; + log.Info($"DEBUG - running on {Environment.OSVersion.Platform} ({CalamariEnvironment.IsRunningOnWindows}) ({isSsh})"); + if (gitCredential is SshKeyGitCredentialDto && CalamariEnvironment.IsRunningOnWindows) { throw new CommandException( From 94f217661da72173e092ebf17ff31492db180b6a Mon Sep 17 00:00:00 2001 From: Eddy Moulton Date: Mon, 1 Jun 2026 15:50:39 +1000 Subject: [PATCH 5/7] Fix location of exception --- .../Git/AuthenticatingRepositoryFactory.cs | 12 ----------- .../Calamari/ArgoCD/Git/RepositoryFactory.cs | 4 +++- source/Calamari/ArgoCD/Git/WindowsSshKeys.cs | 20 +++++++++++++++++++ 3 files changed, 23 insertions(+), 13 deletions(-) create mode 100644 source/Calamari/ArgoCD/Git/WindowsSshKeys.cs diff --git a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs index 63215e134..7079deefd 100644 --- a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs +++ b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs @@ -31,18 +31,6 @@ public AuthenticatingRepositoryFactory( public RepositoryWrapper CloneRepository(string requestedUrl, string targetRevision) { var gitCredential = gitCredentials.GetValueOrDefault(requestedUrl); - - var isSsh = gitCredential is SshKeyGitCredentialDto ? "isSshKey" : "notSsh"; - log.Info($"DEBUG - running on {Environment.OSVersion.Platform} ({CalamariEnvironment.IsRunningOnWindows}) ({isSsh})"); - - if (gitCredential is SshKeyGitCredentialDto && CalamariEnvironment.IsRunningOnWindows) - { - throw new CommandException( - $"Cannot clone '{requestedUrl}' using an SSH key credential: " - + "SSH git credentials are not supported when Calamari is running on Windows. " - + "Supply a username/password credential for this repository, or run the deployment on a Linux worker."); - } - switch (gitCredential) { case GitCredentialDto passwordCredential: diff --git a/source/Calamari/ArgoCD/Git/RepositoryFactory.cs b/source/Calamari/ArgoCD/Git/RepositoryFactory.cs index 6b70ec16b..771c9b83b 100644 --- a/source/Calamari/ArgoCD/Git/RepositoryFactory.cs +++ b/source/Calamari/ArgoCD/Git/RepositoryFactory.cs @@ -1,10 +1,10 @@ using System; using System.IO; using System.Linq; -using System.Text; using System.Threading; using Calamari.ArgoCD.Git.PullRequests; using Calamari.Common.Commands; +using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.Extensions; using Calamari.Common.Plumbing.FileSystem; using Calamari.Common.Plumbing.Logging; @@ -48,6 +48,8 @@ public RepositoryFactory(ILog log, ICalamariFileSystem fileSystem, string reposi public RepositoryWrapper CloneRepository(string repositoryName, IGitConnection gitConnection) { + WindowsSshKeys.AssertSupported(gitConnection); + var repositoryPath = Path.Combine(repositoryParentDirectory, repositoryName); fileSystem.CreateDirectory(repositoryPath); diff --git a/source/Calamari/ArgoCD/Git/WindowsSshKeys.cs b/source/Calamari/ArgoCD/Git/WindowsSshKeys.cs new file mode 100644 index 000000000..e84bb5208 --- /dev/null +++ b/source/Calamari/ArgoCD/Git/WindowsSshKeys.cs @@ -0,0 +1,20 @@ +using System; +using Calamari.Common.Plumbing; +using NuGet.Commands; + +namespace Calamari.ArgoCD.Git; + +// Our fork LibGit2Sharp uses WinCNG for it's SSH support, and WinCNG does not support some keys. +// This first implementation blocks on all as we want to get the feature out for Linux first and +// we'll come back to attempt to handle it more gracefully at a later date. +public class WindowsSshKeys +{ + public static void AssertSupported(IGitConnection? connection) + { + if (!CalamariEnvironment.IsRunningOnWindows) return; + if (connection is not SshKeyGitConnection) return; + + throw new CommandException( + "SSH credentials are not currently supported for Git operations running on Windows. Use HTTPS credentials (username + password or PAT) instead or run the deployment on a Linux worker."); + } +} \ No newline at end of file From 57f352cefc7a82d5d26c0c40775f8423f81e6f21 Mon Sep 17 00:00:00 2001 From: Eddy Moulton Date: Mon, 1 Jun 2026 16:38:12 +1000 Subject: [PATCH 6/7] Fix test (by excluding it) --- source/Calamari.Tests/ArgoCD/Git/RepositoryFactoryTests.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/Calamari.Tests/ArgoCD/Git/RepositoryFactoryTests.cs b/source/Calamari.Tests/ArgoCD/Git/RepositoryFactoryTests.cs index d9103a546..f52231a15 100644 --- a/source/Calamari.Tests/ArgoCD/Git/RepositoryFactoryTests.cs +++ b/source/Calamari.Tests/ArgoCD/Git/RepositoryFactoryTests.cs @@ -96,6 +96,8 @@ public void CanCloneAnExistingRepositoryAtHEADAndAssociatedFiles() } [Test] + // SSH not currently functional on Windows + [Category(TestCategory.CompatibleOS.OnlyNixOrMac)] public void CloningSshKeyGitConnectionDoesNotResolveAPullRequestClientAndLogsVerboseMessage() { var filename = "sshTest.txt"; From c42b558b943ff9b8c9dccee0572d73352dc22de5 Mon Sep 17 00:00:00 2001 From: Eddy Moulton Date: Tue, 2 Jun 2026 09:22:59 +1000 Subject: [PATCH 7/7] Revert whitespace --- source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs index 7079deefd..d24b3ee2c 100644 --- a/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs +++ b/source/Calamari/ArgoCD/Git/AuthenticatingRepositoryFactory.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Calamari.Common.Commands; -using Calamari.Common.Plumbing; using Calamari.Common.Plumbing.Logging; using Octopus.Calamari.Contracts.ArgoCD; @@ -63,4 +61,4 @@ public RepositoryWrapper CloneRepository(string requestedUrl, string targetRevis var anonGitConnection = new HttpsGitConnection(null, null, GitCloneSafeUrl.ConvertToUriString(requestedUrl), GitReference.CreateFromString(targetRevision)); return repositoryFactory.CloneRepository(UniqueRepoNameGenerator.Generate(), anonGitConnection); } -} +} \ No newline at end of file