Skip to content

Commit 78ad523

Browse files
committed
Always run in 32-bit on Windows
In order to ensure maximum compatibilty on Windows, we target 32-bit platforms (64-bit Windows can run 32-bit applications in WoW). Also fix a bug where we had mismatching/incompatible use of the native libgit2 binaries by the main GCM executable, and the .NET Framework-only WinForms 'authentication helpers' such as the Microsoft.Authentication.Helper project. These WinForms projects prefer 32-bit and failed to load the libgit2 binaries, which were restored as their 64-bit forms by the main CLI project.
1 parent 0bd90a0 commit 78ad523

File tree

9 files changed

+103
-66
lines changed

9 files changed

+103
-66
lines changed

.azure-pipelines/templates/windows/pack.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
steps:
22
- script: |
33
xcopy "out\windows\Installer.Windows\bin\$(configuration)\net461" "$(Build.StagingDirectory)\publish\"
4-
xcopy "out\windows\Payload.Windows\bin\$(configuration)\net461\win-x64" "$(Build.StagingDirectory)\publish\payload\"
4+
xcopy "out\windows\Payload.Windows\bin\$(configuration)\net461\win-x86" "$(Build.StagingDirectory)\publish\payload\"
55
mkdir "$(Build.StagingDirectory)\publish\payload.sym\"
66
move "$(Build.StagingDirectory)\publish\payload\*.pdb" "$(Build.StagingDirectory)\publish\payload.sym\"
77
displayName: Prepare final build artifact

src/shared/Git-Credential-Manager/Git-Credential-Manager.csproj

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
6-
<TargetFrameworks Condition="'$(OSPlatform)'=='windows'">netcoreapp2.1;net461</TargetFrameworks>
7-
<RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
5+
<TargetFramework>netcoreapp2.1</TargetFramework>
6+
<TargetFramework Condition="'$(OSPlatform)'=='windows'">net461</TargetFramework>
7+
<RuntimeIdentifiers>win-x86;osx-x64</RuntimeIdentifiers>
88
<AssemblyName>git-credential-manager-core</AssemblyName>
99
<RootNamespace>Microsoft.Git.CredentialManager</RootNamespace>
1010
<ApplicationIcon>$(RepoAssetsPath)gcmicon.ico</ApplicationIcon>
@@ -13,6 +13,12 @@
1313
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
1414
</PropertyGroup>
1515

16+
<!-- On Windows we target 32-bit because the WinForms authentication helpers are 32-bit
17+
and also need to use the native libgit2 library (we use the 32-bit version on Windows) -->
18+
<PropertyGroup Condition="'$(OSPlatform)'=='windows'">
19+
<Prefer32Bit>true</Prefer32Bit>
20+
</PropertyGroup>
21+
1622
<ItemGroup>
1723
<ProjectReference Include="..\GitHub\GitHub.csproj" />
1824
<ProjectReference Include="..\Microsoft.AzureRepos\Microsoft.AzureRepos.csproj" />

src/shared/Microsoft.Git.CredentialManager.Tests/Interop/LibGit2Tests.cs

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,10 @@ public void LibGit2_GetConfiguration_ReturnsConfiguration()
4141
[Fact]
4242
public void LibGit2Configuration_Enumerate_CallbackReturnsTrue_InvokesCallbackForEachEntry()
4343
{
44-
string repoPath = CreateRepository();
45-
Git(repoPath, "config --local foo.name lancelot").AssertSuccess();
46-
Git(repoPath, "config --local foo.quest seek-holy-grail").AssertSuccess();
47-
Git(repoPath, "config --local foo.favcolor blue").AssertSuccess();
44+
string repoPath = CreateRepository(out string workDirPath);
45+
Git(repoPath, workDirPath, "config --local foo.name lancelot").AssertSuccess();
46+
Git(repoPath, workDirPath, "config --local foo.quest seek-holy-grail").AssertSuccess();
47+
Git(repoPath, workDirPath, "config --local foo.favcolor blue").AssertSuccess();
4848

4949
var expectedVisitedEntries = new List<(string name, string value)>
5050
{
@@ -79,10 +79,10 @@ bool cb(string name, string value)
7979
[Fact]
8080
public void LibGit2Configuration_Enumerate_CallbackReturnsFalse_InvokesCallbackForEachEntryUntilReturnsFalse()
8181
{
82-
string repoPath = CreateRepository();
83-
Git(repoPath, "config --local foo.name lancelot").AssertSuccess();
84-
Git(repoPath, "config --local foo.quest seek-holy-grail").AssertSuccess();
85-
Git(repoPath, "config --local foo.favcolor blue").AssertSuccess();
82+
string repoPath = CreateRepository(out string workDirPath);
83+
Git(repoPath, workDirPath, "config --local foo.name lancelot").AssertSuccess();
84+
Git(repoPath, workDirPath, "config --local foo.quest seek-holy-grail").AssertSuccess();
85+
Git(repoPath, workDirPath, "config --local foo.favcolor blue").AssertSuccess();
8686

8787
var expectedVisitedEntries = new List<(string name, string value)>
8888
{
@@ -116,8 +116,8 @@ bool cb(string name, string value)
116116
[Fact]
117117
public void LibGit2Configuration_TryGetValue_Name_Exists_ReturnsTrueOutString()
118118
{
119-
string repoPath = CreateRepository();
120-
Git(repoPath, "config --local user.name john.doe").AssertSuccess();
119+
string repoPath = CreateRepository(out string workDirPath);
120+
Git(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();
121121

122122
var trace = new NullTrace();
123123
var git = new LibGit2(trace);
@@ -149,8 +149,8 @@ public void LibGit2Configuration_TryGetValue_Name_DoesNotExists_ReturnsFalse()
149149
[Fact]
150150
public void LibGit2Configuration_TryGetValue_SectionProperty_Exists_ReturnsTrueOutString()
151151
{
152-
string repoPath = CreateRepository();
153-
Git(repoPath, "config --local user.name john.doe").AssertSuccess();
152+
string repoPath = CreateRepository(out string workDirPath);
153+
Git(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();
154154

155155
var trace = new NullTrace();
156156
var git = new LibGit2(trace);
@@ -183,8 +183,8 @@ public void LibGit2Configuration_TryGetValue_SectionProperty_DoesNotExists_Retur
183183
[Fact]
184184
public void LibGit2Configuration_TryGetValue_SectionScopeProperty_Exists_ReturnsTrueOutString()
185185
{
186-
string repoPath = CreateRepository();
187-
Git(repoPath, "config --local user.example.com.name john.doe").AssertSuccess();
186+
string repoPath = CreateRepository(out string workDirPath);
187+
Git(repoPath, workDirPath, "config --local user.example.com.name john.doe").AssertSuccess();
188188

189189
var trace = new NullTrace();
190190
var git = new LibGit2(trace);
@@ -200,8 +200,8 @@ public void LibGit2Configuration_TryGetValue_SectionScopeProperty_Exists_Returns
200200
[Fact]
201201
public void LibGit2Configuration_TryGetValue_SectionScopeProperty_NullScope_ReturnsTrueOutUnscopedString()
202202
{
203-
string repoPath = CreateRepository();
204-
Git(repoPath, "config --local user.name john.doe").AssertSuccess();
203+
string repoPath = CreateRepository(out string workDirPath);
204+
Git(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();
205205

206206
var trace = new NullTrace();
207207
var git = new LibGit2(trace);
@@ -235,8 +235,8 @@ public void LibGit2Configuration_TryGetValue_SectionScopeProperty_DoesNotExists_
235235
[Fact]
236236
public void LibGit2Configuration_GetString_Name_Exists_ReturnsString()
237237
{
238-
string repoPath = CreateRepository();
239-
Git(repoPath, "config --local user.name john.doe").AssertSuccess();
238+
string repoPath = CreateRepository(out string workDirPath);
239+
Git(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();
240240

241241
var trace = new NullTrace();
242242
var git = new LibGit2(trace);
@@ -265,8 +265,8 @@ public void LibGit2Configuration_GetString_Name_DoesNotExists_ThrowsException()
265265
[Fact]
266266
public void LibGit2Configuration_GetString_SectionProperty_Exists_ReturnsString()
267267
{
268-
string repoPath = CreateRepository();
269-
Git(repoPath, "config --local user.name john.doe").AssertSuccess();
268+
string repoPath = CreateRepository(out string workDirPath);
269+
Git(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();
270270

271271
var trace = new NullTrace();
272272
var git = new LibGit2(trace);
@@ -296,8 +296,8 @@ public void LibGit2Configuration_GetString_SectionProperty_DoesNotExists_ThrowsE
296296
[Fact]
297297
public void LibGit2Configuration_GetString_SectionScopeProperty_Exists_ReturnsString()
298298
{
299-
string repoPath = CreateRepository();
300-
Git(repoPath, "config --local user.example.com.name john.doe").AssertSuccess();
299+
string repoPath = CreateRepository(out string workDirPath);
300+
Git(repoPath, workDirPath, "config --local user.example.com.name john.doe").AssertSuccess();
301301

302302
var trace = new NullTrace();
303303
var git = new LibGit2(trace);
@@ -312,8 +312,8 @@ public void LibGit2Configuration_GetString_SectionScopeProperty_Exists_ReturnsSt
312312
[Fact]
313313
public void LibGit2Configuration_GetString_SectionScopeProperty_NullScope_ReturnsUnscopedString()
314314
{
315-
string repoPath = CreateRepository();
316-
Git(repoPath, "config --local user.name john.doe").AssertSuccess();
315+
string repoPath = CreateRepository(out string workDirPath);
316+
Git(repoPath, workDirPath, "config --local user.name john.doe").AssertSuccess();
317317

318318
var trace = new NullTrace();
319319
var git = new LibGit2(trace);
@@ -344,10 +344,9 @@ public void LibGit2Configuration_GetString_SectionScopeProperty_DoesNotExists_Th
344344
[Fact]
345345
public void LibGit2Configuration_GetRepositoryPath_ReturnsRepositoryPath()
346346
{
347-
string repoBasePath = CreateRepository();
348-
string expectedRepoGitPath = Path.Combine(repoBasePath, ".git") + "/";
349-
string fileL0Path = Path.Combine(repoBasePath, "file.txt");
350-
string directoryL0Path = Path.Combine(repoBasePath, "directory");
347+
string expectedRepoGitPath = CreateRepository(out string workDirPath) + "/";
348+
string fileL0Path = Path.Combine(workDirPath, "file.txt");
349+
string directoryL0Path = Path.Combine(workDirPath, "directory");
351350
string fileL1Path = Path.Combine(directoryL0Path, "inner-file.txt");
352351
string directoryL1Path = Path.Combine(directoryL0Path, "sub-directory");
353352

@@ -425,34 +424,55 @@ private static string RealPath(string path)
425424
throw new PlatformNotSupportedException();
426425
}
427426

428-
private static string CreateRepository()
427+
private static string CreateBareRepository()
428+
{
429+
string tempDirectory = Path.GetTempPath();
430+
string repoName = $"repo-{Guid.NewGuid().ToString("N").Substring(0, 8)}";
431+
var gitDirPath = Path.Combine(tempDirectory, repoName);
432+
433+
if (Directory.Exists(gitDirPath))
434+
{
435+
Directory.Delete(gitDirPath);
436+
}
437+
438+
Directory.CreateDirectory(gitDirPath);
439+
440+
Git(gitDirPath, gitDirPath, "init --bare").AssertSuccess();
441+
442+
return gitDirPath;
443+
}
444+
445+
private static string CreateRepository() => CreateRepository(out _);
446+
447+
private static string CreateRepository(out string workDirPath)
429448
{
430449
string tempDirectory = Path.GetTempPath();
431450
string repoName = $"repo-{Guid.NewGuid().ToString("N").Substring(0, 8)}";
432-
string repoPath = Path.Combine(tempDirectory, repoName);
451+
workDirPath = Path.Combine(tempDirectory, repoName);
452+
string gitDirPath = Path.Combine(workDirPath, ".git");
433453

434-
if (Directory.Exists(repoPath))
454+
if (Directory.Exists(workDirPath))
435455
{
436-
Directory.Delete(repoPath);
456+
Directory.Delete(workDirPath);
437457
}
438458

439-
Directory.CreateDirectory(repoPath);
459+
Directory.CreateDirectory(workDirPath);
440460

441-
Git(repoPath, "init").AssertSuccess();
461+
Git(gitDirPath, workDirPath, "init").AssertSuccess();
442462

443-
return repoPath;
463+
return gitDirPath;
444464
}
445465

446-
private static GitResult Git(string repositoryPath, string command)
466+
private static GitResult Git(string repositoryPath, string workingDirectory, string command)
447467
{
448468
var procInfo = new ProcessStartInfo("git", command)
449469
{
450470
RedirectStandardOutput = true,
451471
RedirectStandardError = true,
472+
WorkingDirectory = workingDirectory
452473
};
453474

454-
string gitDirectory = Path.Combine(repositoryPath, ".git");
455-
procInfo.Environment["GIT_DIR"] = gitDirectory;
475+
procInfo.Environment["GIT_DIR"] = repositoryPath;
456476

457477
Process proc = Process.Start(procInfo);
458478
if (proc is null)

src/shared/Microsoft.Git.CredentialManager/Interop/LibGit2.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public unsafe IGitConfiguration GetConfiguration(string repositoryPath)
4040
if (repositoryPath != null)
4141
{
4242
// We don't need to check for the file's existence since libgit2 will do that for us!
43-
string repoConfigPath = Path.Combine(repositoryPath, ".git", "config");
43+
string repoConfigPath = Path.Combine(repositoryPath, "config");
4444

4545
// Add the repository configuration
4646
_trace.WriteLine($"Adding local configuration from repository '{repositoryPath}'...");
@@ -123,9 +123,15 @@ public unsafe void Enumerate(GitConfigurationEnumerationCallback cb)
123123

124124
int native_cb(git_config_entry entry, void* payload)
125125
{
126-
if (!cb(entry.GetName(), entry.GetValue()))
126+
if (entry != null)
127127
{
128-
return GIT_ITEROVER;
128+
string name = entry.GetName();
129+
string value = entry.GetValue();
130+
131+
if (!cb(name, value))
132+
{
133+
return GIT_ITEROVER;
134+
}
129135
}
130136

131137
return GIT_OK;

src/shared/Microsoft.Git.CredentialManager/Interop/Native/LibGit2.cs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT license.
33
using System;
44
using System.Runtime.InteropServices;
5+
using static System.Runtime.InteropServices.CallingConvention;
56
using static System.Runtime.InteropServices.UnmanagedType;
67
using static Microsoft.Git.CredentialManager.Interop.U8StringMarshaler;
78

@@ -31,19 +32,19 @@ public static partial class LibGit2
3132
*/
3233
private const string LibraryName = "git2-572e4d8";
3334

34-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
35+
[DllImport(LibraryName, CallingConvention = Cdecl)]
3536
public static extern int git_libgit2_init();
3637

37-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
38+
[DllImport(LibraryName, CallingConvention = Cdecl)]
3839
public static extern int git_libgit2_shutdown();
3940

40-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
41+
[DllImport(LibraryName, CallingConvention = Cdecl)]
4142
public static extern unsafe git_error* git_error_last();
4243

43-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
44+
[DllImport(LibraryName, CallingConvention = Cdecl)]
4445
public static extern void git_buf_dispose(git_buf buf);
4546

46-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
47+
[DllImport(LibraryName, CallingConvention = Cdecl)]
4748
public static extern int git_repository_discover(
4849
git_buf @out,
4950
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
@@ -52,7 +53,7 @@ public static extern int git_repository_discover(
5253
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
5354
string ceiling_dirs);
5455

55-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
56+
[DllImport(LibraryName, CallingConvention = Cdecl)]
5657
public static extern unsafe int git_config_add_file_ondisk(
5758
git_config* cfg,
5859
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
@@ -61,35 +62,35 @@ public static extern unsafe int git_config_add_file_ondisk(
6162
git_repository* repo,
6263
int force);
6364

64-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
65+
[DllImport(LibraryName, CallingConvention = Cdecl)]
6566
public static extern unsafe void git_config_free(git_config* cfg);
6667

67-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
68+
[DllImport(LibraryName, CallingConvention = Cdecl)]
6869
public static extern unsafe int git_config_get_string(
6970
[MarshalAs(CustomMarshaler, MarshalCookie = ManagedCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
7071
out string @out,
7172
git_config* cfg,
7273
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
7374
string name);
7475

75-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
76+
[DllImport(LibraryName, CallingConvention = Cdecl)]
7677
public static extern unsafe int git_config_foreach(git_config* cfg, git_config_foreach_cb callback, void* payload);
7778

78-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
79+
[DllImport(LibraryName, CallingConvention = Cdecl)]
7980
public static extern unsafe int git_config_set_string(
8081
git_config* cfg,
8182
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
8283
string name,
8384
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
8485
string value);
8586

86-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
87+
[DllImport(LibraryName, CallingConvention = Cdecl)]
8788
public static extern unsafe int git_config_delete_entry(
8889
git_config* cfg,
8990
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
9091
string name);
9192

92-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
93+
[DllImport(LibraryName, CallingConvention = Cdecl)]
9394
public static extern unsafe int git_config_get_multivar_foreach(
9495
git_config* cfg,
9596
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
@@ -99,7 +100,7 @@ public static extern unsafe int git_config_get_multivar_foreach(
99100
git_config_foreach_cb callback,
100101
void *payload);
101102

102-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
103+
[DllImport(LibraryName, CallingConvention = Cdecl)]
103104
public static extern unsafe int git_config_set_multivar(
104105
git_config* cfg,
105106
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
@@ -109,7 +110,7 @@ public static extern unsafe int git_config_set_multivar(
109110
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
110111
string value);
111112

112-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
113+
[DllImport(LibraryName, CallingConvention = Cdecl)]
113114
public static extern unsafe int git_config_delete_multivar(
114115
git_config* cfg,
115116
[MarshalAs(CustomMarshaler, MarshalCookie = NativeCookie, MarshalTypeRef = typeof(U8StringMarshaler))]
@@ -118,17 +119,20 @@ public static extern unsafe int git_config_delete_multivar(
118119
string regexp);
119120

120121

121-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
122+
[DllImport(LibraryName, CallingConvention = Cdecl)]
122123
public static extern unsafe int git_config_open_default(git_config** @out);
123124

124-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
125+
[DllImport(LibraryName, CallingConvention = Cdecl)]
125126
public static extern unsafe int git_config_open_level(git_config** @out, git_config* parent, git_config_level_t level);
126127

127-
[DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl)]
128+
[DllImport(LibraryName, CallingConvention = Cdecl)]
128129
public static extern unsafe int git_config_snapshot(git_config** @out, git_config* config);
129130
}
130131

132+
[UnmanagedFunctionPointer(Cdecl)]
131133
public unsafe delegate int git_config_foreach_cb(git_config_entry entry, void* payload);
134+
135+
[UnmanagedFunctionPointer(Cdecl)]
132136
public delegate void git_config_entry_free_callback(git_config_entry entry);
133137

134138
[StructLayout(LayoutKind.Sequential)]

0 commit comments

Comments
 (0)